Add ManifoldState (N,32) versor field over graph edges, GraphDiffusionOperator with damped convergence via construction_seed_versor closure, deterministic hash-to-versor stub, and run_pulse.py end-to-end script proving injection → propagation → vault recall → token output. 24 new tests, zero regressions on architectural invariants.
83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
"""ManifoldState construction, validation, and immutability tests."""
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from algebra.rotor import make_rotor_from_angle
|
|
from field.state import ManifoldState
|
|
|
|
|
|
def _make_versors(n: int) -> np.ndarray:
|
|
return np.stack(
|
|
[make_rotor_from_angle(0.1 * (i + 1)).astype(np.float32) for i in range(n)],
|
|
axis=0,
|
|
)
|
|
|
|
|
|
def _triangle_edges() -> np.ndarray:
|
|
return np.array([[0, 1], [1, 2], [0, 2]], dtype=np.int32)
|
|
|
|
|
|
class TestConstruction:
|
|
def test_valid_construction(self) -> None:
|
|
fields = _make_versors(3)
|
|
edges = _triangle_edges()
|
|
ms = ManifoldState(fields=fields, edges=edges)
|
|
assert ms.fields.shape == (3, 32)
|
|
assert ms.edges.shape == (3, 2)
|
|
assert ms.step == 0
|
|
|
|
def test_fields_are_copied(self) -> None:
|
|
fields = _make_versors(2)
|
|
edges = np.array([[0, 1]], dtype=np.int32)
|
|
ms = ManifoldState(fields=fields, edges=edges)
|
|
assert ms.fields is not fields
|
|
|
|
def test_bad_field_shape_raises(self) -> None:
|
|
with pytest.raises(ValueError, match="shape"):
|
|
ManifoldState(
|
|
fields=np.ones((3, 16), dtype=np.float32),
|
|
edges=_triangle_edges(),
|
|
)
|
|
|
|
def test_bad_edge_shape_raises(self) -> None:
|
|
with pytest.raises(ValueError, match="shape"):
|
|
ManifoldState(
|
|
fields=_make_versors(3),
|
|
edges=np.array([[0, 1, 2]], dtype=np.int32),
|
|
)
|
|
|
|
def test_edge_out_of_bounds_raises(self) -> None:
|
|
with pytest.raises(ValueError, match="indices"):
|
|
ManifoldState(
|
|
fields=_make_versors(2),
|
|
edges=np.array([[0, 5]], dtype=np.int32),
|
|
)
|
|
|
|
def test_versor_condition_enforced(self) -> None:
|
|
bad_fields = np.random.randn(2, 32).astype(np.float32)
|
|
with pytest.raises(ValueError, match="versor_condition"):
|
|
ManifoldState(
|
|
fields=bad_fields,
|
|
edges=np.array([[0, 1]], dtype=np.int32),
|
|
)
|
|
|
|
|
|
class TestMutation:
|
|
def test_frozen(self) -> None:
|
|
ms = ManifoldState(fields=_make_versors(2), edges=np.array([[0, 1]], dtype=np.int32))
|
|
with pytest.raises(AttributeError):
|
|
ms.step = 5 # type: ignore[misc]
|
|
|
|
def test_with_fields_returns_new(self) -> None:
|
|
ms = ManifoldState(fields=_make_versors(2), edges=np.array([[0, 1]], dtype=np.int32))
|
|
new_fields = _make_versors(2)
|
|
ms2 = ms.with_fields(new_fields)
|
|
assert ms2 is not ms
|
|
assert ms.step == ms2.step
|
|
|
|
def test_advance_increments_step(self) -> None:
|
|
ms = ManifoldState(fields=_make_versors(2), edges=np.array([[0, 1]], dtype=np.int32))
|
|
ms2 = ms.advance()
|
|
assert ms2.step == ms.step + 1
|
|
assert np.array_equal(ms2.fields, ms.fields)
|