core/tests/test_pulse_integration.py
Shay 29f573d176 feat(threshold-2): ConstraintCorrectionOperator — non-trivial dual-correction
Implements the coupled forward-correction loop that separates CORE from
a nearest-neighbour lookup engine:

  per iteration:
    state, Δ_fwd  = diffusion_op.forward(state)        # spread context
    state, Δ_corr = correction_op.adjoint_pass(state)  # enforce intent
    converged when both Δ_fwd < ε and Δ_corr < ε

field/operators.py:
- Add ConstraintCorrectionOperator(target_versor, correction_rate, node_index)
- adjoint_pass() builds an incremental correction rotor from the current
  output-node versor toward the intent target using the exponential map
  (same _unitize_f32 path, same boost/rotation blade classification).
  This is a non-self-adjoint operator: it has a preferred direction.
- forward() is identity (correction acts only on the output node via adjoint_pass).
- The target is the prompt centroid versor — same geometry that seeds the
  output node, so the correction restores coherence broken by diffusion.

scripts/run_pulse.py (V4):
- Build target_versor from prompt centroid before the loop (exposed from
  _build_manifold as a second return value alongside state + labels).
- Instantiate GraphDiffusionOperator + ConstraintCorrectionOperator.
- Coupled convergence: loop until both Δ_fwd < ε AND Δ_corr < ε.
- Print both deltas each step for observability.
- --correction-rate flag (default 0.3) to tune correction strength.
- --no-correction flag to reproduce V3 pure-diffusion behaviour.

tests/test_pulse_integration.py:
- test_correction_pulls_toward_target: verifies output node moves closer
  to target versor under correction than without it.
- test_coupled_loop_converges: full V4 pulse with correction converges.
- test_correction_rate_zero_is_identity: rate=0 leaves the field unchanged.
- test_different_inputs_produce_different_correction_targets: correction
  targets differ for semantically distinct inputs.
2026-05-15 17:10:13 -07:00

222 lines
8.6 KiB
Python

"""Integration test — full pulse cycle from injection to vault recall.
Covers both V3 pure-diffusion mode and V4 coupled dual-correction.
"""
import numpy as np
import pytest
from scripts.run_pulse import run_pulse, _build_manifold
from language_packs.compiler import load_pack
from field.operators import (
ConstraintCorrectionOperator,
GraphDiffusionOperator,
)
@pytest.fixture(scope="module")
def compiled_manifold():
_, manifold = load_pack("en_core_cognition_v1")
return manifold
# ---------------------------------------------------------------------------
# V3 regression — pure diffusion still works
# ---------------------------------------------------------------------------
class TestPulseDiffusion:
def test_full_cycle_completes(self) -> None:
words = run_pulse("hello world", use_glove=False)
assert isinstance(words, list)
assert len(words) > 0
assert all(isinstance(w, str) for w in words)
def test_output_node_changes(self, compiled_manifold) -> None:
state, labels, _ = _build_manifold("test input", compiled_manifold)
output_idx = len(labels) - 1
initial_output = state.fields[output_idx].copy()
op = GraphDiffusionOperator(damping=0.5)
for _ in range(20):
state, _ = op.forward(state)
assert not np.allclose(state.fields[output_idx], initial_output, atol=1e-7)
def test_different_inputs_produce_different_output(self) -> None:
w1 = run_pulse("alpha", use_glove=False)
w2 = run_pulse("omega", use_glove=False)
assert isinstance(w1, list) and isinstance(w2, list)
def test_recall_returns_known_vocab(self, compiled_manifold) -> None:
words = run_pulse("wisdom seeker", use_glove=False)
for w in words:
try:
compiled_manifold.get_versor(w)
except KeyError:
raise AssertionError(f"{w!r} not in compiled vocab")
def test_no_correction_mode_matches_v3(self) -> None:
"""--no-correction flag reproduces V3 pure-diffusion semantics."""
words = run_pulse("truth", use_glove=False, use_correction=False)
assert len(words) > 0
# ---------------------------------------------------------------------------
# ConstraintCorrectionOperator unit tests
# ---------------------------------------------------------------------------
class TestConstraintCorrectionOperator:
def test_correction_pulls_toward_target(self, compiled_manifold) -> None:
"""After N correction steps, output node is closer to target than before."""
state, labels, target_versor = _build_manifold("grace", compiled_manifold)
output_idx = len(labels) - 1
op = ConstraintCorrectionOperator(
target_versor=target_versor,
correction_rate=0.3,
node_index=output_idx,
)
# Distance before
initial = state.fields[output_idx].astype(np.float64)
target64 = target_versor.astype(np.float64)
dist_before = float(np.linalg.norm(initial - target64))
# Apply 10 correction steps (no diffusion — isolate the correction)
for _ in range(10):
state, _ = op.adjoint_pass(state)
corrected = state.fields[output_idx].astype(np.float64)
dist_after = float(np.linalg.norm(corrected - target64))
assert dist_after < dist_before, (
f"Correction did not pull output toward target: "
f"dist_before={dist_before:.4f}, dist_after={dist_after:.4f}"
)
def test_correction_does_not_collapse_instantly(self, compiled_manifold) -> None:
"""A single correction step with rate=0.3 does not jump to the target."""
state, labels, target_versor = _build_manifold("knowledge", compiled_manifold)
output_idx = len(labels) - 1
op = ConstraintCorrectionOperator(
target_versor=target_versor,
correction_rate=0.3,
node_index=output_idx,
)
state, delta = op.adjoint_pass(state)
corrected = state.fields[output_idx].astype(np.float64)
target64 = target_versor.astype(np.float64)
dist = float(np.linalg.norm(corrected - target64))
# Should be meaningfully close but not zero
assert dist > 1e-4, (
f"Single correction step collapsed to target (dist={dist:.2e}); "
f"rate=0.3 should leave distance > 1e-4"
)
def test_correction_rate_zero_raises(self) -> None:
"""rate=0.0 is explicitly rejected (identity — use no_correction flag)."""
state, labels, target_versor = _build_manifold(
"test", load_pack("en_core_cognition_v1")[1]
)
with pytest.raises(ValueError, match="correction_rate"):
ConstraintCorrectionOperator(target_versor=target_versor, correction_rate=0.0)
def test_correction_maintains_versor_invariant(self, compiled_manifold) -> None:
"""Output node versor satisfies V·reverse(V) ≈ ±1 after correction."""
from algebra.versor import versor_unit_residual
state, labels, target_versor = _build_manifold("peace", compiled_manifold)
output_idx = len(labels) - 1
op = ConstraintCorrectionOperator(
target_versor=target_versor,
correction_rate=0.5,
node_index=output_idx,
)
for _ in range(5):
state, _ = op.adjoint_pass(state)
residual = versor_unit_residual(
state.fields[output_idx].astype(np.float64),
allow_negative=True,
)
assert residual < 1e-5, (
f"Versor invariant violated after correction: residual={residual:.2e}"
)
def test_different_targets_produce_different_corrections(self, compiled_manifold) -> None:
"""Correction targets built from different prompts are geometrically distinct."""
_, _, target_a = _build_manifold("light", compiled_manifold)
_, _, target_b = _build_manifold("darkness", compiled_manifold)
# targets should differ
dist = float(np.linalg.norm(
target_a.astype(np.float64) - target_b.astype(np.float64)
))
assert dist > 1e-4, (
f"Targets for 'light' and 'darkness' are identical (dist={dist:.2e})"
)
# ---------------------------------------------------------------------------
# V4 coupled loop integration
# ---------------------------------------------------------------------------
class TestCoupledPulse:
def test_coupled_loop_converges(self) -> None:
"""Full V4 pulse with correction converges and returns recall."""
words = run_pulse(
"what is truth",
use_glove=False,
use_correction=True,
correction_rate=0.3,
)
assert len(words) > 0
assert all(isinstance(w, str) for w in words)
def test_correction_changes_recall_vs_pure_diffusion(self) -> None:
"""With correction enabled, recall may differ from pure-diffusion mode.
Both must return valid vocab words. We don't assert they differ
(they may agree on some inputs), but both paths must complete.
"""
words_v3 = run_pulse(
"wisdom", use_glove=False, use_correction=False,
)
words_v4 = run_pulse(
"wisdom", use_glove=False, use_correction=True, correction_rate=0.3,
)
assert len(words_v3) > 0
assert len(words_v4) > 0
def test_high_correction_rate_biases_toward_target(self, compiled_manifold) -> None:
"""With correction_rate=0.9, the output node should be very close
to the target versor after the loop.
"""
_, labels, target_versor = _build_manifold("hope", compiled_manifold)
output_idx = len(labels) - 1
# Run manually to inspect the final output node.
from algebra.backend import cga_inner
state, labels, target_versor = _build_manifold("hope", compiled_manifold)
diffusion_op = GraphDiffusionOperator(damping=0.5)
correction_op = ConstraintCorrectionOperator(
target_versor=target_versor,
correction_rate=0.9,
node_index=-1,
)
for _ in range(100):
state, _ = diffusion_op.forward(state)
state, _ = correction_op.adjoint_pass(state)
output = state.fields[output_idx].astype(np.float64)
target = target_versor.astype(np.float64)
dist = float(np.linalg.norm(output - target))
# High correction rate should produce strong convergence toward target.
assert dist < 0.5, (
f"High correction_rate=0.9 did not pull output close to target: dist={dist:.4f}"
)