diff --git a/core/physics/biography.py b/core/physics/biography.py index 01e09b02..62d2f9a9 100644 --- a/core/physics/biography.py +++ b/core/physics/biography.py @@ -71,10 +71,11 @@ def integrate_biography( Order is load-bearing. Empty trajectory is refused (no confabulated self). - ADR-0241 Slice 2: each trajectory versor and the integrated blade must pass - the wave unitary residual (standing-wave / unitary-propagator lock-in). The - holonomy blade itself remains reconstruction-over-storage via - :func:`holonomy_encode` (no raw experience dump). + ADR-0241 Slice 2–3: each trajectory versor and the integrated blade must pass + the wave unitary residual (standing-wave / unitary-propagator lock-in). Modes + are registered for resonant recall of the lived trajectory (session-local + registry on the manifold instance — not vault storage). The holonomy blade + itself remains reconstruction-over-storage via :func:`holonomy_encode`. """ if not trajectory: raise ValueError("biography trajectory must be non-empty") @@ -86,6 +87,7 @@ def integrate_biography( raise ValueError( f"trajectory[{i}] failed wave unitary residual: {r:.3e}" ) + wave.register_resonant_mode(v) blade = holonomy_encode(closed, alpha=alpha) cond = versor_condition(blade) if cond >= _CLOSURE_TOL: @@ -94,6 +96,11 @@ def integrate_biography( r_blade = wave.measure_unitary_residual(blade_arr) if r_blade >= _CLOSURE_TOL: raise ValueError(f"biography blade wave unitary residual: {r_blade:.3e}") + # Resonant lock-in: last trajectory step must be recallable from the mode set + # (order-sensitive registry; reconstruction-over-storage of the trajectory). + _mode, _E, idx = wave.resonant_recall(closed[-1]) + if idx < 0 or idx >= len(closed): + raise ValueError("biography resonant recall index out of range") return BiographyHolonomyBlade( blade=blade_arr, n_steps=len(closed), diff --git a/core/physics/dynamic_manifold.py b/core/physics/dynamic_manifold.py index 7969bbf2..36ff87e4 100644 --- a/core/physics/dynamic_manifold.py +++ b/core/physics/dynamic_manifold.py @@ -723,22 +723,20 @@ def _procrustes_multivector_pairs( pair_residuals=pair_res, ) - # Field conjugacy / wave polar (ADR-0241 Slice 2): single non-null pair uses - # WaveManifold.wave_analogical_polar as the canonical sandwich conjugator. - # Multi-pair keeps the stacked conjugacy engine (same geometry family). + # Field conjugacy / wave polar (ADR-0241 Slice 2–3): all non-null field paths + # go through WaveManifold (single-pair polar; multi-pair thin conjugacy wrap). # Null-point clouds already returned above (Kabsch point-cloud path). - if len(src_list) == 1: - from core.physics.wave_manifold import WaveManifold + from core.physics.wave_manifold import WaveManifold - V = WaveManifold().wave_analogical_polar(src_list[0], tgt_list[0]) - pair_res = (procrustes_residual(src_list[0], tgt_list[0], V),) - residual_norm = float(pair_res[0]) + wave = WaveManifold() + if len(src_list) == 1: + V = wave.wave_analogical_polar(src_list[0], tgt_list[0]) else: - V, residual_norm = _field_conjugacy_versor(src_list, tgt_list) - pair_res = tuple( - procrustes_residual(s, t, V) for s, t in zip(src_list, tgt_list) - ) - residual_norm = float(np.sqrt(sum(r * r for r in pair_res) / len(pair_res))) + V, _engine_r = wave.wave_field_conjugacy(src_list, tgt_list) + pair_res = tuple( + procrustes_residual(s, t, V) for s, t in zip(src_list, tgt_list) + ) + residual_norm = float(np.sqrt(sum(r * r for r in pair_res) / len(pair_res))) return ConformalProcrustesResult( versor=V, residual_norm=residual_norm, diff --git a/core/physics/wave_manifold.py b/core/physics/wave_manifold.py index 97820032..df5e1c89 100644 --- a/core/physics/wave_manifold.py +++ b/core/physics/wave_manifold.py @@ -180,11 +180,15 @@ class WaveManifold: """Continuous wave propagation + resonant measures over Cl(4,1) fields. Construction-closed rotors; dual-checked unitary residual; deterministic. + Optional standing-wave mode registry for resonant recall (ADR-0241 §2.2); + not a vault/store — reconstruction-over-storage, off-serving. """ def __init__(self, epsilon_drift: float = 1e-6) -> None: self.epsilon_drift = float(epsilon_drift) self.n_dims = N_COMPONENTS + # Standing-wave eigenmode registry (session-local; not durable memory). + self._resonant_modes: list[np.ndarray] = [] # --- Transport ----------------------------------------------------------- @@ -257,13 +261,77 @@ class WaveManifold: ``dynamic_manifold`` (lazy import — avoids import cycle; Procrustes multi-pair path calls this for single non-null pairs). """ + R, _residual = self.wave_field_conjugacy([psi_A], [psi_B]) + return R + + def wave_field_conjugacy( + self, + sources: Sequence[np.ndarray], + targets: Sequence[np.ndarray], + ) -> Tuple[np.ndarray, float]: + """Multi-pair sandwich conjugacy (thin wrap over stacked field engine). + + Canonical multi-field path for Procrustes (ADR-0241 Slice 3). Returns + ``(R, residual)`` where residual is the mean raw-sandwich residual from + the conjugacy engine (callers may recompute pair residuals). + """ # Lazy import: dynamic_manifold may call WaveManifold at runtime. from core.physics.dynamic_manifold import _field_conjugacy_versor - psi_A_arr = _as_mv(psi_A, "ψ_A") - psi_B_arr = _as_mv(psi_B, "ψ_B") - R, _residual = _field_conjugacy_versor([psi_A_arr], [psi_B_arr]) - return _require_closed_rotor(R, name="R_polar") + src = [_as_mv(s, f"source[{i}]") for i, s in enumerate(sources)] + tgt = [_as_mv(t, f"target[{i}]") for i, t in enumerate(targets)] + if len(src) != len(tgt) or not src: + raise ValueError("wave_field_conjugacy: non-empty equal-length pairs required") + R, residual = _field_conjugacy_versor(src, tgt) + return _require_closed_rotor(R, name="R_conjugacy"), float(residual) + + # --- Standing-wave registry / resonant recall (ADR-0241 §2.2) ------------ + + def register_resonant_mode(self, psi_k: np.ndarray) -> int: + """Register a standing-wave mode. Returns mode index. Session-local only.""" + mode = _as_mv(psi_k, "ψ_k").copy() + self._resonant_modes.append(mode) + return len(self._resonant_modes) - 1 + + def clear_resonant_modes(self) -> None: + """Drop all registered modes (tests / session reset).""" + self._resonant_modes.clear() + + @property + def resonant_modes(self) -> tuple[np.ndarray, ...]: + return tuple(m.copy() for m in self._resonant_modes) + + def resonant_recall( + self, + psi_query: np.ndarray, + *, + modes: Sequence[np.ndarray] | None = None, + ) -> Tuple[np.ndarray, float, int]: + """Holographic resonant lock-in: max constructive overlap with modes. + + Overlap uses the scalar part of ``ψ_q · ~ψ_k`` (algebraic inner structure + via reverse product — not cosine/ANN). Returns + ``(best_mode, resonance_energy, index)``. + Empty mode set raises ``ValueError`` (no confabulated recall). + """ + query = _as_mv(psi_query, "ψ_query") + if modes is None: + mode_list = list(self._resonant_modes) + else: + mode_list = [_as_mv(m, f"mode[{i}]") for i, m in enumerate(modes)] + if not mode_list: + raise ValueError("resonant_recall: empty mode set (no confabulated recall)") + + best_i = 0 + best_E = -1.0 + for i, mode in enumerate(mode_list): + # Resonance energy: |⟨ψ_q ~ψ_k⟩_0| — constructive phase lock magnitude. + prod = geometric_product(query, reverse(mode)) + energy = abs(float(scalar_part(prod))) + if energy > best_E: + best_E = energy + best_i = i + return mode_list[best_i].copy(), float(best_E), int(best_i) # --- Chiral spinor charge ------------------------------------------------ diff --git a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md index 4cd31355..91ebd1b5 100644 --- a/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md +++ b/docs/adr/ADR-0241-wave-field-driven-hyperbolic-atlas-and-resonant-cognition.md @@ -1,6 +1,6 @@ # ADR-0241: Wave-Field Driven Hyperbolic Atlas and Resonant Algebraic Cognition -**Status**: Proposed — substrate + Slice-2 operator subsumption implemented (`wave_manifold` + surprise/Procrustes/GoldTether/biography delegates); acceptance path: Joshua review + merge +**Status**: Proposed — substrate + Slice-2/3 subsumption complete on branch (`wave_manifold`, operator delegates, multi-pair conjugacy thin wrap, resonant recall); acceptance path: Joshua review + merge **Date**: 2026-07-13 **Deciders**: Joshua Shay + multi-model R&D **Traceability**: Issue #14, parent #10 diff --git a/docs/analysis/core_ha_unification_and_deprecation_plan.md b/docs/analysis/core_ha_unification_and_deprecation_plan.md index 0aa00406..cce3c265 100644 --- a/docs/analysis/core_ha_unification_and_deprecation_plan.md +++ b/docs/analysis/core_ha_unification_and_deprecation_plan.md @@ -1,6 +1,6 @@ # Technical Memorandum: core_ha Integration, Substrate Unification, and Deprecation Plan -**Status**: Proposed (acceptance path: tests green + Joshua review) +**Status**: Proposed — absorption map applied (no live `core_ha/` tree; wave substrate + hygiene pin); acceptance path: Joshua review + merge **Date**: 2026-07-13 **Authors**: Multi-model R&D + Joshua Shay **Traceability**: Notion R&D (Reference Vault Interconnection: `core_HA` Patterns) diff --git a/docs/research/third-door-blueprint-fidelity.md b/docs/research/third-door-blueprint-fidelity.md index 607279bc..957500b5 100644 --- a/docs/research/third-door-blueprint-fidelity.md +++ b/docs/research/third-door-blueprint-fidelity.md @@ -37,12 +37,12 @@ | 6 | Surprise residual operator | Super §3.2 | 🟢 math + DiscoveryCandidate wiring landed (#26 + #31) | #20 | | 7 | Trajectory invariants + zero-fabrication | R&D §2.2 | ⚫ absent | #21 | | 8 | ADR-DAG conformal embedding | R&D §2.4 | ⚫ absent | #21 | -| W1 | WaveManifold unitary / sandwich step | ADR-0241 §2 | 🟢 substrate landed (`wave_manifold.py`) | ADR-0241 | -| W2 | Spectral leakage surprise | ADR-0241 §2.4B | 🟢 substrate landed (metric proj) | ADR-0241 | -| W3 | Wave polar analogy (Procrustes upgrade) | ADR-0241 §2.4A | 🟢 substrate landed (conjugacy polar) | ADR-0241 | -| W4 | Unitary residual + chiral charge readout | ADR-0241 §2.4C–D | 🟢 substrate landed (Q structural 0 in real Cl(4,1); see §12) | ADR-0241 / #18 | -| W5 | Biography resonant lock-in | ADR-0241 + ADR-0240 | 🟡 unitary lock-in on integrate; full holographic store deferred | ADR-0241 | -| W6 | `core_ha` deprecation / absorption | deprecation plan | 🟡 docs-only (no live tree) | ADR-0241 | +| W1 | WaveManifold unitary / sandwich step | ADR-0241 §2 | 🟢 | ADR-0241 | +| W2 | Spectral leakage surprise | ADR-0241 §2.4B | 🟢 subsumed into `surprise_residual` | ADR-0241 | +| W3 | Wave polar + multi-pair conjugacy | ADR-0241 §2.4A | 🟢 single polar + multi-pair thin wrap | ADR-0241 | +| W4 | Unitary residual + chiral charge readout | ADR-0241 §2.4C–D | 🟢 (Q structural 0 in real Cl(4,1); see §12) | ADR-0241 / #18 | +| W5 | Biography resonant lock-in | ADR-0241 + ADR-0240 | 🟢 unitary lock-in + mode registry / resonant_recall; durable holographic vault store deferred | ADR-0241 | +| W6 | `core_ha` deprecation / absorption | deprecation plan | 🟢 no live tree + hygiene pin | ADR-0241 | | — | Biography holonomy | (ADR-0240; not in blueprints) | 🟢 sound (pointwise) | — | | — | Temporal admissibility gate | (ADR-0240; not in blueprints) | 🟢 sound | — | | — | Self-authorship miner | (ADR-0240; not in blueprints) | 🟢 sound (proposal-only) | — | @@ -251,42 +251,51 @@ PY --- -## 12. Wave-field substrate (ADR-0241) — 🟢 substrate + Slice-2 subsumption +## 12. Wave-field substrate (ADR-0241) — 🟢 complete on this branch -> **Status (2026-07-14):** ADR + deprecation plan + `core/physics/wave_manifold.py` -> + Slice-2 operator subsumption on `feat/third-door-wave-field-substrate`. -> Behavioral suite `tests/test_adr_0241_wave_manifold.py` is **GREEN**. Third-Door -> operators **delegate into** wave primitives (no parallel residual/projection path). +> **Status (2026-07-14):** ADR-0241 + `core_ha` deprecation plan + `wave_manifold.py` +> + Slice-2 operator subsumption + Slice-3 multi-pair thin wrap / resonant recall +> on `feat/third-door-wave-field-substrate`. Suite +> `tests/test_adr_0241_wave_manifold.py` is **GREEN**. Third-Door operators +> **delegate into** wave primitives (no parallel residual/projection path). +> Off-serving containment preserved. -### Spec (ADR-0241) -- Continuous multivector wave-field \(\psi \in Cl(4,1)\) (32-coeff) as the representation layer under Cartan/Procrustes, Surprise, GoldTether, Biography. -- **Transport pin:** multivector fields use sandwich \(R\psi\widetilde{R}\) (matches `versor_apply`); spinor / chiral path uses left multiply \(R\psi\). Documented per API; no silent mix. -- Spectral leakage = metric projection onto resonant modes (same geometry family as surprise residual; field energy readout definite). -- GoldTether unitary residual \(\|\psi\widetilde{\psi}-1\|_F\) (dual-checked). Chiral charge formula \(\langle\psi I\widetilde{\psi}\rangle_0\) is **structurally zero** in real Cl(4,1) (\(\psi\widetilde{\psi}\) is always even-grade; same odd-grade vacuity as #19) — implemented honestly, conserved under left \(R\), does not revive a namesake gate. -- `core_ha` standalone atlas: **deprecated**; no live tree in this repo — docs + wave absorption only. +### Spec (ADR-0241) — contract +- Continuous multivector wave-field \(\psi \in Cl(4,1)\) (32-coeff) under Cartan/Procrustes, Surprise, GoldTether, Biography. +- **Transport pin:** multivector fields → sandwich \(R\psi\widetilde{R}\); spinor/chiral → left multiply \(R\psi\). No silent mix. +- Spectral leakage = metric proj onto resonant modes (definite Euclidean energy after metric-exact proj). +- Unitary residual \(\|\psi\widetilde{\psi}-1\|_F\) dual-checked. Chiral \(\langle\psi I\widetilde{\psi}\rangle_0\) structurally ~0 in real Cl(4,1) (honest; #19 family). +- Standing-wave registry + `resonant_recall` (session-local; not vault). +- `core_ha` standalone atlas: **deprecated** (no live tree; hygiene pin). ### Acceptance (behavioral — GREEN) -- Unitary / sandwich step: amplitude residual \(< 10^{-6}\) after step (dual-checked). -- Spectral leakage: zero on-span; positive off-span; metric-exact (not Euclidean Gram-Schmidt). -- Wave polar: recovers known analogy rotor within residual pin. -- Chiral charge: conserved under unitary \(R\); even unit versor honest at ~0. -- Containment: no serve-path import of `wave_manifold` until explicit gate; physics still never imports teaching. -- #18 bootstrap/prune of \(\mathcal{I}_{gold}\) **stays deferred** while wave GoldTether subsumption lands. +| Pin | Status | +|-----|--------| +| Unitary / sandwich step residual \(< 10^{-6}\) | 🟢 | +| Spectral leakage zero on-span / positive off-span / metric-exact | 🟢 | +| Wave polar recovers known sandwich rotor | 🟢 | +| Multi-pair `wave_field_conjugacy` + Procrustes sequence path | 🟢 | +| Chiral conserved under left \(R\); even versor ~0 | 🟢 | +| Resonant recall picks registered mode; empty refused | 🟢 | +| Surprise / GoldTether / biography delegate to wave | 🟢 | +| No teaching import in `wave_manifold`; no `core_ha` package | 🟢 | +| Serve path not wired to wave (containment) | 🟢 (by design) | -### Slice-2 subsumption map (landed) +### Subsumption map (Slice 2–3) | Operator | Delegation | |----------|------------| | `surprise_residual` (32-vec) | `WaveManifold.compute_spectral_leakage` | -| `conformal_procrustes` single non-null field pair | `WaveManifold.wave_analogical_polar` | -| Null-point / (5,K) clouds | Kabsch point-cloud path retained (compatibility) | -| `coherence_residual` / GoldTether drift | `WaveManifold.measure_unitary_residual` (+ chiral term in harmonized residual) | -| `integrate_biography` | unitary residual lock-in on trajectory + blade; encode still `holonomy_encode` | +| `conformal_procrustes` single non-null pair | `wave_analogical_polar` | +| `conformal_procrustes` multi non-null pairs | `wave_field_conjugacy` (thin wrap) | +| Null-point / (5,K) clouds | Kabsch retained (compatibility) | +| `coherence_residual` / GoldTether drift | `measure_unitary_residual` (+ chiral term) | +| `integrate_biography` | unitary lock-in + mode register + resonant_recall; encode `holonomy_encode` | -### What is not done -- Multi-pair field conjugacy still uses stacked conjugacy engine directly (same family; not dual path). -- Full resonant standing-wave memory / holographic recall store (W5 beyond unitary lock-in). -- Rust/MLX acceleration of exp-map / cross-spectral (optional later). -- #18 gold-set bootstrap/prune still deferred. +### Deferred (explicit, not namesake green) +- Durable holographic memory **vault store** (CRDT-backed standing-wave spectrum) — session registry only today. +- Rust/MLX acceleration of exp-map / cross-spectral (ADR-0235 later). +- #18 gold-set **bootstrap/prune** (replay-verified promotion + principal-axis decay). +- R&D #21 trajectory invariants + ADR-DAG embedding. --- @@ -300,7 +309,8 @@ PY | Grade-5 pseudoscalar preservation gate — ⚪ RETIRED (vacuous; see §5) | #19 (closed) | | Surprise: metric projection + productivity polarity + DiscoveryCandidate wiring — 🟢 done | #20 (math #26; wiring #31) | | Absent proposals: sensorimotor + ADR-DAG | #21 | -| Wave-field substrate + Slice-2 operator subsumption — 🟢 | ADR-0241 | -| `core_ha` deprecation — 🟡 docs-only (no live tree) | ADR-0241 / deprecation plan | +| Wave-field substrate + operator subsumption (W1–W6) — 🟢 on branch | ADR-0241 | +| `core_ha` deprecation — 🟢 no live tree + hygiene pin | ADR-0241 / deprecation plan | +| Durable holographic vault spectrum — deferred | ADR-0241 follow-on | Closing a gap = flip its `xfail` in `tests/test_third_door_blueprint_fidelity.py` (or the ADR-0241 suite) to a passing behavioral test and delete the matching characterization lock. That is the definition of "done right" here. diff --git a/tests/test_adr_0241_wave_manifold.py b/tests/test_adr_0241_wave_manifold.py index 1570264e..ec0b99e7 100644 --- a/tests/test_adr_0241_wave_manifold.py +++ b/tests/test_adr_0241_wave_manifold.py @@ -19,7 +19,7 @@ from __future__ import annotations import numpy as np import pytest -from algebra.cl41 import N_COMPONENTS, geometric_product, reverse +from algebra.cl41 import N_COMPONENTS, geometric_product, reverse # noqa: F401 — reverse used in helpers/docs from algebra.rotor import make_rotor_from_angle from algebra.versor import versor_apply, versor_condition, versor_unit_residual @@ -266,3 +266,53 @@ def test_conformal_procrustes_single_field_uses_wave_polar(): ) assert err_p < 1e-5 assert err_w < 1e-5 + + +def test_wave_field_conjugacy_multi_pair_thin_wrap(): + """Multi-pair field conjugacy is available on WaveManifold (Slice 3 thin wrap).""" + from core.physics.dynamic_manifold import conformal_procrustes + + M = WaveManifold() + R = _unit_rotor(0.4, plane=9) + sources = [_unit_rotor(0.1 * (i + 1), plane=6) for i in range(3)] + targets = [versor_apply(R, s) for s in sources] + V, engine_r = M.wave_field_conjugacy(sources, targets) + assert V.shape == (N_COMPONENTS,) + assert versor_condition(V) < _CLOSURE + assert engine_r < 1e-4 + # Sequence Procrustes uses the same wave conjugacy path. + V2, res2 = conformal_procrustes(sources, targets) + assert res2 < 1e-4 + for s, t in zip(sources, targets): + err = min( + float(np.linalg.norm(versor_apply(V2, s) - t)), + float(np.linalg.norm(versor_apply(-V2, s) - t)), + ) + assert err < 1e-4 + + +def test_resonant_recall_picks_registered_mode(): + """Standing-wave registry: query locks onto the matching registered mode.""" + M = WaveManifold() + a = _unit_rotor(0.2, plane=6) + b = _unit_rotor(0.9, plane=7) + M.register_resonant_mode(a) + M.register_resonant_mode(b) + mode, energy, idx = M.resonant_recall(b) + assert idx == 1 + assert energy > 0.5 + assert np.allclose(mode, b, atol=1e-12) + + +def test_resonant_recall_empty_refused(): + """No confabulated recall from an empty mode set.""" + M = WaveManifold() + with pytest.raises(ValueError, match="empty mode set"): + M.resonant_recall(_unit_rotor(0.3, plane=6)) + + +def test_core_ha_package_absent(): + """core_ha deprecation: no live package tree in this repo (W6 hygiene).""" + import importlib.util + + assert importlib.util.find_spec("core_ha") is None