feat(adr-0244): Phase 5b — §2.9 standing-wave recall is order-independent (proven)
The §2.9 low-discrepancy mode allocator concern is satisfied by construction, so the deliverable is a proof, not a bolt-on wire. tests/test_adr_0244_mode_order_ independence.py (3 tests) pins: - Recall (WaveManifold.compute_spectral_leakage) is a Gram least-squares projection onto span(modes); projection onto a span is invariant to the order of the spanning set. Verified: energy + residual identical across mode insertion permutations to ~1e-15 (machine epsilon; nonzero-residual probe too). - atlas_packing.golden_angle_pack places mode k at a deterministic golden-angle coordinate from ordinal k alone (ALLOCATOR_VERSION reconstructible) — layout is order-independent by construction; a prefix of a larger pack is the same modes. Wiring the allocator into the durable content-seal path would be a category mismatch (that path seals content versors at their own ψ, not allocator positions). Test-only; no production change. Verified: 3 new tests pass. Full smoke + fast lane at Phase 5 close.
This commit is contained in:
parent
5c69b74138
commit
4a550c1d03
1 changed files with 87 additions and 0 deletions
87
tests/test_adr_0244_mode_order_independence.py
Normal file
87
tests/test_adr_0244_mode_order_independence.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"""ADR-0244 §2.9 — standing-wave-mode registration is insertion-order-independent.
|
||||
|
||||
The §2.9 concern is a low-discrepancy *mode-centroid allocator* so the standing-
|
||||
wave spectrum does not depend on the order modes were registered. Two facts make
|
||||
that hold **by construction**, so the honest deliverable is to *prove* it rather
|
||||
than bolt an allocator onto a path that does not need one:
|
||||
|
||||
1. **Recall is a metric-exact subspace projection.** `WaveManifold` recall
|
||||
(`compute_spectral_leakage`) projects the incoming ψ onto `span(modes)` via a
|
||||
Gram least-squares solve (`_metric_project`). Projection onto a subspace span
|
||||
is mathematically invariant to the order of the spanning set — permuting the
|
||||
modes permutes G's rows/columns identically and leaves the projection (and
|
||||
thus the residual / surprise) unchanged. Verified here to machine epsilon.
|
||||
2. **The allocator is reconstructible from ordinals.** `atlas_packing.golden_
|
||||
angle_pack(n, α)` places mode k at a deterministic golden-angle coordinate
|
||||
derived from k alone (`ALLOCATOR_VERSION` + ordinal), so its layout is
|
||||
order-independent by construction — there is no opaque mutable coordinate
|
||||
table to drift.
|
||||
|
||||
Wiring the allocator into the durable content-seal path (`holographic_vault`)
|
||||
would be a category mismatch: that path seals *content* versors at their own ψ,
|
||||
not allocator positions. §2.9 is satisfied by (1)+(2), pinned below.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from core.physics.atlas_packing import (
|
||||
allocator_layout_descriptor,
|
||||
golden_angle_pack,
|
||||
)
|
||||
from core.physics.wave_manifold import WaveManifold
|
||||
|
||||
_TOL = 1e-12 # observed order-to-order drift is ~1e-15 (float64 round-off)
|
||||
|
||||
|
||||
def _recall(perm, modes, probe):
|
||||
manifold = WaveManifold()
|
||||
for i in perm:
|
||||
manifold.register_resonant_mode(modes[i])
|
||||
residual, energy = manifold.compute_spectral_leakage(probe, manifold.resonant_modes)
|
||||
return energy, residual
|
||||
|
||||
|
||||
def test_allocator_is_deterministic_and_ordinal_reconstructible():
|
||||
a = golden_angle_pack(6, 0.6)
|
||||
b = golden_angle_pack(6, 0.6)
|
||||
assert all(np.array_equal(x, y) for x, y in zip(a, b))
|
||||
# a prefix of a larger pack is the same modes (layout depends on ordinal only)
|
||||
bigger = golden_angle_pack(9, 0.6)
|
||||
assert all(np.array_equal(x, y) for x, y in zip(a, bigger[:6]))
|
||||
# descriptor is content-free reconstruction metadata (no coordinate leak)
|
||||
desc = allocator_layout_descriptor(6, 0.6)
|
||||
assert desc["allocator_version"] == "golden_angle_v1"
|
||||
assert "alpha" in desc and "min_d" in desc
|
||||
|
||||
|
||||
def test_recall_projection_is_insertion_order_independent():
|
||||
modes = golden_angle_pack(5, 0.6)
|
||||
# probe fully inside span(modes) → residual ≈ 0, still order-invariant
|
||||
in_span = np.asarray(
|
||||
modes[0] * 0.4 + modes[2] * 0.5 + modes[4] * 0.3, dtype=np.float64
|
||||
)
|
||||
base_e, base_r = _recall(range(5), modes, in_span)
|
||||
perms = list(itertools.islice(itertools.permutations(range(5)), 0, 120, 13))
|
||||
for perm in perms:
|
||||
e, r = _recall(list(perm), modes, in_span)
|
||||
assert abs(e - base_e) < _TOL
|
||||
assert float(np.max(np.abs(r - base_r))) < _TOL
|
||||
|
||||
|
||||
def test_order_independence_holds_for_nontrivial_residual():
|
||||
modes = golden_angle_pack(5, 0.6)
|
||||
alien = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||
alien[3] = 1.0
|
||||
# a genuine out-of-span component → nonzero residual energy
|
||||
probe = np.asarray(modes[1] * 0.6 + modes[3] * 0.4 + alien * 0.7, dtype=np.float64)
|
||||
base_e, base_r = _recall(range(5), modes, probe)
|
||||
assert base_e > 0.1 # the residual is meaningful, not numerical dust
|
||||
for perm in itertools.islice(itertools.permutations(range(5)), 0, 120, 11):
|
||||
e, r = _recall(list(perm), modes, probe)
|
||||
assert abs(e - base_e) < _TOL
|
||||
assert float(np.max(np.abs(r - base_r))) < _TOL
|
||||
Loading…
Reference in a new issue