feat(adr-0246): §3 induced-action primitives (pure, off-serving)
First implementation unit of ADR-0246 Ring-1 per the preflight brief §3/§13.
Promotes the induced-action apparatus from the slice-0 eval prototype into pure,
tested geometry — the single source of truth both the future gate surface and the
§11 grounding-feasibility study will consume.
core/physics/identity_manifold.py:
+ induced_action(versor) A(F) = G^-1 · <a_k, F a_j F~>_0 (§3.1)
+ orthogonality_defect(versor) d_orth = ||A^T G A - G||_F (§3.2)
+ typed_residual_energy(versor) e4/e5/spatial_foreign/unclassified (§3.6)
+ orthogonality_defect_of_action / SPATIAL_GRADE1_INDICES / E4,E5 index pins
core/physics/identity_action.py (new, pure):
+ IdentityStabilizer (locked singleton H_id={I}, §3.3)
+ stabilizer_defect d_stab = min_H ||A - H||_G (§3.2)
G-weighted norm reduces to Frobenius at G=I (default pack)
evals/adr_0246_mismatch_diagnostic: rewired to delegate to the canonical
primitives (removes the duplicate prototype copies; drops unused import).
Scope: off-serving (algebra-only, A-04 quarantine, pinned by test); no gate,
threshold, axis, H_id, corrector, or flag change. identity_wave_gate stays
default-off. Path ledger (§3.4/3.5), gate admit surface (§3.7), and the eval
matrix + §11 feasibility study are subsequent units (brief §0a records the
sequencing). No self-Accept.
[Verification]: uv run core test --suite smoke -q => 176 passed;
tests/test_adr_0246_induced_action.py 12 passed (RED-first);
identity surfaces (mismatch-diagnostic rewired, identity_manifold, identity_gate
wave/runtime/eval, gamma_calibration) 87 passed.
This commit is contained in:
parent
04d67ca535
commit
4941cf188e
5 changed files with 456 additions and 74 deletions
131
core/physics/identity_action.py
Normal file
131
core/physics/identity_action.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
"""core.physics.identity_action — lawful identity action policy (ADR-0246 §3.2–§3.3).
|
||||
|
||||
Where :mod:`core.physics.identity_manifold` measures *what a versor does* to the
|
||||
value frame (the induced action ``A(F)``, its orthogonality defect ``d_orth``, and
|
||||
typed leakage), this module measures *whether that action is lawful* — how far the
|
||||
induced action sits from the explicitly permitted identity actions ``H_id``.
|
||||
|
||||
The two diagnostics are deliberately never collapsed (ADR-0246 §3.2):
|
||||
|
||||
* ``d_orth`` (in identity_manifold) — detects non-isometric / numerically
|
||||
corrupt action on the subspace. A conditioning check, NOT an authorization.
|
||||
* ``d_stab`` (here) — ``min_{H ∈ H_id} ‖A(F) − H‖_G`` — detects departure from
|
||||
the explicitly *permitted* identity action. This is the lawfulness measure.
|
||||
|
||||
**Locked stabilizer (ADR-0246 §3.3).** For the default identity pack
|
||||
|
||||
H_id = { I }
|
||||
|
||||
the singleton containing only the identity matrix in the axis basis. Algebraic
|
||||
cleanliness ≠ identity lawfulness, so ``-I`` (global inversion), axis
|
||||
permutations, continuous rotations, and arbitrary reweightings are **excluded**.
|
||||
Under a singleton stabilizer there is no continuous projection that "invents" a
|
||||
lawful action: ``d_stab`` is a pass/fail distance, and callers must NOT soft-project
|
||||
``A`` onto ``I`` and then compose the projection as if the turn were lawful.
|
||||
Enlarging ``H_id`` is a future, explicit, reviewed pack/policy change — never an
|
||||
implicit convenience here.
|
||||
|
||||
Pure (numpy + identity_manifold only), deterministic, float64, off-serving. The
|
||||
lawful-only path composition and hard-break ledger (§3.4/§3.5) are a separate,
|
||||
later unit; this module provides only the per-turn stabilizer defect.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
|
||||
from core.physics.identity_manifold import IdentityManifoldGeometry
|
||||
|
||||
# Below this the axis Gram is treated as the identity matrix and the G-weighted
|
||||
# norm collapses to the plain Frobenius norm (exact for the default pack).
|
||||
_IDENTITY_GRAM_TOL: float = 1e-9
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IdentityStabilizer:
|
||||
"""The permitted identity actions ``H_id`` in the axis basis.
|
||||
|
||||
``members`` are the allowed action matrices. The default (and only ratified)
|
||||
policy is the singleton ``{ I }`` — see module docstring / ADR-0246 §3.3.
|
||||
Constructing a non-singleton stabilizer is possible for research/analysis but
|
||||
is NOT a ratified live policy; enlarging ``H_id`` for serving requires an
|
||||
explicit reviewed change.
|
||||
"""
|
||||
|
||||
members: tuple[np.ndarray, ...]
|
||||
|
||||
@classmethod
|
||||
def singleton(cls, dimension: int) -> "IdentityStabilizer":
|
||||
"""The locked default ``H_id = { I_dimension }``."""
|
||||
return cls(members=(np.eye(int(dimension), dtype=np.float64),))
|
||||
|
||||
@property
|
||||
def is_singleton_identity(self) -> bool:
|
||||
"""True iff this is exactly the locked default ``{ I }``."""
|
||||
return len(self.members) == 1 and bool(
|
||||
np.allclose(self.members[0], np.eye(self.members[0].shape[0]))
|
||||
)
|
||||
|
||||
|
||||
def _matrix_sqrt_spd(gram: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
||||
"""``(G^{1/2}, G^{-1/2})`` for a symmetric positive-definite Gram via eigh."""
|
||||
eigvals, eigvecs = np.linalg.eigh(gram)
|
||||
if float(eigvals.min()) <= 0.0:
|
||||
raise ValueError("stabilizer defect requires a positive-definite Gram")
|
||||
root = eigvecs @ np.diag(np.sqrt(eigvals)) @ eigvecs.T
|
||||
inv_root = eigvecs @ np.diag(1.0 / np.sqrt(eigvals)) @ eigvecs.T
|
||||
return root, inv_root
|
||||
|
||||
|
||||
def _g_weighted_frobenius(matrix: np.ndarray, gram: np.ndarray) -> float:
|
||||
"""Metric-consistent matrix norm ``‖M‖_G`` (ADR-0246 §3.2).
|
||||
|
||||
Measured in a ``G``-orthonormal frame: ``‖M‖_G = ‖G^{1/2} M G^{-1/2}‖_F``.
|
||||
Reduces exactly to the plain Frobenius norm when ``G = I`` (the default pack),
|
||||
and is invariant to the metric-preserving change of axis coordinates. The
|
||||
convention is fixed here for the value packs in use; a broader-pack review may
|
||||
revisit it (ADR-0246 §3.2 leaves general-pack ``‖·‖_G`` to ADR-0246 proper).
|
||||
"""
|
||||
if np.allclose(gram, np.eye(gram.shape[0]), atol=_IDENTITY_GRAM_TOL):
|
||||
return float(np.linalg.norm(matrix, ord="fro"))
|
||||
root, inv_root = _matrix_sqrt_spd(gram)
|
||||
return float(np.linalg.norm(root @ matrix @ inv_root, ord="fro"))
|
||||
|
||||
|
||||
def stabilizer_defect(
|
||||
action: np.ndarray,
|
||||
gram: np.ndarray,
|
||||
stabilizer: IdentityStabilizer,
|
||||
) -> float:
|
||||
"""``d_stab = min_{H ∈ H_id} ‖A − H‖_G`` (ADR-0246 §3.2–§3.3).
|
||||
|
||||
The lawfulness distance of an induced action ``A`` from the permitted identity
|
||||
actions. Zero iff ``A`` equals a permitted action. Under the locked singleton
|
||||
``H_id = { I }`` this is exactly ``‖A − I‖_G`` — a pass/fail distance, not a
|
||||
corrector.
|
||||
"""
|
||||
action = np.asarray(action, dtype=np.float64)
|
||||
gram = np.asarray(gram, dtype=np.float64)
|
||||
if not stabilizer.members:
|
||||
raise ValueError("stabilizer must contain at least one permitted action")
|
||||
return min(
|
||||
_g_weighted_frobenius(action - np.asarray(H, dtype=np.float64), gram)
|
||||
for H in stabilizer.members
|
||||
)
|
||||
|
||||
|
||||
def stabilizer_defect_for_versor(
|
||||
geometry: IdentityManifoldGeometry,
|
||||
versor: np.ndarray,
|
||||
stabilizer: IdentityStabilizer | None = None,
|
||||
) -> float:
|
||||
"""``d_stab`` for a versor's induced action against ``geometry``.
|
||||
|
||||
Defaults to the locked singleton ``H_id = { I }`` sized to the value frame.
|
||||
"""
|
||||
action = geometry.induced_action(versor)
|
||||
if stabilizer is None:
|
||||
stabilizer = IdentityStabilizer.singleton(action.shape[0])
|
||||
return stabilizer_defect(action, geometry.gram, stabilizer)
|
||||
|
|
@ -56,6 +56,15 @@ from algebra.cl41 import (
|
|||
# to resolve without mode-aliasing (ADR-0244 §2.1).
|
||||
CONDITION_BOUND: float = 1e5
|
||||
|
||||
# Grade-1 basis-vector slots in the 32-component Cl(4,1) layout. The spatial block
|
||||
# is e1/e2/e3 (indices 1/2/3) — the default value-axis support; e4/e5 (indices 4/5)
|
||||
# are the extra grade-1 directions a versor tilts (e4, null/conformal) or boosts
|
||||
# (e5) a value axis toward. Pinned against ``algebra.cl41.basis_vector`` in tests
|
||||
# (ADR-0246 §3.6 fixed blade map).
|
||||
SPATIAL_GRADE1_INDICES: tuple[int, int, int] = (1, 2, 3)
|
||||
E4_GRADE1_INDEX: int = 4
|
||||
E5_GRADE1_INDEX: int = 5
|
||||
|
||||
|
||||
class ManifoldConditioningError(ValueError):
|
||||
"""Raised when the value-axis Gram matrix is too ill-conditioned.
|
||||
|
|
@ -151,6 +160,18 @@ def euclidean_norm(s: np.ndarray) -> float:
|
|||
return float(np.linalg.norm(np.asarray(s, dtype=np.float64), ord=2))
|
||||
|
||||
|
||||
def orthogonality_defect_of_action(action: np.ndarray, gram: np.ndarray) -> float:
|
||||
"""``‖AᵀGA − G‖_F`` for a precomputed induced action ``A`` (ADR-0246 §3.2).
|
||||
|
||||
Zero iff ``A`` is a ``G``-isometry of the value subspace. The single home for
|
||||
the d_orth definition, shared by :meth:`IdentityManifoldGeometry.orthogonality_defect`
|
||||
and the off-serving diagnostics.
|
||||
"""
|
||||
action = np.asarray(action, dtype=np.float64)
|
||||
gram = np.asarray(gram, dtype=np.float64)
|
||||
return float(np.linalg.norm(action.T @ gram @ action - gram, ord="fro"))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IdentityManifoldGeometry:
|
||||
"""Frozen operator-preservation geometry for a set of value axes.
|
||||
|
|
@ -232,3 +253,94 @@ class IdentityManifoldGeometry:
|
|||
"""
|
||||
leakage, _ = self.axis_response(versor)
|
||||
return float((sum(value * value for value in leakage) / len(leakage)) ** 0.5)
|
||||
|
||||
# -- ADR-0246 §3 induced-action primitives (pure, off-serving) --------------
|
||||
|
||||
def induced_action(self, versor: np.ndarray) -> np.ndarray:
|
||||
"""The induced action matrix ``A(F)`` of a versor on the value frame.
|
||||
|
||||
``A_kj = (G⁻¹)_{km} ⟨a_m, F a_j F̃⟩₀`` (ADR-0246 §3.1). Column ``j`` is the
|
||||
image of axis ``j`` (``F a_j F̃``) re-expressed in the axis basis. This
|
||||
captures ALL in-subspace action — including the permutations and rotations
|
||||
that per-axis leakage is blind to (a rotor rotating e1→e2 has zero leakage
|
||||
but a non-identity ``A``). It is RAW (unnormalized): a boost that stretches
|
||||
an axis shows up as a column norm > 1 and hence in :meth:`orthogonality_defect`,
|
||||
deliberately not hidden by normalization.
|
||||
|
||||
When ``F`` preserves the subspace isometrically, ``A`` is ``G``-orthogonal
|
||||
(``AᵀGA = G``). Built only from existing primitives (Gram, signed inner
|
||||
product, sandwich); no new algebra.
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
n = len(self.axes_psi)
|
||||
overlaps = np.empty((n, n), dtype=np.float64)
|
||||
for j, axis_j in enumerate(self.axes_psi):
|
||||
image = sandwich(versor, axis_j)
|
||||
for k, axis_k in enumerate(self.axes_psi):
|
||||
overlaps[k, j] = _inner0(axis_k, image)
|
||||
return self.gram_inv @ overlaps
|
||||
|
||||
def orthogonality_defect(self, versor: np.ndarray) -> float:
|
||||
"""``d_orth(F) = ‖A(F)ᵀ G A(F) − G‖_F`` (ADR-0246 §3.2).
|
||||
|
||||
Zero iff the induced action is a ``G``-isometry of the value subspace.
|
||||
Non-zero flags numerical failure or genuinely non-isometric action (a
|
||||
boost stretches the frame). It detects a DIFFERENT failure than
|
||||
:func:`~core.physics.identity_action.stabilizer_defect` and must never be
|
||||
read as a semantic authorization policy — it is a conditioning / isometry
|
||||
check only.
|
||||
"""
|
||||
return orthogonality_defect_of_action(self.induced_action(versor), self.gram)
|
||||
|
||||
def typed_residual_energy(self, versor: np.ndarray) -> dict[str, float]:
|
||||
"""Typed decomposition of the out-of-subspace leakage (ADR-0246 §3.6).
|
||||
|
||||
For each axis the rejection ``r = F a F̃ − P_I(F a F̃)`` is split by which
|
||||
grade-1 direction it leaks into, summed over axes, and returned as
|
||||
fractions of the total rotated-axis energy:
|
||||
|
||||
* ``null_or_conformal`` — energy on e4 (index 4): conformal/null tilt.
|
||||
* ``boost_like`` — energy on e5 (index 5): noncompact/boost class.
|
||||
* ``spatial_foreign`` — energy on spatial grade-1 slots (e1/e2/e3) not
|
||||
in the value-axis span; structurally 0 for the default full-span pack.
|
||||
* ``unclassified`` — the remainder (higher-grade contamination after
|
||||
the sandwich, numerical junk). Fail-closed: no correction policy ever
|
||||
attaches to this channel. For a clean versor (grade-preserving sandwich)
|
||||
it is ~0.
|
||||
|
||||
Retains the positive-definite Euclidean coefficient norm (never the
|
||||
indefinite ``⟨·,·⟩₀``) so a boost/e5 component cannot silently vanish.
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
e4_energy = e5_energy = spatial_foreign = unclassified = total = 0.0
|
||||
for axis in self.axes_psi:
|
||||
rotated = sandwich(versor, axis)
|
||||
rejection = rotated - self.project(rotated)
|
||||
total += euclidean_norm(rotated) ** 2
|
||||
e4_energy += float(rejection[E4_GRADE1_INDEX] ** 2)
|
||||
e5_energy += float(rejection[E5_GRADE1_INDEX] ** 2)
|
||||
spatial_foreign += float(
|
||||
sum(rejection[i] ** 2 for i in SPATIAL_GRADE1_INDICES)
|
||||
)
|
||||
rejection_energy = euclidean_norm(rejection) ** 2
|
||||
unclassified += max(
|
||||
0.0,
|
||||
rejection_energy
|
||||
- float(rejection[E4_GRADE1_INDEX] ** 2)
|
||||
- float(rejection[E5_GRADE1_INDEX] ** 2)
|
||||
- float(sum(rejection[i] ** 2 for i in SPATIAL_GRADE1_INDICES)),
|
||||
)
|
||||
if total <= 0.0:
|
||||
# Versor annihilated every axis — fail-closed as fully unaccounted.
|
||||
return {
|
||||
"null_or_conformal": 0.0,
|
||||
"boost_like": 0.0,
|
||||
"spatial_foreign": 0.0,
|
||||
"unclassified": 1.0,
|
||||
}
|
||||
return {
|
||||
"null_or_conformal": e4_energy / total,
|
||||
"boost_like": e5_energy / total,
|
||||
"spatial_foreign": spatial_foreign / total,
|
||||
"unclassified": unclassified / total,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,33 @@ Do **not** insert Rings 1–3 into active D4 commits.
|
|||
|
||||
---
|
||||
|
||||
## 0a. Post-D4 execution sequencing (actual — 2026-07-17)
|
||||
|
||||
D4 closed and was ratified; this brief is now the live plan. The R&D assessment's
|
||||
one-shot "build the whole Ring-1 gate machinery" is being executed as small,
|
||||
verified units, and the brief's own **§11 semantic-grounding question was promoted
|
||||
to run as a consumer of the §3 apparatus** (not a re-sequenced blocker), because
|
||||
D4 Phase 3 + slice 0 proved no fixed spatial frame is dynamically stabilized —
|
||||
building a lawfulness gate on the declared frame before knowing whether *any*
|
||||
structure is stabilized would instrument lawfulness on a frame the dynamics ignore
|
||||
(the "instruments ≠ meaning" trap, §2 / §11).
|
||||
|
||||
| Unit | Scope | Status |
|
||||
|------|-------|--------|
|
||||
| **Slice 0** — mismatch diagnostic | evidence-only classification of the benign mismatch (foreign leakage vs in-span-unlawful vs numerical vs path vs semantic-coupling-absent) | **merged** `main` (quarantined diagnostic artifact); found: structural e4/e5 foreign leakage, declared frame dynamically unspecial |
|
||||
| **§3 primitives** (this unit) | pure `A(F)`, `d_orth`, `d_stab` vs locked `H_id={I}`, typed residual channels in `core/physics/identity_manifold.py` + `identity_action.py`; slice-0 eval rewired to consume them (single source of truth) | in progress — RED→GREEN, off-serving, flag untouched |
|
||||
| §3.4/§3.5 path ledger | lawful-only composition + hard breaks | next unit |
|
||||
| §3.7 gate admit surface | wire d_orth/d_stab/typed channels into `IdentityScore` (flag default-off) | after ledger |
|
||||
| §6 eval matrix + §11 feasibility | synthetic + path suites + discrimination report; the invariant/semantic-grounding feasibility study runs here as the **first consumer** of the §3 primitives (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) | after gate surface |
|
||||
| ADR-0246 body + acceptance packet | §10 criteria; no self-Accept | last |
|
||||
|
||||
Nothing in the reordering relaxes a §7 non-goal: no `C_id` corrector, no `H_id`
|
||||
enlargement, no pack/axis redesign, no gate activation. The §11 grounding study
|
||||
remains *feasibility only* until it produces a held-out-stable, safety-relevant
|
||||
candidate (per the D4 ratification's activation prerequisites).
|
||||
|
||||
---
|
||||
|
||||
## 1. Authority documents
|
||||
|
||||
| Doc | Role |
|
||||
|
|
|
|||
|
|
@ -50,9 +50,13 @@ from algebra.cl41 import N_COMPONENTS, grade_project
|
|||
from core.physics.identity_manifold import (
|
||||
IdentityManifoldGeometry,
|
||||
euclidean_norm,
|
||||
sandwich,
|
||||
orthogonality_defect_of_action,
|
||||
_inner0,
|
||||
)
|
||||
from core.physics.identity_action import (
|
||||
IdentityStabilizer,
|
||||
stabilizer_defect,
|
||||
)
|
||||
from evals.adr_0244_gamma_calibration import (
|
||||
LEAKAGE_ATTACKS,
|
||||
_boost,
|
||||
|
|
@ -90,93 +94,34 @@ def default_geometry() -> IdentityManifoldGeometry:
|
|||
return IdentityManifoldGeometry.from_directions(DEFAULT_DIRECTIONS)
|
||||
|
||||
|
||||
# --- brief §3.1: induced action matrix ----------------------------------------
|
||||
# --- ADR-0246 §3 primitives — thin wrappers over the canonical implementations -
|
||||
# The substantive definitions now live in ``core.physics.identity_manifold`` /
|
||||
# ``identity_action`` (promoted from this slice-0 prototype). These wrappers keep
|
||||
# the diagnostic's call sites and its merged test API stable while delegating to
|
||||
# the single source of truth.
|
||||
|
||||
|
||||
def induced_action(geometry: IdentityManifoldGeometry, versor: np.ndarray) -> np.ndarray:
|
||||
"""``A_ij(F) = (G⁻¹)_ik ⟨a_k, F a_j F̃⟩₀`` — the full in-subspace action.
|
||||
|
||||
Column ``j`` is the image of axis ``j`` expressed in the axis basis. Captures
|
||||
in-span permutations/rotations/inversions that per-axis leakage misses. Raw
|
||||
(unnormalized): a boost that stretches an axis shows up as a column norm > 1
|
||||
and hence in ``d_orth``, deliberately not hidden by normalization.
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
n = len(geometry.axes_psi)
|
||||
m = np.empty((n, n), dtype=np.float64)
|
||||
for j, axis_j in enumerate(geometry.axes_psi):
|
||||
image = sandwich(versor, axis_j)
|
||||
for k, axis_k in enumerate(geometry.axes_psi):
|
||||
m[k, j] = _inner0(axis_k, image)
|
||||
return geometry.gram_inv @ m
|
||||
"""Induced action ``A(F)`` (delegates to the geometry primitive)."""
|
||||
return geometry.induced_action(versor)
|
||||
|
||||
|
||||
def d_orth(geometry: IdentityManifoldGeometry, action: np.ndarray) -> float:
|
||||
"""``‖AᵀGA − G‖_F`` — 0 iff the induced action is a G-isometry of the span.
|
||||
|
||||
Detects numerical corruption and non-isometric (e.g. boost-stretched) action;
|
||||
must never be read as a semantic authorization policy (brief §3.2).
|
||||
"""
|
||||
G = geometry.gram
|
||||
return float(np.linalg.norm(action.T @ G @ action - G, ord="fro"))
|
||||
"""``‖AᵀGA − G‖_F`` for a precomputed action (delegates to canonical)."""
|
||||
return orthogonality_defect_of_action(action, geometry.gram)
|
||||
|
||||
|
||||
def d_stab(geometry: IdentityManifoldGeometry, action: np.ndarray) -> float:
|
||||
"""``min_{H∈H_id} ‖A − H‖_G`` under the LOCKED singleton ``H_id = {I}``.
|
||||
|
||||
For the default pack the axis Gram is exactly the identity matrix, so the
|
||||
G-weighted norm coincides with the Frobenius norm; pinning ‖·‖_G for general
|
||||
packs is ADR-0246-proper work, not this slice's.
|
||||
"""
|
||||
eye = np.eye(action.shape[0], dtype=np.float64)
|
||||
return float(np.linalg.norm(action - eye, ord="fro"))
|
||||
|
||||
|
||||
# --- brief §3.6: typed residual channels --------------------------------------
|
||||
"""``d_stab`` under the locked singleton ``H_id = {I}`` (delegates to canonical)."""
|
||||
stabilizer = IdentityStabilizer.singleton(action.shape[0])
|
||||
return stabilizer_defect(action, geometry.gram, stabilizer)
|
||||
|
||||
|
||||
def typed_residual_channels(
|
||||
geometry: IdentityManifoldGeometry, versor: np.ndarray
|
||||
) -> dict[str, float]:
|
||||
"""Energy split of the out-of-span rejection, summed over axes, as fractions
|
||||
of total rotated-axis energy.
|
||||
|
||||
Channels (pinned blade indices; default pack support = e1/e2/e3 so the
|
||||
spatial-foreign channel is structurally empty and reported as 0):
|
||||
|
||||
* ``null_or_conformal`` — e4 grade-1 residual energy (index 4)
|
||||
* ``boost_like`` — e5 grade-1 residual energy (index 5)
|
||||
* ``spatial_foreign`` — grade-1 spatial residual outside the axis
|
||||
support (empty for the default pack)
|
||||
* ``unclassified`` — everything else (higher-grade contamination
|
||||
after the sandwich, numerical junk); fail-closed,
|
||||
no correction policy ever attaches to it
|
||||
"""
|
||||
versor = np.asarray(versor, dtype=np.float64)
|
||||
e4_energy = e5_energy = unclassified = total = 0.0
|
||||
for axis in geometry.axes_psi:
|
||||
rotated = sandwich(versor, axis)
|
||||
rejection = rotated - geometry.project(rotated)
|
||||
total += euclidean_norm(rotated) ** 2
|
||||
e4_energy += float(rejection[IDX_E4] ** 2)
|
||||
e5_energy += float(rejection[IDX_E5] ** 2)
|
||||
accounted = rejection.copy()
|
||||
accounted[IDX_E4] = 0.0
|
||||
accounted[IDX_E5] = 0.0
|
||||
unclassified += euclidean_norm(accounted) ** 2
|
||||
if total <= 0.0:
|
||||
return {
|
||||
"null_or_conformal": 1.0,
|
||||
"boost_like": 0.0,
|
||||
"spatial_foreign": 0.0,
|
||||
"unclassified": 1.0,
|
||||
}
|
||||
return {
|
||||
"null_or_conformal": e4_energy / total,
|
||||
"boost_like": e5_energy / total,
|
||||
"spatial_foreign": 0.0,
|
||||
"unclassified": unclassified / total,
|
||||
}
|
||||
"""Typed residual channel split (delegates to the geometry primitive)."""
|
||||
return geometry.typed_residual_energy(versor)
|
||||
|
||||
|
||||
def versor_plane_occupancy(versor: np.ndarray) -> dict[str, float]:
|
||||
|
|
|
|||
167
tests/test_adr_0246_induced_action.py
Normal file
167
tests/test_adr_0246_induced_action.py
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
"""ADR-0246 Ring-1 §3 — pure induced-action / d_orth / d_stab / typed-residual pins.
|
||||
|
||||
These pin the canonical primitives promoted from the slice-0 diagnostic prototype
|
||||
into ``core.physics.identity_manifold`` (induced action A(F), orthogonality defect
|
||||
d_orth, typed residual energy) and the new ``core.physics.identity_action`` (the
|
||||
locked singleton stabilizer H_id={I} and the stabilizer defect d_stab).
|
||||
|
||||
Ground truth is the brief §3.1–§3.3/§3.6 and §6.1 constructions: identity versor →
|
||||
A=I; an in-span rotation is a G-isometry (d_orth≈0) but NOT the identity action
|
||||
(d_stab>0); an in-span permutation/inversion is leakage-invisible but d_stab-visible;
|
||||
an e4 tilt fires only the null/conformal channel; an e5 boost fires the boost channel
|
||||
and is non-isometric (d_orth>0). The primitives are pure (algebra-only, off-serving).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from algebra.cl41 import N_COMPONENTS, basis_vector
|
||||
from core.physics.identity_manifold import (
|
||||
IdentityManifoldGeometry,
|
||||
E4_GRADE1_INDEX,
|
||||
E5_GRADE1_INDEX,
|
||||
)
|
||||
from core.physics.identity_action import (
|
||||
IdentityStabilizer,
|
||||
stabilizer_defect,
|
||||
stabilizer_defect_for_versor,
|
||||
)
|
||||
|
||||
# grade-2 bivector plane indices (grade-2 block starts at 6)
|
||||
_E12, _E13, _E14, _E15, _E23, _E24, _E25 = 6, 7, 8, 9, 10, 11, 12
|
||||
|
||||
|
||||
def _rotor(biv: int, theta: float) -> np.ndarray:
|
||||
r = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||
r[0] = np.cos(theta / 2.0)
|
||||
r[biv] = np.sin(theta / 2.0)
|
||||
return r
|
||||
|
||||
|
||||
def _boost(biv: int, theta: float) -> np.ndarray:
|
||||
r = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||
r[0] = np.cosh(theta / 2.0)
|
||||
r[biv] = np.sinh(theta / 2.0)
|
||||
return r
|
||||
|
||||
|
||||
def _identity_versor() -> np.ndarray:
|
||||
v = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||
v[0] = 1.0
|
||||
return v
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def geometry() -> IdentityManifoldGeometry:
|
||||
# default pack: span(e1,e2,e3), Gram = I3
|
||||
return IdentityManifoldGeometry.from_directions(
|
||||
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
|
||||
)
|
||||
|
||||
|
||||
def test_blade_index_constants_match_algebra():
|
||||
assert basis_vector(3)[E4_GRADE1_INDEX] == 1.0 # e4
|
||||
assert basis_vector(4)[E5_GRADE1_INDEX] == 1.0 # e5
|
||||
assert np.count_nonzero(basis_vector(3)) == 1
|
||||
assert np.count_nonzero(basis_vector(4)) == 1
|
||||
|
||||
|
||||
def test_identity_versor_action_is_identity(geometry):
|
||||
action = geometry.induced_action(_identity_versor())
|
||||
assert np.allclose(action, np.eye(3), atol=1e-12)
|
||||
assert geometry.orthogonality_defect(_identity_versor()) < 1e-12
|
||||
assert stabilizer_defect_for_versor(geometry, _identity_versor()) < 1e-12
|
||||
|
||||
|
||||
def test_inplane_rotation_is_isometry_but_not_identity_action(geometry):
|
||||
theta = 0.4
|
||||
versor = _rotor(_E12, theta)
|
||||
action = geometry.induced_action(versor)
|
||||
# e12 rotor rotates the e1/e2 plane; e3 fixed.
|
||||
assert action[2, 2] == pytest.approx(1.0, abs=1e-9)
|
||||
assert abs(action[0, 0]) == pytest.approx(abs(np.cos(theta)), abs=1e-6)
|
||||
assert geometry.orthogonality_defect(versor) < 1e-6 # G-isometry
|
||||
assert stabilizer_defect_for_versor(geometry, versor) > 0.05 # not H_id={I}
|
||||
|
||||
|
||||
def test_permutation_and_inversion_are_leakage_invisible_but_dstab_visible(geometry):
|
||||
for versor in (_rotor(_E12, np.pi / 2.0), _rotor(_E12, np.pi)):
|
||||
leak, _ = geometry.axis_response(versor)
|
||||
assert max(leak) < 1e-6
|
||||
assert stabilizer_defect_for_versor(geometry, versor) > 0.05
|
||||
|
||||
|
||||
def test_e4_tilt_fires_only_null_conformal_channel(geometry):
|
||||
channels = geometry.typed_residual_energy(_rotor(_E14, 1.2))
|
||||
assert channels["null_or_conformal"] > 0.05
|
||||
assert channels["boost_like"] == pytest.approx(0.0, abs=1e-12)
|
||||
assert channels["unclassified"] < 1e-9
|
||||
|
||||
|
||||
def test_e5_boost_fires_boost_channel_and_is_non_isometric(geometry):
|
||||
versor = _boost(_E15, 1.0)
|
||||
channels = geometry.typed_residual_energy(versor)
|
||||
assert channels["boost_like"] > 0.05
|
||||
assert channels["null_or_conformal"] == pytest.approx(0.0, abs=1e-12)
|
||||
assert geometry.orthogonality_defect(versor) > 0.05 # boost not a G-isometry
|
||||
|
||||
|
||||
def test_typed_residual_energy_fractions_are_bounded_and_clean(geometry):
|
||||
for versor in (_rotor(_E14, 0.7), _boost(_E25, 0.6), _rotor(_E12, 0.3)):
|
||||
ch = geometry.typed_residual_energy(versor)
|
||||
total = (
|
||||
ch["null_or_conformal"]
|
||||
+ ch["boost_like"]
|
||||
+ ch["spatial_foreign"]
|
||||
+ ch["unclassified"]
|
||||
)
|
||||
assert 0.0 <= total <= 1.0 + 1e-9
|
||||
# sandwich output of a versor stays grade-1: no unclassified contamination
|
||||
assert ch["unclassified"] < 1e-9
|
||||
|
||||
|
||||
def test_stabilizer_is_singleton_identity_by_default(geometry):
|
||||
stab = IdentityStabilizer.singleton(3)
|
||||
assert len(stab.members) == 1
|
||||
assert np.allclose(stab.members[0], np.eye(3))
|
||||
# d_stab of the identity action is 0; of a rotation, > 0
|
||||
eye = np.eye(3)
|
||||
assert stabilizer_defect(eye, geometry.gram, stab) < 1e-12
|
||||
rot = geometry.induced_action(_rotor(_E12, 0.5))
|
||||
assert stabilizer_defect(rot, geometry.gram, stab) > 0.05
|
||||
|
||||
|
||||
def test_stabilizer_defect_g_weighted_reduces_to_frobenius_at_identity_gram(geometry):
|
||||
action = geometry.induced_action(_rotor(_E13, 0.35))
|
||||
stab = IdentityStabilizer.singleton(3)
|
||||
d = stabilizer_defect(action, geometry.gram, stab)
|
||||
frob = float(np.linalg.norm(action - np.eye(3), ord="fro"))
|
||||
assert d == pytest.approx(frob, abs=1e-9) # default pack Gram is I3
|
||||
|
||||
|
||||
def test_induced_action_is_deterministic(geometry):
|
||||
versor = _boost(_E15, 0.9)
|
||||
a1 = geometry.induced_action(versor)
|
||||
a2 = geometry.induced_action(versor.copy())
|
||||
assert np.array_equal(a1, a2)
|
||||
|
||||
|
||||
def test_primitives_are_pure_offserving():
|
||||
import core.physics.identity_manifold as m
|
||||
import core.physics.identity_action as a
|
||||
|
||||
for mod in (m, a):
|
||||
with open(mod.__file__, encoding="utf-8") as fh:
|
||||
src = fh.read()
|
||||
assert "chat.runtime" not in src
|
||||
assert "import chat" not in src
|
||||
|
||||
|
||||
def test_gate_surface_untouched_by_this_branch():
|
||||
from core.config import RuntimeConfig
|
||||
from core.physics import identity
|
||||
|
||||
assert RuntimeConfig().identity_wave_gate is False
|
||||
assert identity._WAVE_LEAKAGE_BOUND == 0.2126624458513829
|
||||
Loading…
Reference in a new issue