From 3c3d2c296e081bd06a143ffcb2db653590ab1dc7 Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 17 Jul 2026 16:12:54 -0700 Subject: [PATCH] =?UTF-8?q?feat(adr-0244):=20Phase=201=20=E2=80=94=20opera?= =?UTF-8?q?tor-preservation=20identity=20manifold=20primitive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements ADR-0244 §2.1/§4a: the metric-exact operator-preservation geometry, pure and off-serve (wired into the runtime gate in Phase 2). core/physics/identity_manifold.py: - lift_axis: R^3 pack direction -> grade-1 Cl(4,1) at e1/e2/e3 slots (basis_vector, not embed_point) so the value subspace lives in the spatial grade-1 block where <.,.>_0 = Euclidean and the Gram matrix is positive-definite. - gram_matrix + ManifoldConditioningError (fail closed when cond(G)>1e5). - subspace_project: metric-orthogonal P_I(x) with SIGNED coefficients. - sandwich: versor action R x R~. - euclidean_norm: positive-definite leakage magnitude (NOT the indefinite Cl(4,1) norm, which can vanish/negate an e5-boost leakage). - IdentityManifoldGeometry (frozen): axes_psi + Gram + Gram^-1; .project, .axis_response -> (leakage, self_align), .leakage_rms. Operator-preservation, per the ratified core-mechanism correction: the live trajectory final_state.F is a VERSOR (even-grade operator, zero grade-1 content), so we measure whether it PRESERVES the value subspace via F a_i F~, not whether it lives in it. Two per-axis measures, both required and non-redundant: - subspace leakage euclidean_norm(rot_i - P_I(rot_i)) catches tilt of a value axis toward an alien dimension (e4/e5); - signed self-alignment _0 catches in-subspace inversion (e1 -> -e1: leakage 0 but self-align -1). tests/test_adr_0244_identity_manifold.py (24 tests) pins every falsifiable claim: G=I for the default pack, idempotent projection, identity versor -> 0 leakage/+1 align, within-plane rotation -> no leak, e4/e5 tilt -> leak, pi-inversion -> 0 leak but -1 self-align, near-degenerate axes -> ManifoldConditioningError, determinism. [Verification]: 24 new tests passed; in-worktree smoke 176 passed; fast lane (-m 'not quarantine and not slow' -n auto) 11863 passed, 109 skipped; serve-quarantine transitive test 2 passed (new module is NOT dragged onto the serve path — off-serve until Phase 2). --- core/physics/identity_manifold.py | 217 ++++++++++++++++++++ tests/test_adr_0244_identity_manifold.py | 249 +++++++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100644 core/physics/identity_manifold.py create mode 100644 tests/test_adr_0244_identity_manifold.py diff --git a/core/physics/identity_manifold.py b/core/physics/identity_manifold.py new file mode 100644 index 00000000..9dbb2b81 --- /dev/null +++ b/core/physics/identity_manifold.py @@ -0,0 +1,217 @@ +"""core.physics.identity_manifold — metric-exact operator-preservation identity geometry. + +ADR-0244 §2.1 / §4a (operator-preservation reframe, governance annotation item 12). + +The identity manifold is a fixed geometric subspace ``I = span(axis_1, …, axis_n)`` +of the Cl(4,1) state space, encoding CORE's value axes. The live identity +trajectory is ``final_state.F``, whose class invariant is +``versor_condition(F) < 1e-6`` — i.e. **F is a versor: an even-grade operator +(grades 0,2,4) with zero grade-1 content.** Projecting such an operator onto the +grade-1 value subspace is vacuous (``P_I(F) = 0`` identically). The geometrically +correct question for an *operator* against a *subspace* is whether the operator +**preserves** the subspace — evaluated by its action on the axes via the sandwich +product ``F aᵢ F̃``: + + * **subspace leakage** ``‖F aᵢ F̃ − P_I(F aᵢ F̃)‖₂`` — the out-of-subspace + component of each rotated axis; catches a versor tilting a value axis toward + an alien dimension (e4/e5). The magnitude is the positive-definite Euclidean + coefficient norm — NOT the indefinite Cl(4,1) inner product ``⟨S, S̃⟩₀``, + which signature (+,+,+,+,−) permits to vanish (or go negative for an e5/boost + component) for nonzero leakage, silently hiding a breach. + * **signed self-alignment** ``⟨aᵢ, F aᵢ F̃⟩₀`` — the signed overlap of an axis + with its own rotated image; catches an in-subspace *inversion* + (``e1 → −e1``: leakage 0 but self-alignment −1). Never ``abs()``'d, so + anti-alignment (opposition) stays distinguishable from orthogonality. + +Both measures are required and non-redundant. + +Value axes are lifted from the pack's R³ ``direction`` to grade-1 Cl(4,1) +multivectors at the e1/e2/e3 slots (``algebra.cl41.basis_vector(0..2)``), so +``I`` lives in the spatial grade-1 block where ``⟨·,·⟩₀`` coincides with the +Euclidean inner product and the Gram matrix is positive-definite. + +This module is pure (depends only on ``algebra.cl41``), deterministic, and +float64 throughout — the offline precision domain. The f64→f32 serving cast +(ADR-0244 §2.5 / ADR-0245 §2.2) applies only to the live per-turn versor at the +Phase-2 gate boundary, not to this axis construction. Off-serve until ADR-0244 +D4 Phase 2 wires it into ``core.physics.identity``. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Sequence + +import numpy as np + +from algebra.cl41 import ( + N_COMPONENTS, + basis_vector, + geometric_product, + reverse, + scalar_part, +) + +# Gram condition-number ceiling above which axis modes are too near-degenerate +# to resolve without mode-aliasing (ADR-0244 §2.1). +CONDITION_BOUND: float = 1e5 + + +class ManifoldConditioningError(ValueError): + """Raised when the value-axis Gram matrix is too ill-conditioned. + + A condition number above :data:`CONDITION_BOUND` means two or more value + axes are near-degenerate (nearly parallel), so the metric-exact projection + onto their span cannot be resolved without mode-aliasing. Fail closed rather + than return an unreliable projection. + """ + + +def _inner0(a: np.ndarray, b: np.ndarray) -> float: + """The Cl(4,1) metric inner product ⟨a, b⟩₀ = scalar_part(a · reverse(b)). + + Symmetric in ``a`` and ``b``. Indefinite in general (signature (+,+,+,+,−)), + but positive-definite when restricted to the spatial grade-1 block the value + axes occupy. + """ + return scalar_part(geometric_product(a, reverse(b))) + + +def lift_axis(direction: Sequence[float]) -> np.ndarray: + """Lift a value-axis ``direction`` (R³) to a grade-1 Cl(4,1) multivector. + + Places the three components at the e1/e2/e3 grade-1 slots via + :func:`algebra.cl41.basis_vector`. NOT :func:`algebra.cga.embed_point`, + which maps to null-cone points and would make the Gram matrix a distance + table rather than a metric inner product. Returns a float64 (32,) array. + """ + direction = tuple(float(x) for x in direction) + if len(direction) != 3: + raise ValueError( + f"value-axis direction must have length 3, got {len(direction)}" + ) + psi = np.zeros(N_COMPONENTS, dtype=np.float64) + for k, component in enumerate(direction): + psi = psi + component * basis_vector(k).astype(np.float64) + return psi + + +def gram_matrix(axes_psi: Sequence[np.ndarray]) -> np.ndarray: + """Symmetric metric-restricted Gram matrix ``G_ij = ⟨axis_i, axis_j⟩₀``. + + Raises :class:`ManifoldConditioningError` when ``cond(G) > CONDITION_BOUND``. + """ + n = len(axes_psi) + if n == 0: + raise ValueError("identity manifold requires at least one value axis") + G = np.empty((n, n), dtype=np.float64) + for i in range(n): + for j in range(n): + G[i, j] = _inner0(axes_psi[i], axes_psi[j]) + condition = float(np.linalg.cond(G)) + if condition > CONDITION_BOUND: + raise ManifoldConditioningError( + f"value-axis Gram condition number {condition:.3e} exceeds " + f"{CONDITION_BOUND:.0e}: axes are near-degenerate" + ) + return G + + +def subspace_project( + x: np.ndarray, axes_psi: Sequence[np.ndarray], gram_inv: np.ndarray +) -> np.ndarray: + """Metric-orthogonal projection of ``x`` onto ``I = span(axes_psi)``. + + ``P_I(x) = Σ_ij axis_i · (G⁻¹)_ij · ⟨axis_j, x⟩₀``. The overlap coefficients + are SIGNED (never ``abs()``'d) so orientation is preserved. + """ + coeffs_raw = np.array( + [_inner0(a, x) for a in axes_psi], dtype=np.float64 + ) + coeffs = gram_inv @ coeffs_raw + out = np.zeros(N_COMPONENTS, dtype=np.float64) + for weight, axis in zip(coeffs, axes_psi): + out = out + weight * axis + return out + + +def sandwich(versor: np.ndarray, x: np.ndarray) -> np.ndarray: + """Versor action ``R x R̃``. For a versor ``R`` this preserves grade and + norm, so a grade-1 axis maps to a grade-1 vector of the same magnitude.""" + return geometric_product(geometric_product(versor, x), reverse(versor)) + + +def euclidean_norm(s: np.ndarray) -> float: + """Positive-definite coefficient-Euclidean norm ``‖s‖₂``. + + Used for the leakage *magnitude* — deliberately NOT the indefinite Cl(4,1) + norm ``⟨s, s̃⟩₀``, which can vanish or go negative for a nonzero leakage + (e.g. an e5/boost component), silently hiding a breach. + """ + return float(np.linalg.norm(np.asarray(s, dtype=np.float64), ord=2)) + + +@dataclass(frozen=True) +class IdentityManifoldGeometry: + """Frozen operator-preservation geometry for a set of value axes. + + Constructed once at manifold/pack load and never mutated within a session + (ADR-0244 governance annotation item 8 — the identity subspace is inalienable + by construction). ``axes_psi`` are the grade-1 lifts; ``gram`` / ``gram_inv`` + the metric-restricted Gram matrix and its inverse. + """ + + axes_psi: tuple[np.ndarray, ...] + gram: np.ndarray + gram_inv: np.ndarray + + @classmethod + def from_directions( + cls, directions: Sequence[Sequence[float]] + ) -> "IdentityManifoldGeometry": + """Build the geometry from pack value-axis directions (R³ each). + + Raises :class:`ManifoldConditioningError` if the axes are near-degenerate. + """ + axes = tuple(lift_axis(d) for d in directions) + gram = gram_matrix(axes) + gram_inv = np.linalg.inv(gram) + return cls(axes_psi=axes, gram=gram, gram_inv=gram_inv) + + def project(self, x: np.ndarray) -> np.ndarray: + """Metric-orthogonal projection of ``x`` onto the value subspace.""" + return subspace_project(x, self.axes_psi, self.gram_inv) + + def axis_response( + self, versor: np.ndarray + ) -> tuple[list[float], list[float]]: + """Per-axis operator-preservation measures for ``versor``. + + Returns ``(leakage, self_align)`` — parallel lists over the value axes: + + * ``leakage[i]`` = ``‖R aᵢ R̃ − P_I(R aᵢ R̃)‖₂`` (subspace departure; + catches tilt toward alien dimensions e4/e5). + * ``self_align[i]`` = ``⟨aᵢ, R aᵢ R̃⟩₀`` (signed orientation; catches + in-subspace inversion — ``e1 → −e1`` gives leakage 0 but −1 here). + """ + versor = np.asarray(versor, dtype=np.float64) + leakage: list[float] = [] + self_align: list[float] = [] + for axis in self.axes_psi: + rotated = sandwich(versor, axis) + rejection = rotated - subspace_project( + rotated, self.axes_psi, self.gram_inv + ) + leakage.append(euclidean_norm(rejection)) + self_align.append(_inner0(axis, rotated)) + return leakage, self_align + + def leakage_rms(self, versor: np.ndarray) -> float: + """Root-mean-square subspace leakage over all axes. + + Each rotated axis is unit-norm (a versor preserves norm), so this is the + aggregate subspace-departure fraction in ``[0, 1]``; the Phase-2 gate's + ``score`` is ``1 − leakage_rms``. + """ + leakage, _ = self.axis_response(versor) + return float((sum(value * value for value in leakage) / len(leakage)) ** 0.5) diff --git a/tests/test_adr_0244_identity_manifold.py b/tests/test_adr_0244_identity_manifold.py new file mode 100644 index 00000000..40d16c94 --- /dev/null +++ b/tests/test_adr_0244_identity_manifold.py @@ -0,0 +1,249 @@ +"""ADR-0244 §2.1/§4a — operator-preservation identity manifold primitive. + +Pins the falsifiable claims of the metric-exact operator-preservation geometry +(governance annotation item 12): a versor's action on the value axes reveals +whether it PRESERVES the identity subspace. Subspace leakage catches tilt toward +alien dimensions (e4/e5); signed self-alignment catches in-subspace inversion. +Both are required and non-redundant. + +All numbers here were pre-verified against ``algebra.cl41`` before implementation +(see the ADR-0244 D4 plan progress log, 2026-07-17 operator-preservation entry). +""" + +from __future__ import annotations + +import numpy as np +import pytest + +from algebra.cl41 import N_COMPONENTS, basis_vector, grade_start, grade_count +from core.physics.identity_manifold import ( + CONDITION_BOUND, + IdentityManifoldGeometry, + ManifoldConditioningError, + euclidean_norm, + gram_matrix, + lift_axis, + sandwich, + subspace_project, +) + +# Default identity pack axes (packs/identity/default_general_v1.json): the three +# spatial basis directions truthfulness/coherence/reverence. +DEFAULT_DIRECTIONS = ([1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]) + +# Grade-2 bivector component indices (grade-2 block starts at grade_start(2)=6; +# combinations(range(5),2) order → (0,1)=e12=6, (0,3)=e14=8, (0,4)=e15=9). +_E12, _E14, _E15 = 6, 8, 9 + + +def _spatial_rotor(biv_idx: int, theta: float) -> np.ndarray: + """Unit rotor cos(θ/2) + sin(θ/2)·B for a B²=−1 (spatial) plane.""" + R = np.zeros(N_COMPONENTS, dtype=np.float64) + R[0] = np.cos(theta / 2.0) + R[biv_idx] = np.sin(theta / 2.0) + return R + + +def _boost(biv_idx: int, theta: float) -> np.ndarray: + """Unit boost cosh(θ/2) + sinh(θ/2)·B for a B²=+1 (e5-containing) plane.""" + R = np.zeros(N_COMPONENTS, dtype=np.float64) + R[0] = np.cosh(theta / 2.0) + R[biv_idx] = np.sinh(theta / 2.0) + return R + + +def _identity_versor() -> np.ndarray: + R = np.zeros(N_COMPONENTS, dtype=np.float64) + R[0] = 1.0 + return R + + +def _geom() -> IdentityManifoldGeometry: + return IdentityManifoldGeometry.from_directions(DEFAULT_DIRECTIONS) + + +# --- lift_axis ------------------------------------------------------------ + +def test_lift_axis_places_components_at_grade1_slots(): + psi = lift_axis([1.0, 0.0, 0.0]) + assert psi.dtype == np.float64 + assert psi.shape == (N_COMPONENTS,) + # e1 lives at component index 1 (basis_vector(0)); everything else zero. + np.testing.assert_array_equal(psi, basis_vector(0).astype(np.float64)) + assert psi[1] == 1.0 + assert np.count_nonzero(psi) == 1 + + +def test_lift_axis_is_pure_grade1(): + psi = lift_axis([0.6, 0.8, 0.0]) + g1_start, g1_count = grade_start(1), grade_count(1) + grade1_energy = float(np.sum(psi[g1_start : g1_start + g1_count] ** 2)) + total_energy = float(np.sum(psi**2)) + assert grade1_energy == pytest.approx(total_energy) # all energy in grade 1 + assert psi[1] == pytest.approx(0.6) + assert psi[2] == pytest.approx(0.8) + + +def test_lift_axis_rejects_non_r3(): + with pytest.raises(ValueError): + lift_axis([1.0, 0.0]) + + +# --- gram_matrix ---------------------------------------------------------- + +def test_gram_of_default_pack_is_identity(): + geom = _geom() + np.testing.assert_allclose(geom.gram, np.eye(3), atol=1e-12) + np.testing.assert_allclose(geom.gram_inv, np.eye(3), atol=1e-12) + + +def test_gram_is_symmetric(): + axes = [lift_axis(d) for d in ([0.6, 0.8, 0.0], [0.0, 0.6, 0.8], [0.8, 0.0, 0.6])] + G = gram_matrix(axes) + np.testing.assert_allclose(G, G.T, atol=1e-12) + + +def test_near_degenerate_axes_raise_conditioning_error(): + # Two almost-parallel axes → ill-conditioned Gram → fail closed. + axes = [ + lift_axis([1.0, 0.0, 0.0]), + lift_axis([1.0, 1e-7, 0.0]), + lift_axis([0.0, 0.0, 1.0]), + ] + with pytest.raises(ManifoldConditioningError): + gram_matrix(axes) + + +def test_empty_axes_rejected(): + with pytest.raises(ValueError): + gram_matrix([]) + + +# --- subspace_project ----------------------------------------------------- + +def test_projection_is_idempotent(): + geom = _geom() + rng = np.random.default_rng(0) + x = rng.standard_normal(N_COMPONENTS) + p1 = geom.project(x) + p2 = geom.project(p1) + np.testing.assert_allclose(p2, p1, atol=1e-12) + + +def test_in_subspace_vector_is_fixed_by_projection(): + geom = _geom() + x = lift_axis([0.3, -0.5, 0.7]) # lives in span(e1,e2,e3) + np.testing.assert_allclose(geom.project(x), x, atol=1e-12) + + +def test_out_of_subspace_grade1_projects_to_zero(): + geom = _geom() + e4 = basis_vector(3).astype(np.float64) # e4 ∉ span(e1,e2,e3) + assert euclidean_norm(geom.project(e4)) < 1e-12 + + +def test_standalone_project_matches_geometry_method(): + geom = _geom() + rng = np.random.default_rng(1) + x = rng.standard_normal(N_COMPONENTS) + np.testing.assert_allclose( + subspace_project(x, geom.axes_psi, geom.gram_inv), geom.project(x), atol=1e-15 + ) + + +def test_condition_bound_is_the_documented_1e5(): + assert CONDITION_BOUND == pytest.approx(1e5) + + +# --- sandwich ------------------------------------------------------------- + +def test_sandwich_identity_leaves_axis_unchanged(): + e1 = basis_vector(0).astype(np.float64) + np.testing.assert_allclose(sandwich(_identity_versor(), e1), e1, atol=1e-12) + + +def test_sandwich_preserves_norm_for_versor(): + e1 = basis_vector(0).astype(np.float64) + R = _spatial_rotor(_E14, 0.9) + rotated = sandwich(R, e1) + assert euclidean_norm(rotated) == pytest.approx(1.0, abs=1e-9) + + +# --- axis_response: the core operator-preservation claims ----------------- + +def test_identity_versor_perfectly_preserves(): + geom = _geom() + leakage, self_align = geom.axis_response(_identity_versor()) + assert max(leakage) < 1e-12 + for a in self_align: + assert a == pytest.approx(1.0, abs=1e-9) + + +def test_rotation_within_value_plane_does_not_leak(): + # e12 rotation keeps every value axis inside span(e1,e2,e3). + geom = _geom() + leakage, _ = geom.axis_response(_spatial_rotor(_E12, 0.5)) + assert max(leakage) < 1e-9 + + +def test_tilt_toward_e4_leaks(): + # e14 rotation tilts the e1 axis toward e4 (out of the value subspace). + geom = _geom() + leakage, _ = geom.axis_response(_spatial_rotor(_E14, 0.5)) + assert leakage[0] > 0.05 # e1 axis leaks + assert geom.leakage_rms(_spatial_rotor(_E14, 0.5)) > 0.05 + + +def test_boost_toward_e5_leaks(): + # e15 boost tilts the e1 axis toward e5 — the Euclidean norm catches this + # even though the indefinite Cl(4,1) norm would (mis)count e5 as negative. + geom = _geom() + leakage, _ = geom.axis_response(_boost(_E15, 0.5)) + assert leakage[0] > 0.05 + + +def test_larger_tilt_leaks_more(): + geom = _geom() + small = geom.leakage_rms(_spatial_rotor(_E14, 0.5)) + large = geom.leakage_rms(_spatial_rotor(_E14, 1.2)) + assert large > small + + +def test_in_subspace_inversion_caught_by_self_alignment_not_leakage(): + # π rotation in e12 sends e1 → −e1: still inside span(e1,e2,e3) (leakage 0), + # but inverted. Only the signed self-alignment catches it. + geom = _geom() + leakage, self_align = geom.axis_response(_spatial_rotor(_E12, np.pi)) + assert leakage[0] < 1e-9 # subspace-rejection blind to inversion + assert self_align[0] < -0.9 # signed orientation catches it + + +def test_self_alignment_is_signed_not_absolute(): + # A partial rotation gives a self-alignment strictly between the inverted + # (−1) and preserved (+1) extremes; never collapsed to |·|. + geom = _geom() + _, self_align = geom.axis_response(_spatial_rotor(_E12, 2.4)) + assert -1.0 <= self_align[0] < 0.0 # past 90°, genuinely negative + + +# --- geometry object contract + determinism ------------------------------- + +def test_geometry_is_frozen(): + geom = _geom() + with pytest.raises((AttributeError, TypeError)): + geom.gram_inv = np.eye(3) # type: ignore[misc] + + +def test_from_directions_propagates_conditioning_error(): + with pytest.raises(ManifoldConditioningError): + IdentityManifoldGeometry.from_directions( + ([1.0, 0.0, 0.0], [1.0, 1e-7, 0.0], [0.0, 0.0, 1.0]) + ) + + +def test_axis_response_is_deterministic(): + geom = _geom() + R = _spatial_rotor(_E14, 0.7) + first = geom.axis_response(R) + second = geom.axis_response(R) + assert first == second # bit-exact repeat (pure f64, no randomness)