feat(third-door): Slice-3 wave ledger close + multi-pair conjugacy + resonant recall
Finalize ADR-0241 branch contract: multi-pair Procrustes thin-wraps wave_field_conjugacy; standing-wave registry + resonant_recall; biography registers trajectory modes; ledger W1–W6 green with explicit deferred items (durable holographic vault, #18 bootstrap/prune, Rust/MLX). Hygiene pin for absent core_ha package.
This commit is contained in:
parent
541257f81d
commit
932b5d481f
7 changed files with 192 additions and 59 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 ------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue