From ab8e636483cb25bfd88dcef888cdcb6e6a4f665f Mon Sep 17 00:00:00 2001 From: Mingli Yuan Date: Thu, 27 Aug 2026 23:20:23 +0800 Subject: [PATCH] research: add compact Brownian red team --- docs/RESEARCH_STATUS.md | 2 +- sonnet/README.md | 2 +- .../02-compact-space-redteam.md | 166 ++++++++ sonnet/brownian-scale-fibre/README.md | 22 +- .../compact_space_redteam.py | 390 ++++++++++++++++++ .../phase_compact_contract.py | 270 ++++++++++++ .../test_brownian_compact_space_redteam.py | 214 ++++++++++ 7 files changed, 1060 insertions(+), 6 deletions(-) create mode 100644 sonnet/brownian-scale-fibre/02-compact-space-redteam.md create mode 100644 sonnet/brownian-scale-fibre/compact_space_redteam.py create mode 100644 sonnet/brownian-scale-fibre/phase_compact_contract.py create mode 100644 tests/research/test_brownian_compact_space_redteam.py diff --git a/docs/RESEARCH_STATUS.md b/docs/RESEARCH_STATUS.md index 24356a5a..e0e96fe5 100644 --- a/docs/RESEARCH_STATUS.md +++ b/docs/RESEARCH_STATUS.md @@ -159,7 +159,7 @@ Process Geometry solver or independent adoption. | [`am-conformal-chart-normal-forms`](../sonnet/am-conformal-chart-normal-forms/00-problem-frontier.md) | Phase 1 exact Riccati lift, Möbius covariance, scalar gauge, cubic no-go, and eight-axis cost accounting | run oracle-isolated bounded discovery (1B) before the pendulum atlas search; no economy theorem yet | | [`moving-am-observer`](../sonnet/moving-am-observer/README.md) | static-observer no-go followed by blind moving-frame recovery, held-out family selection, task-equivalence quotient, blind morphism discovery, grammar stability, and dimensionful Bellman covariance | broader non-affine/multi-family or stochastic pressure, now separated into its own Sonnet | | [`stochastic-feedback-trap-first-passage`](../sonnet/stochastic-feedback-trap-first-passage/README.md) | bounded grammar census, exact Itô task quotient, independent BVP/Monte Carlo first-passage comparison, and reset Bellman value/policy covariance complete | calibration is closed; new discovery or API incubation requires a new contract | -| [`brownian-scale-fibre`](../sonnet/brownian-scale-fibre/README.md) | S0/S1 initialized: blind centered finite-law scale balance, typed drift refusal, exact endpoint fibres, and concatenation pushforward | separate lattice point return, continuum neighbourhood recurrence, and singleton hitting before authorizing a Brownian/heat-kernel lowering | +| [`brownian-scale-fibre`](../sonnet/brownian-scale-fibre/README.md) | S0/S1 plus compact red team: exact integer-to-cycle deck-fibre pushforward, typed even-cycle period obstruction, exact lazy uniform stationary law, and frozen circle/sphere boundaries | execute the sphere action/stabilizer gate and keep compact time-stationarity separate from line scale-stability before any continuum lowering | | [`lonely-runner`](../sonnet/lonely-runner/README.md) | exact upstream pruning transfer and bounded K4/K5 contact mechanisms; Phase 15A global audit complete | lift-aware initial search on solved cases, then frozen K13 validation; `LRC(13)` remains open | | [`hidden-am-noether`](../sonnet/hidden-am-noether/README.md) | bounded static-observer route reached a structural no-go; the viable moving-observer continuation is tracked separately | do not reopen static conjugation without new grammar or task semantics | | [`pcr3bp-history-cost`](../sonnet/pcr3bp-history-cost/README.md) | Phase 0/1 history, scale-jet, topology, and coding audit complete; Phase 2 contract frozen | execute converged return/absorbing ensemble and twisted partition audit; no numerical result yet | diff --git a/sonnet/README.md b/sonnet/README.md index e37fc834..b637b27e 100644 --- a/sonnet/README.md +++ b/sonnet/README.md @@ -80,7 +80,7 @@ deferred until its next oracle/evidence gate is affordable. | [`hidden-am-noether/`](hidden-am-noether/) | static branch closed negatively | a frozen observer in the same product-affine group cannot reveal a missing stabilizer dimension | [static-observer no-go](hidden-am-noether/03-static-observer-no-go-and-schedule-split.md); continuation moved to `moving-am-observer/` | | [`moving-am-observer/`](moving-am-observer/) | affine deterministic phase closed | bounded blind observer discovery, task-equivalent minimum slice, and dimensionful Bellman covariance on the declared family | [study ledger](moving-am-observer/README.md); stochastic continuation moved to its own Sonnet | | [`stochastic-feedback-trap-first-passage/`](stochastic-feedback-trap-first-passage/) | current calibration closed | exact Itô task quotient plus independent first-passage and reset-Bellman covariance across 242 monotone charts | [Phase-4 results](stochastic-feedback-trap-first-passage/09-phase4-reset-bellman-results.md); further work requires a new Sonnet or governed extraction proposal | -| [`brownian-scale-fibre/`](brownian-scale-fibre/) | S0/S1 active | blind centered finite-law scale balance and exact endpoint-fibre concatenation; no continuum limit or heat-kernel lowering yet | [problem frontier](brownian-scale-fibre/00-problem-frontier.md); next separate recurrence observers, then certify the continuum lowering | +| [`brownian-scale-fibre/`](brownian-scale-fibre/) | S0/S1 plus compact red team | exact line endpoint fibres now push to finite cycles through retained deck fibres; even cycles expose period two, while lazy compact laws have an exact uniform stationary fixed point distinct from line scale stability | [compact-space red team](brownian-scale-fibre/02-compact-space-redteam.md); next execute the separate sphere action/stabilizer gate before any continuum lowering | | [`pcr3bp-history-cost/`](pcr3bp-history-cost/) | Phases 0–1 complete; Phase 2 frozen | lifted topology and scale-jet reconstruction separate word, clock, deck, and hyperbolic costs; no Bellman/Huffman source is yet justified | [Phase-2 contract](pcr3bp-history-cost/02-return-partition-holonomy-contract.md); next run the frozen two-gate covariance and convergence gates | | [`am-conformal-chart-normal-forms/`](am-conformal-chart-normal-forms/) | T0/T1; Phase 1 mechanism calibrated | exact Riccati lift, Möbius covariance, scalar-gauge invariance, cubic no-go, and eight-axis Pareto accounting; no discovery or economy theorem | [Phase-1 results](am-conformal-chart-normal-forms/02-phase1-riccati-results.md); next freeze a blind low-height grammar and run bounded recovery | | [`effective-scale-carrier-ladder/`](effective-scale-carrier-ladder/) | T1 / NARROW | finite syntax decision eliminates a surreal runtime; symbolic height is a real C2 obstruction, but semantic evaluation and C3/C4 separation remain open | [results](effective-scale-carrier-ladder/01-results.md), [compiler](../workstreams/carrier_ladder/compiler/), and [commit--reveal audit](../workstreams/carrier_ladder/redteam/); next implement an effective normalized hyperiteration fragment, not a general surreal runtime | diff --git a/sonnet/brownian-scale-fibre/02-compact-space-redteam.md b/sonnet/brownian-scale-fibre/02-compact-space-redteam.md new file mode 100644 index 00000000..b469cbda --- /dev/null +++ b/sonnet/brownian-scale-fibre/02-compact-space-redteam.md @@ -0,0 +1,166 @@ +# Compact-space Brownian red team + +Status: finite cycle phase executed for +[#162](https://github.com/mountain/process-geometry/issues/162); circle +semantics and the sphere claim boundary are frozen. No continuum heat-kernel +or mixing theorem is claimed. + +## 1. Question + +The line calibration can suggest that long process time, scale normalization, +and a Gaussian stable law are one universal mechanism. Compact spaces force +three notions apart: + +1. a locally Gaussian small-time chart; +2. a scale-renormalized law on a noncompact space; +3. a time-stationary invariant measure on a compact space. + +The native red team starts before a continuum density. It counts finite +nearest-neighbour histories on the integer lattice and pushes them through a +finite deck quotient. + +## 2. Exact cycle quotient + +For horizon (N), let + +\[ +\pi_N(\omega)=\sum_{j=1}^N\xi_j\in\mathbb Z, +\qquad \xi_j\in\{-1,+1\}. +\] + +For the finite cycle (C_q=\mathbb Z/q\mathbb Z), the endpoint count at residue +(r) is exactly + +\[ +c_N^{(q)}(r) +=\sum_{k\in\mathbb Z}c_N(r+kq). +\] + +At finite (N), only finitely many lifts have nonzero mass. The software +retains every pair `(integer lift, count)` rather than discarding it after the +sum. It then independently performs the chronological update directly on +(C_q) and compares the two constructions residue by residue. + +For (q=5,N=7), both constructions return + +\[ +(14,35,22,22,35), +\] + +with total mass (128=2^7). The residue-zero deck fibre is + +\[ +\{(-5,7),(5,7)\}, +\] + +which makes the forgotten lift explicit. + +This establishes a finite exact statement: + +\[ +\boxed{ +\text{cycle endpoint counting} += +\text{integer endpoint counting integrated along deck fibres} +} +\] + +It does not require a wrapped Gaussian, Fourier basis, transition matrix, or +heat kernel. + +## 3. Winding is task-relative residual information + +On (C_4), the equal-horizon histories + +\[ +(+1,+1,+1,+1), +\qquad +(+1,-1,+1,-1) +\] + +have integer lifts (4) and (0), but both have cycle endpoint zero. Thus the +cycle endpoint quotient is exact for endpoint mass and inadequate for winding, +running extrema, or chronological path tasks. + +The continuum circle statement + +\[ +S^1=\mathbb R/(2\pi\mathbb Z) +\] + +has the same structure: a circle endpoint has an integer deck fibre on the +cover. A wrapped law is the pushforward obtained by integrating over that +fibre. This continuum interpretation is recorded here but not implemented as +a heat-kernel computation. + +## 4. Period two blocks naive time forgetting + +For the non-lazy nearest-neighbour walk on an even cycle, every step switches +the parity class. The clock has period two. Consequently the terminal law +cannot converge pointwise to the uniform law; its total-variation distance +from uniform is at least (1/2) at every finite time. + +The implementation returns a typed `periodic-clock-obstruction` and names the +three distinct repairs: + +- add a lazy stay event; +- pass to a continuous-time clock; +- use a Cesaro time observer. + +These repairs are not silently identified. On an odd cycle, or under a +strictly intermediate lazy probability, the clock is irreducible and the +period is one. Stay probability one is separately rejected as a reducible +clock: the uniform law remains stationary, but every point mass is also +stationary, so stationarity alone cannot establish mixing. + +For the exact lazy walk with stay probability (1/2), the uniform law is an +exact one-step fixed point. A bounded (C_5) audit records decreasing rational +total-variation distances through horizon eight, ending at +(3571/163840). This is evidence of a bounded trend, not a proof of an +asymptotic mixing theorem. + +## 5. Two types of stable law + +The red team uses separate machine-readable types: + +| Mechanism | Example | Required operation | +| --- | --- | --- | +| `scale-renormalized` | line Gaussian | ensemble growth followed by dynamic spatial rescaling | +| `time-stationary` | uniform cycle/circle law | declared clock evolution on a compact carrier | + +The uniform compact law must not be reported as a Gaussian or as a scaling +fixed point. Conversely, line Brownian has no finite stationary probability +under raw physical-time evolution. + +## 6. Sphere gate + +The next compact red team is frozen around + +\[ +S^2=SO(3)/SO(2). +\] + +It must retain the following obstructions before any computation is promoted: + +- no single global additive position chart; +- no internal global spatial dilation while the radius is fixed; +- a tangent-chart Gaussian is not a global Gaussian law; +- curvature, chart transition, and holonomy remain residuals; +- the long-time stationary candidate is normalized area + (dA/(4\pi)), not the line Gaussian. + +Unlike the circle, (S^2) has no winding deck fibre from a nontrivial universal +cover. A successful treatment must explain rotation histories and the +stabilizer quotient rather than reuse the circle proof. + +## 7. Consequence for the boundary conjecture + +The finite result weakens a too-simple claim that physical position must escape +to spatial infinity. On a compact quotient, position remains bounded while +history depth and forgotten lifts grow. The more plausible universal object +is therefore the ideal boundary of the time/history fibre, whose task +pushforward may appear as a scale-stable law, Haar measure, Riemannian volume, +or a more detailed path boundary. + +The result supports further study of that distinction. It does not yet prove +that one arithmetic-tower boundary produces all of these measures. diff --git a/sonnet/brownian-scale-fibre/README.md b/sonnet/brownian-scale-fibre/README.md index eae1b17c..77d8620f 100644 --- a/sonnet/brownian-scale-fibre/README.md +++ b/sonnet/brownian-scale-fibre/README.md @@ -16,20 +16,34 @@ Read in order: calculations; 4. [`test_brownian_scale_fibre_phase0.py`](../../tests/research/test_brownian_scale_fibre_phase0.py) — independent certificates and source firewall. +5. [`02-compact-space-redteam.md`](02-compact-space-redteam.md) — exact cycle + quotient, circle cover semantics, period-two obstruction, and the frozen + sphere gate; +6. [`phase_compact_contract.py`](phase_compact_contract.py) and + [`compact_space_redteam.py`](compact_space_redteam.py) — machine-auditable + compact-space red team. -Current S0/S1 result: +Current finite results: ```text raw centered finite law -> active response order 2 -> scale balance a=1/2 finite histories -> endpoint fibres -> exact concatenation pushforward biased law -> typed centering-required refusal continuum / heat-kernel lowering -> not yet authorized +integer lattice -> exact deck-fibre pushforward to finite cycles +even non-lazy cycle -> typed period-two obstruction +lazy compact law -> exact uniform stationary fixed point +circle/sphere -> time-stationary, not line scale-stable ``` The scale result is narrower than a central-limit theorem. The endpoint result -is narrower than path equivalence. S2 must next separate lattice point return, -continuum neighbourhood recurrence, and singleton hitting before S3 authorizes -a Brownian/heat-kernel lowering. +is narrower than path equivalence. Recurrence and heat-kernel continuation +remain unauthorized while the compact sphere action/stabilizer gate is open. + +The compact-space red team in #162 now blocks a second conflation. A line +Gaussian is scale-renormalized; a compact cycle/circle law can instead be +time-stationary. The finite cycle result is exact, while the continuum circle +and sphere remain semantic gates rather than heat-kernel claims. This task is the second independent representation family requested in [#140](https://github.com/mountain/process-geometry/issues/140). Draft diff --git a/sonnet/brownian-scale-fibre/compact_space_redteam.py b/sonnet/brownian-scale-fibre/compact_space_redteam.py new file mode 100644 index 00000000..23805f90 --- /dev/null +++ b/sonnet/brownian-scale-fibre/compact_space_redteam.py @@ -0,0 +1,390 @@ +"""Exact compact-space Brownian red teams for issue #162. + +The executable layer stays finite. Integer-lattice histories are pushed to a +cycle by the deck quotient ``x -> x mod q`` and compared with an independent +direct cycle update. No transform, matrix, heat kernel, or continuum solver +enters native discovery. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum +from fractions import Fraction +from typing import Iterable + + +class CompactBrownianDomainError(ValueError): + """The compact red-team grammar does not cover the requested input.""" + + +class PeriodicClockObstruction(CompactBrownianDomainError): + """A terminal-time mixing claim is blocked by a periodic discrete clock.""" + + def __init__(self, modulus: int, period: int) -> None: + self.modulus = modulus + self.period = period + super().__init__( + "periodic-clock-obstruction: " + f"nearest-neighbour walk on C_{modulus} has period {period}; " + "use a lazy/continuous clock or a Cesaro observer" + ) + + +class ReducibleClockObstruction(CompactBrownianDomainError): + """A stationary law is not a mixing limit when the clock never moves.""" + + def __init__(self, modulus: int) -> None: + self.modulus = modulus + super().__init__( + "reducible-clock-obstruction: " + f"stay probability one leaves every state of C_{modulus} absorbing" + ) + + +class StabilityMechanism(str, Enum): + SCALE_RENORMALIZED = "scale-renormalized" + TIME_STATIONARY = "time-stationary" + + +def _require_modulus(modulus: int) -> None: + if isinstance(modulus, bool) or not isinstance(modulus, int) or modulus < 2: + raise CompactBrownianDomainError("cycle modulus must be an integer >= 2") + + +def _require_horizon(horizon: int) -> None: + if isinstance(horizon, bool) or not isinstance(horizon, int) or horizon < 0: + raise CompactBrownianDomainError( + "cycle horizon must be a non-negative integer" + ) + + +@dataclass(frozen=True) +class CycleEndpointCounts: + modulus: int + horizon: int + counts: tuple[int, ...] + construction: str + + def __post_init__(self) -> None: + _require_modulus(self.modulus) + _require_horizon(self.horizon) + if len(self.counts) != self.modulus: + raise CompactBrownianDomainError("one count is required per residue") + if any( + isinstance(count, bool) or not isinstance(count, int) or count < 0 + for count in self.counts + ): + raise CompactBrownianDomainError( + "cycle counts must be non-negative integers" + ) + if self.total_histories != 2**self.horizon: + raise CompactBrownianDomainError( + "cycle endpoint counts do not conserve history mass" + ) + if not self.construction.strip(): + raise CompactBrownianDomainError("cycle construction must be declared") + + @property + def total_histories(self) -> int: + return sum(self.counts) + + def probability(self, residue: int) -> Fraction: + return Fraction(self.counts[residue % self.modulus], self.total_histories) + + +@dataclass(frozen=True) +class DeckFibrePushforward: + cycle: CycleEndpointCounts + deck_fibres: tuple[tuple[tuple[int, int], ...], ...] + + def __post_init__(self) -> None: + if len(self.deck_fibres) != self.cycle.modulus: + raise CompactBrownianDomainError( + "one deck fibre is required per cycle residue" + ) + for residue, fibre in enumerate(self.deck_fibres): + if any(lift % self.cycle.modulus != residue for lift, _ in fibre): + raise CompactBrownianDomainError("a lift is in the wrong deck fibre") + if sum(count for _, count in fibre) != self.cycle.counts[residue]: + raise CompactBrownianDomainError( + "deck fibre mass does not match the quotient count" + ) + + +def fold_integer_endpoint_counts( + line_counts: Iterable[tuple[int, int]], + *, + modulus: int, + horizon: int, +) -> DeckFibrePushforward: + """Push exact line counts through ``Z -> Z/qZ`` and retain every lift.""" + + _require_modulus(modulus) + _require_horizon(horizon) + fibres: list[list[tuple[int, int]]] = [[] for _ in range(modulus)] + seen: set[int] = set() + for lift, count in line_counts: + if isinstance(lift, bool) or not isinstance(lift, int): + raise CompactBrownianDomainError("line endpoints must be integers") + if lift in seen: + raise CompactBrownianDomainError("line endpoints must be unique") + seen.add(lift) + if isinstance(count, bool) or not isinstance(count, int) or count <= 0: + raise CompactBrownianDomainError( + "line endpoint counts must be positive integers" + ) + fibres[lift % modulus].append((lift, count)) + ordered_fibres = tuple(tuple(sorted(fibre)) for fibre in fibres) + counts = tuple(sum(count for _, count in fibre) for fibre in ordered_fibres) + cycle = CycleEndpointCounts( + modulus=modulus, + horizon=horizon, + counts=counts, + construction="deck-fibre pushforward from the integer lattice", + ) + return DeckFibrePushforward(cycle=cycle, deck_fibres=ordered_fibres) + + +def direct_cycle_endpoint_counts(modulus: int, horizon: int) -> CycleEndpointCounts: + """Independent chronological update directly on the cycle.""" + + _require_modulus(modulus) + _require_horizon(horizon) + counts = [0] * modulus + counts[0] = 1 + for _ in range(horizon): + next_counts = [0] * modulus + for residue, count in enumerate(counts): + next_counts[(residue - 1) % modulus] += count + next_counts[(residue + 1) % modulus] += count + counts = next_counts + return CycleEndpointCounts( + modulus=modulus, + horizon=horizon, + counts=tuple(counts), + construction="direct chronological nearest-neighbour cycle update", + ) + + +@dataclass(frozen=True) +class CycleLaw: + masses: tuple[Fraction, ...] + + def __post_init__(self) -> None: + if len(self.masses) < 2: + raise CompactBrownianDomainError("a cycle law needs at least two states") + if any(not isinstance(mass, Fraction) or mass < 0 for mass in self.masses): + raise CompactBrownianDomainError( + "cycle masses must be non-negative exact fractions" + ) + if sum(self.masses, start=Fraction(0)) != 1: + raise CompactBrownianDomainError("cycle masses must sum exactly to one") + + @classmethod + def point_mass(cls, modulus: int, residue: int = 0) -> "CycleLaw": + _require_modulus(modulus) + masses = [Fraction(0)] * modulus + masses[residue % modulus] = Fraction(1) + return cls(tuple(masses)) + + @classmethod + def uniform(cls, modulus: int) -> "CycleLaw": + _require_modulus(modulus) + return cls((Fraction(1, modulus),) * modulus) + + @property + def modulus(self) -> int: + return len(self.masses) + + +def cycle_step(law: CycleLaw, *, stay_probability: Fraction) -> CycleLaw: + """Apply one exact symmetric step, optionally with a declared lazy clock.""" + + if not isinstance(stay_probability, Fraction): + raise CompactBrownianDomainError("stay probability must be an exact Fraction") + if not 0 <= stay_probability <= 1: + raise CompactBrownianDomainError("stay probability must lie in [0, 1]") + move_probability = (1 - stay_probability) / 2 + next_masses = [Fraction(0)] * law.modulus + for residue, mass in enumerate(law.masses): + next_masses[residue] += stay_probability * mass + next_masses[(residue - 1) % law.modulus] += move_probability * mass + next_masses[(residue + 1) % law.modulus] += move_probability * mass + return CycleLaw(tuple(next_masses)) + + +def nearest_neighbour_period(modulus: int, *, stay_probability: Fraction) -> int: + _require_modulus(modulus) + if not isinstance(stay_probability, Fraction): + raise CompactBrownianDomainError("stay probability must be an exact Fraction") + if not 0 <= stay_probability <= 1: + raise CompactBrownianDomainError("stay probability must lie in [0, 1]") + if stay_probability > 0: + return 1 + return 2 if modulus % 2 == 0 else 1 + + +def require_terminal_mixing_clock( + modulus: int, + *, + stay_probability: Fraction, +) -> None: + if stay_probability == 1: + raise ReducibleClockObstruction(modulus) + period = nearest_neighbour_period( + modulus, + stay_probability=stay_probability, + ) + if period != 1: + raise PeriodicClockObstruction(modulus, period) + + +def total_variation(left: CycleLaw, right: CycleLaw) -> Fraction: + if left.modulus != right.modulus: + raise CompactBrownianDomainError( + "total variation requires laws on the same cycle" + ) + return sum( + (abs(a - b) for a, b in zip(left.masses, right.masses)), + start=Fraction(0), + ) / 2 + + +@dataclass(frozen=True) +class BoundedMixingAudit: + modulus: int + stay_probability: Fraction + horizons: tuple[int, ...] + distances_to_uniform: tuple[Fraction, ...] + nonincreasing: bool + claim_boundary: str + + +def bounded_lazy_mixing_audit( + modulus: int, + *, + horizon: int, + stay_probability: Fraction = Fraction(1, 2), +) -> BoundedMixingAudit: + """Record a bounded exact trend; this is deliberately not a limit theorem.""" + + _require_modulus(modulus) + _require_horizon(horizon) + require_terminal_mixing_clock( + modulus, + stay_probability=stay_probability, + ) + law = CycleLaw.point_mass(modulus) + uniform = CycleLaw.uniform(modulus) + distances = [total_variation(law, uniform)] + for _ in range(horizon): + law = cycle_step(law, stay_probability=stay_probability) + distances.append(total_variation(law, uniform)) + return BoundedMixingAudit( + modulus=modulus, + stay_probability=stay_probability, + horizons=tuple(range(horizon + 1)), + distances_to_uniform=tuple(distances), + nonincreasing=all( + right <= left for left, right in zip(distances, distances[1:]) + ), + claim_boundary=( + "bounded exact calibration only; no asymptotic mixing theorem is claimed" + ), + ) + + +@dataclass(frozen=True) +class WindingResidual: + modulus: int + left_history: tuple[int, ...] + right_history: tuple[int, ...] + left_lift: int + right_lift: int + shared_residue: int + lost_observer: str + + +def expose_winding_residual( + modulus: int, + left_history: Iterable[int], + right_history: Iterable[int], +) -> WindingResidual: + _require_modulus(modulus) + left = tuple(left_history) + right = tuple(right_history) + if left == right or len(left) != len(right): + raise CompactBrownianDomainError( + "winding witness needs distinct equal-horizon histories" + ) + if any(step not in {-1, 1} for step in left + right): + raise CompactBrownianDomainError("winding histories use only +/-1 steps") + left_lift = sum(left) + right_lift = sum(right) + if left_lift == right_lift or left_lift % modulus != right_lift % modulus: + raise CompactBrownianDomainError( + "winding histories need different lifts with one cycle endpoint" + ) + return WindingResidual( + modulus=modulus, + left_history=left, + right_history=right, + left_lift=left_lift, + right_lift=right_lift, + shared_residue=left_lift % modulus, + lost_observer="integer lift, winding, and chronological path observables", + ) + + +@dataclass(frozen=True) +class CompactSpaceClaimBoundary: + space: str + quotient_or_homogeneous_space: str + local_law: str + long_time_candidate: str + stability_mechanism: StabilityMechanism + unavailable_global_actions: tuple[str, ...] + retained_residuals: tuple[str, ...] + claim_boundary: str + + +CIRCLE_GATE = CompactSpaceClaimBoundary( + space="circle S^1", + quotient_or_homogeneous_space="R / (2*pi*Z)", + local_law="Gaussian only on a local/cover chart; globally a wrapped pushforward", + long_time_candidate="normalized Haar measure dtheta/(2*pi)", + stability_mechanism=StabilityMechanism.TIME_STATIONARY, + unavailable_global_actions=( + "global additive position chart", + "internal spatial dilation at fixed circumference", + ), + retained_residuals=("winding/deck lift", "clock periodicity", "path order"), + claim_boundary=( + "finite cycle calibration and continuum semantic contract only; no circle " + "heat-kernel or continuum mixing theorem" + ), +) + + +SPHERE_GATE = CompactSpaceClaimBoundary( + space="sphere S^2", + quotient_or_homogeneous_space="SO(3) / SO(2)", + local_law="Gaussian only in a tangent chart with curvature residual", + long_time_candidate="normalized Riemannian area measure dA/(4*pi)", + stability_mechanism=StabilityMechanism.TIME_STATIONARY, + unavailable_global_actions=( + "single global additive position chart", + "internal spatial dilation at fixed radius", + ), + retained_residuals=( + "curvature", + "chart transition", + "holonomy", + "path history", + ), + claim_boundary=( + "initial sphere gate only; no executable continuum Brownian construction, " + "heat-kernel theorem, or AMP universality result" + ), +) diff --git a/sonnet/brownian-scale-fibre/phase_compact_contract.py b/sonnet/brownian-scale-fibre/phase_compact_contract.py new file mode 100644 index 00000000..87d4f936 --- /dev/null +++ b/sonnet/brownian-scale-fibre/phase_compact_contract.py @@ -0,0 +1,270 @@ +"""Executable compact-space Brownian red-team contract for issue #162.""" + +from __future__ import annotations + +from dataclasses import dataclass +from fractions import Fraction +import importlib.util +from pathlib import Path +import sys + + +ROOT = Path(__file__).resolve().parents[2] + + +def _load(name: str, path: Path): + if name in sys.modules: + return sys.modules[name] + spec = importlib.util.spec_from_file_location(name, path) + assert spec and spec.loader + loaded = importlib.util.module_from_spec(spec) + sys.modules[name] = loaded + spec.loader.exec_module(loaded) + return loaded + + +firewall = _load( + "native_method_firewall", + ROOT / "workstreams/native_method_firewall/native_method_firewall.py", +) +line = _load("brownian_native", Path(__file__).with_name("brownian_native.py")) +compact = _load( + "compact_space_redteam", + Path(__file__).with_name("compact_space_redteam.py"), +) + + +COMPACT_REDTEAM_CONTRACT = firewall.MethodContract( + contract_id="brownian-compact-space-redteam-c0-s0", + problem=( + "separate scale-stable line laws from time-stationary compact-space laws " + "using exact history-fibre pushforwards" + ), + primitive_processes=( + "chronological nearest-neighbour histories on the integer lattice", + "deck quotient x -> x mod q", + "exact rational lazy-clock pushforward on a finite cycle", + ), + tasks=( + firewall.TaskContract( + task_id="cycle-deck-fibre", + observer="cycle endpoint after forgetting the integer lift", + deliverable=( + "exact residue counts, retained deck fibres, and an independent " + "direct-cycle certificate" + ), + regime="finite nearest-neighbour histories and cycle modulus q >= 2", + accuracy="exact integer counts", + claim_mode=firewall.ClaimMode.EXACT_FINITE, + failure_semantics=("invalid-modulus", "history-mass-mismatch"), + ), + firewall.TaskContract( + task_id="compact-stability-kind", + observer="terminal cycle law under a declared discrete clock", + deliverable=( + "typed periodic obstruction or exact stationary-law certificate" + ), + regime="finite cycles with exact rational stay probability", + accuracy="exact rational mass and period classification", + claim_mode=firewall.ClaimMode.EXACT_FINITE, + failure_semantics=( + "periodic-clock-obstruction", + "reducible-clock-obstruction", + "bounded-audit-not-a-limit-theorem", + ), + ), + firewall.TaskContract( + task_id="sphere-claim-boundary", + observer="global chart and stability mechanism declared for S^2", + deliverable="frozen obstruction/residual contract for the next phase", + regime="semantic gate only; no continuum implementation", + accuracy="claim-boundary audit", + claim_mode=firewall.ClaimMode.SEARCH_ONLY, + failure_semantics=( + "global-additive-chart-assumption", + "local-gaussian-promoted-globally", + ), + ), + ), + native_charts=( + "integer lift with exact endpoint counts", + "finite cycle residue chart with retained deck fibre", + "SO(3)/SO(2) sphere gate with curvature and holonomy residuals", + ), + retained_fibres=( + "integer lift and winding", + "chronological path", + "clock period", + "curvature/chart-transition/holonomy for the sphere phase", + ), + native_function_family=( + "finite counting measures and exact rational laws on additive history fibres" + ), + native_composition=( + "chronological step composition followed by deck-fibre pushforward" + ), + native_operators=( + "integer endpoint addition", + "residue quotient", + "finite fibre counting integral", + "exact lazy-clock pushforward", + ), + claim_boundary=( + "bounded cycle calibration plus circle/sphere semantic gates only; no " + "continuum heat kernel, mixing theorem, or arithmetic universality claim" + ), + allowed_lowerings=(), + baselines=( + firewall.BaselineSpec( + baseline_id="cycle-matrix-baseline", + mechanism=firewall.MethodMechanism.MATRIX_LINEARIZATION, + task_scope=("cycle-deck-fibre", "compact-stability-kind"), + purpose="independent finite transition comparison after native discovery", + independent_reference="finite cycle transition matrix", + ), + firewall.BaselineSpec( + baseline_id="compact-spectral-baseline", + mechanism=firewall.MethodMechanism.FOURIER_SPECTRAL, + task_scope=( + "cycle-deck-fibre", + "compact-stability-kind", + "sphere-claim-boundary", + ), + purpose="later independent circle/sphere spectral certificate", + independent_reference="Fourier or spherical-harmonic heat kernel", + ), + ), +).validate() + + +@dataclass(frozen=True) +class CompactGateResult: + line_distribution: object + folded: object + direct: object + deck_pushforward_certified: bool + periodic_obstruction: object + uniform_stationary_certified: bool + bounded_mixing: object + winding_residual: object + circle_gate: object + sphere_gate: object + trace: object + + +def run_compact_gate() -> CompactGateResult: + horizon = 7 + modulus = 5 + line_distribution = line.endpoint_fibres(1, horizon) + folded = compact.fold_integer_endpoint_counts( + ((point[0], count) for point, count in line_distribution.counts), + modulus=modulus, + horizon=horizon, + ) + direct = compact.direct_cycle_endpoint_counts(modulus, horizon) + deck_certified = folded.cycle.counts == direct.counts + if not deck_certified: # pragma: no cover - exact certificate must fail closed + raise AssertionError("deck pushforward and direct cycle update disagree") + + try: + compact.require_terminal_mixing_clock( + 6, + stay_probability=Fraction(0), + ) + except compact.PeriodicClockObstruction as error: + periodic_obstruction = error + else: # pragma: no cover - the even cycle must expose period two + raise AssertionError("even nearest-neighbour cycle hid its period") + + uniform = compact.CycleLaw.uniform(modulus) + uniform_stationary = ( + compact.cycle_step(uniform, stay_probability=Fraction(1, 2)) == uniform + ) + if not uniform_stationary: # pragma: no cover - exact fixed point + raise AssertionError("uniform cycle law was not stationary") + bounded_mixing = compact.bounded_lazy_mixing_audit( + modulus, + horizon=8, + stay_probability=Fraction(1, 2), + ) + winding_residual = compact.expose_winding_residual( + 4, + (1, 1, 1, 1), + (1, -1, 1, -1), + ) + + trace = firewall.MethodTrace(COMPACT_REDTEAM_CONTRACT) + fibre_event = trace.record( + task_id="cycle-deck-fibre", + lane=firewall.MethodLane.NATIVE_EVALUATION, + mechanism=firewall.MethodMechanism.TASK_FIBRE, + action="push exact integer endpoint counts through the deck quotient", + input_semantics="finite integer lifts with exact history multiplicities", + output_semantics="cycle counts plus every retained deck lift", + cost=firewall.CostLedger( + evaluation_steps=line_distribution.support_size, + live_state_units=modulus, + stored_history_units=sum(len(fibre) for fibre in folded.deck_fibres), + residual_units=1, + ), + ) + direct_certificate = trace.record( + task_id="cycle-deck-fibre", + lane=firewall.MethodLane.CERTIFICATE, + mechanism=firewall.MethodMechanism.EXACT_FINITE_ENUMERATION, + action="compare folded counts with an independent direct cycle update", + input_semantics="two exact constructions with one horizon and modulus", + output_semantics="zero residue-count mismatch", + cost=firewall.CostLedger( + evaluation_steps=2 * modulus * horizon, + stored_history_units=modulus, + ), + ) + stability_event = trace.record( + task_id="compact-stability-kind", + lane=firewall.MethodLane.NATIVE_DISCOVERY, + mechanism=firewall.MethodMechanism.NATIVE_PROCESS, + action="classify the discrete clock before making a terminal mixing claim", + input_semantics="cycle modulus and exact stay probability", + output_semantics="typed period-two obstruction or an aperiodic clock witness", + cost=firewall.CostLedger(discovery_steps=2, residual_units=1), + ) + stationary_event = trace.record( + task_id="compact-stability-kind", + lane=firewall.MethodLane.NATIVE_EVALUATION, + mechanism=firewall.MethodMechanism.NATIVE_FUNCTION_FAMILY, + action="apply one lazy cycle step to the exact uniform law", + input_semantics="rational cycle law and declared lazy clock", + output_semantics="exact stationary uniform law and bounded distance audit", + cost=firewall.CostLedger( + evaluation_steps=modulus * 3 * 9, + live_state_units=modulus, + residual_units=1, + ), + ) + trace.claim_native_result( + task_id="cycle-deck-fibre", + statement="cycle endpoint counts are the exact deck-fibre pushforward of line counts", + evidence_event_ids=(fibre_event.event_id, direct_certificate.event_id), + ) + trace.claim_native_result( + task_id="compact-stability-kind", + statement=( + "compact time-stationarity requires a clock classification and is not " + "the line's scale-renormalized stability" + ), + evidence_event_ids=(stability_event.event_id, stationary_event.event_id), + ) + return CompactGateResult( + line_distribution=line_distribution, + folded=folded, + direct=direct, + deck_pushforward_certified=deck_certified, + periodic_obstruction=periodic_obstruction, + uniform_stationary_certified=uniform_stationary, + bounded_mixing=bounded_mixing, + winding_residual=winding_residual, + circle_gate=compact.CIRCLE_GATE, + sphere_gate=compact.SPHERE_GATE, + trace=trace, + ) diff --git a/tests/research/test_brownian_compact_space_redteam.py b/tests/research/test_brownian_compact_space_redteam.py new file mode 100644 index 00000000..a074b092 --- /dev/null +++ b/tests/research/test_brownian_compact_space_redteam.py @@ -0,0 +1,214 @@ +"""Exact cycle/circle and frozen sphere red teams for issue #162.""" + +from __future__ import annotations + +import ast +from fractions import Fraction +import importlib.util +import json +from pathlib import Path +import sys + +import pytest + + +MODULE_PATH = ( + Path(__file__).parents[2] + / "sonnet/brownian-scale-fibre/phase_compact_contract.py" +) +SPEC = importlib.util.spec_from_file_location("brownian_compact_contract", MODULE_PATH) +assert SPEC and SPEC.loader +module = importlib.util.module_from_spec(SPEC) +sys.modules[SPEC.name] = module +SPEC.loader.exec_module(module) +compact = module.compact +line = module.line +firewall = module.firewall + + +@pytest.mark.parametrize( + "modulus,horizon", + ((2, 0), (2, 5), (3, 7), (4, 6), (5, 9), (11, 4)), +) +def test_cycle_counts_are_the_exact_deck_pushforward_of_line_counts( + modulus: int, + horizon: int, +) -> None: + line_distribution = line.endpoint_fibres(1, horizon) + folded = compact.fold_integer_endpoint_counts( + ((point[0], count) for point, count in line_distribution.counts), + modulus=modulus, + horizon=horizon, + ) + direct = compact.direct_cycle_endpoint_counts(modulus, horizon) + assert folded.cycle.counts == direct.counts + assert folded.cycle.total_histories == 2**horizon + assert all( + lift % modulus == residue + for residue, fibre in enumerate(folded.deck_fibres) + for lift, _ in fibre + ) + + +def test_deck_fibres_retain_lifts_that_the_cycle_endpoint_forgets() -> None: + line_distribution = line.endpoint_fibres(1, 8) + folded = compact.fold_integer_endpoint_counts( + ((point[0], count) for point, count in line_distribution.counts), + modulus=4, + horizon=8, + ) + zero_fibre = folded.deck_fibres[0] + assert tuple(lift for lift, _ in zero_fibre) == (-8, -4, 0, 4, 8) + assert sum(count for _, count in zero_fibre) == folded.cycle.counts[0] + + +def test_even_cycle_period_two_is_a_typed_terminal_limit_obstruction() -> None: + assert compact.nearest_neighbour_period( + 6, + stay_probability=Fraction(0), + ) == 2 + with pytest.raises( + compact.PeriodicClockObstruction, + match="periodic-clock-obstruction", + ) as error: + compact.require_terminal_mixing_clock( + 6, + stay_probability=Fraction(0), + ) + assert error.value.modulus == 6 + assert error.value.period == 2 + + +def test_odd_or_lazy_cycle_has_no_period_two_obstruction() -> None: + assert compact.nearest_neighbour_period( + 5, + stay_probability=Fraction(0), + ) == 1 + assert compact.nearest_neighbour_period( + 6, + stay_probability=Fraction(1, 2), + ) == 1 + compact.require_terminal_mixing_clock(5, stay_probability=Fraction(0)) + compact.require_terminal_mixing_clock(6, stay_probability=Fraction(1, 2)) + + +def test_stationary_uniform_does_not_imply_mixing_for_a_frozen_clock() -> None: + uniform = compact.CycleLaw.uniform(5) + assert compact.cycle_step(uniform, stay_probability=Fraction(1)) == uniform + with pytest.raises( + compact.ReducibleClockObstruction, + match="reducible-clock-obstruction", + ): + compact.require_terminal_mixing_clock( + 5, + stay_probability=Fraction(1), + ) + + +def test_even_nonlazy_cycle_stays_far_from_uniform_at_every_bounded_time() -> None: + modulus = 6 + uniform = compact.CycleLaw.uniform(modulus) + for horizon in range(10): + counts = compact.direct_cycle_endpoint_counts(modulus, horizon) + law = compact.CycleLaw( + tuple(Fraction(count, counts.total_histories) for count in counts.counts) + ) + assert compact.total_variation(law, uniform) >= Fraction(1, 2) + + +@pytest.mark.parametrize("modulus", (2, 3, 5, 8)) +def test_uniform_cycle_law_is_an_exact_fixed_point(modulus: int) -> None: + uniform = compact.CycleLaw.uniform(modulus) + for stay_probability in (Fraction(0), Fraction(1, 2), Fraction(1)): + assert ( + compact.cycle_step(uniform, stay_probability=stay_probability) + == uniform + ) + + +def test_bounded_lazy_audit_records_a_trend_without_claiming_a_limit() -> None: + audit = compact.bounded_lazy_mixing_audit( + 5, + horizon=12, + stay_probability=Fraction(1, 2), + ) + assert audit.nonincreasing + assert audit.distances_to_uniform[-1] < audit.distances_to_uniform[0] + assert "no asymptotic mixing theorem" in audit.claim_boundary + + +def test_cycle_endpoint_quotient_exposes_winding_loss() -> None: + residual = compact.expose_winding_residual( + 4, + (1, 1, 1, 1), + (1, -1, 1, -1), + ) + assert residual.left_lift == 4 + assert residual.right_lift == 0 + assert residual.shared_residue == 0 + assert "winding" in residual.lost_observer + + +def test_circle_and_sphere_use_time_stationarity_not_line_scale_stability() -> None: + assert ( + compact.CIRCLE_GATE.stability_mechanism + is compact.StabilityMechanism.TIME_STATIONARY + ) + assert ( + compact.SPHERE_GATE.stability_mechanism + is compact.StabilityMechanism.TIME_STATIONARY + ) + assert ( + compact.CIRCLE_GATE.stability_mechanism + is not compact.StabilityMechanism.SCALE_RENORMALIZED + ) + assert compact.CIRCLE_GATE.quotient_or_homogeneous_space == "R / (2*pi*Z)" + assert compact.SPHERE_GATE.quotient_or_homogeneous_space == "SO(3) / SO(2)" + assert "curvature" in compact.SPHERE_GATE.retained_residuals + assert "no executable continuum" in compact.SPHERE_GATE.claim_boundary + + +def test_compact_gate_is_machine_auditable_and_lane_separated() -> None: + result = module.run_compact_gate() + report = json.loads(result.trace.to_json()) + assert result.deck_pushforward_certified + assert result.periodic_obstruction.period == 2 + assert result.uniform_stationary_certified + assert result.bounded_mixing.nonincreasing + assert report["summary"]["lane_counts"] == { + "baseline": 0, + "certificate": 1, + "native-discovery": 1, + "native-evaluation": 2, + } + assert len(report["native_claims"]) == 2 + assert report["summary"]["cost_scalarization"] == "not-authorized" + + +def test_compact_engine_imports_no_classical_discovery_backend() -> None: + source_path = MODULE_PATH.with_name("compact_space_redteam.py") + source = source_path.read_text(encoding="utf-8") + tree = ast.parse(source) + imported_roots = { + alias.name.split(".")[0] + for node in ast.walk(tree) + if isinstance(node, (ast.Import, ast.ImportFrom)) + for alias in node.names + } + assert not imported_roots & {"numpy", "scipy", "sympy"} + assert ".series(" not in source + assert ".fourier_transform(" not in source + assert ".as_matrix(" not in source + + +def test_classical_cycle_or_sphere_oracle_cannot_enter_native_discovery() -> None: + trace = firewall.MethodTrace(module.COMPACT_REDTEAM_CONTRACT) + with pytest.raises(firewall.PrematureLoweringError): + trace.record( + task_id="compact-stability-kind", + lane=firewall.MethodLane.NATIVE_DISCOVERY, + mechanism=firewall.MethodMechanism.FOURIER_SPECTRAL, + action="supply a stationary law from a spectral oracle", + input_semantics="undeveloped compact process", + output_semantics="answer-shaped uniform law", + )