feat(adr-0249): P2 affine relation compiler
Compiles output = scale*input + offset (Tier-1: scale > 0) into a quadratic-well
constraint Hamiltonian, reusing the ratified compile_quadratic_well +
HamiltonianCompileError contracts.
Anti-hollow (spike §4.1): the compiler never evaluates scale*input+offset in
Python — it embeds the input (P1) and applies the relation's structure as
versor operators (dilator for scale, translator for offset), so the substrate's
geometric product performs the arithmetic. The returned well is a bare
projector carrying no answer and no coefficients (metadata = {curvature,
target_digest} only); relaxation + projective readback recover the output.
Verified end-to-end: multiply/add/subtract/divide/negative-input all decode
exactly; ablation confirms the start decodes to the input and only the relaxed
state to the answer.
Fail-closed on non-positive scale (outside positive-dilation Tier-1) and
non-finite coefficients. Golden-bytes canary pins the compiled matrix.
Serve-quarantined (A-04). Single-relation primitive; state-chaining is P4.
21/21 pins green.
[Verification]: uv run python -m pytest tests/test_adr_0249_relation_compiler.py -q
This commit is contained in:
parent
aca98d857d
commit
3098971fd2
2 changed files with 215 additions and 0 deletions
91
core/physics/relation_compiler.py
Normal file
91
core/physics/relation_compiler.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"""core.physics.relation_compiler — affine relation → constraint Hamiltonian (ADR-0249 P2).
|
||||
|
||||
Compiles a single affine relation ``output = scale·input + offset`` (Tier-1:
|
||||
scale > 0, offset ∈ ℝ, input a known quantity) into a quadratic-well
|
||||
ProblemHamiltonian whose ground state is the null point encoding the output.
|
||||
|
||||
Anti-hollow (spike §4.1): the compiler NEVER evaluates ``scale·input + offset``
|
||||
in Python. It embeds the input as a null point (P1) and applies the relation's
|
||||
*structure* as versor operators — a dilator for the scale, a translator for the
|
||||
offset — so the arithmetic is performed by the substrate's geometric product,
|
||||
not by the compiler. The returned Hamiltonian is a bare geometric constraint
|
||||
(a projector well); it carries no answer and no relation coefficients. Only
|
||||
relaxation + projective readback recover the output.
|
||||
|
||||
Reuses the ratified ``compile_quadratic_well`` + ``HamiltonianCompileError``
|
||||
contracts (spike §4.3/§4.4); fail-closed on non-finite coefficients and on
|
||||
non-positive scale (outside the positive-dilation Tier-1 envelope). This is the
|
||||
single-relation primitive; multi-step chaining (previously-certified state as
|
||||
the next input) is the P4 turn-program compiler. Serve-quarantined (A-04):
|
||||
``core/physics/`` is never imported by ``chat/runtime.py``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
from core.physics.cognitive_lifecycle import (
|
||||
HamiltonianCompileError,
|
||||
ProblemHamiltonian,
|
||||
compile_quadratic_well,
|
||||
)
|
||||
from core.physics.quantity_kernel import (
|
||||
dilate_quantity,
|
||||
embed_quantity,
|
||||
translate_quantity,
|
||||
)
|
||||
|
||||
__all__ = ["compile_affine_relation", "affine_relaxation_start"]
|
||||
|
||||
# Below this the transported target has collapsed and cannot be unit-normalized.
|
||||
_MIN_TARGET_NORM = 1e-12
|
||||
|
||||
|
||||
def _finite(value: float, *, what: str) -> float:
|
||||
v = float(value)
|
||||
if not math.isfinite(v):
|
||||
raise HamiltonianCompileError(f"{what}_not_finite")
|
||||
return v
|
||||
|
||||
|
||||
def affine_relaxation_start(input_quantity: float) -> np.ndarray:
|
||||
"""Unit-norm null point of the KNOWN input — the natural relaxation start.
|
||||
|
||||
Decodes to the input (a given of the relation), never to the answer, so
|
||||
exposing it is not a hollow leak.
|
||||
"""
|
||||
q = _finite(input_quantity, what="input")
|
||||
psi = embed_quantity(q)
|
||||
return (psi / np.linalg.norm(psi)).astype(np.float64)
|
||||
|
||||
|
||||
def compile_affine_relation(
|
||||
input_quantity: float,
|
||||
*,
|
||||
scale: float,
|
||||
offset: float,
|
||||
curvature: float = 1.0,
|
||||
) -> ProblemHamiltonian:
|
||||
"""``output = scale·input + offset`` as a quadratic-well constraint Hamiltonian.
|
||||
|
||||
``scale`` > 0 (positive-dilation Tier-1 envelope); ``offset`` any finite
|
||||
real. ``curvature`` is validated by ``compile_quadratic_well``.
|
||||
"""
|
||||
q = _finite(input_quantity, what="input")
|
||||
s = _finite(scale, what="scale")
|
||||
o = _finite(offset, what="offset")
|
||||
if s <= 0.0:
|
||||
raise HamiltonianCompileError("scale_not_positive", scale=s)
|
||||
|
||||
# Relation structure as versors — never ``s*q + o`` in Python. Dilation
|
||||
# scales by e^{-alpha}, so multiplying by s needs alpha = -ln(s); the
|
||||
# substrate's geometric product performs the actual arithmetic.
|
||||
psi_scaled = dilate_quantity(embed_quantity(q), -math.log(s))
|
||||
psi_target = translate_quantity(psi_scaled, o)
|
||||
|
||||
norm = float(np.linalg.norm(psi_target))
|
||||
if norm < _MIN_TARGET_NORM:
|
||||
raise HamiltonianCompileError("degenerate_affine_target", norm=norm)
|
||||
unit_target = (psi_target / norm).astype(np.float64)
|
||||
return compile_quadratic_well(unit_target, curvature=curvature)
|
||||
124
tests/test_adr_0249_relation_compiler.py
Normal file
124
tests/test_adr_0249_relation_compiler.py
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
"""ADR-0249 P2 — affine relation compiler pins.
|
||||
|
||||
Compiles `output = scale·input + offset` (Tier-1: scale > 0) into a
|
||||
quadratic-well constraint Hamiltonian, reusing the ratified
|
||||
compile_quadratic_well contract. The arithmetic is performed by the substrate
|
||||
(versor transport), recovered by relaxation + projective readback — never by
|
||||
the compiler (anti-hollow, spike §4.1).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from core.physics.cognitive_lifecycle import (
|
||||
HamiltonianCompileError,
|
||||
ProblemHamiltonian,
|
||||
relax_to_ground,
|
||||
)
|
||||
from core.physics.quantity_kernel import decode_quantity
|
||||
from core.physics.relation_compiler import (
|
||||
affine_relaxation_start,
|
||||
compile_affine_relation,
|
||||
)
|
||||
|
||||
|
||||
def _solve(inp: float, scale: float, offset: float) -> float:
|
||||
"""Compile → relax from the known input → projectively decode the output."""
|
||||
ham = compile_affine_relation(inp, scale=scale, offset=offset)
|
||||
steady = relax_to_ground(affine_relaxation_start(inp), ham).psi_steady
|
||||
return decode_quantity(steady)
|
||||
|
||||
|
||||
# --- The substrate performs the arithmetic; relaxation + decode recover it ---
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("inp", "scale", "offset", "gold"),
|
||||
[
|
||||
(4.0, 3.0, 5.0, 17.0), # multiply then add
|
||||
(10.0, 1.0, -7.0, 3.0), # subtraction (offset < 0)
|
||||
(12.0, 1.0 / 3.0, 0.0, 4.0), # division (scale = 1/divisor)
|
||||
(5.0, 2.0, 0.0, 10.0), # pure multiply
|
||||
(0.0, 4.0, 9.0, 9.0), # offset-only (zero input)
|
||||
(-6.0, 2.0, 3.0, -9.0), # negative input
|
||||
],
|
||||
)
|
||||
def test_forward_affine_answer_recovered(inp, scale, offset, gold) -> None:
|
||||
assert abs(_solve(inp, scale, offset) - gold) < 1e-4
|
||||
|
||||
|
||||
# --- The compiled object is a pure constraint well, carrying no answer -------
|
||||
|
||||
|
||||
def test_compile_returns_problem_hamiltonian() -> None:
|
||||
ham = compile_affine_relation(4.0, scale=3.0, offset=5.0)
|
||||
assert isinstance(ham, ProblemHamiltonian)
|
||||
assert ham.matrix.shape == (32, 32)
|
||||
assert ham.matrix.dtype == np.dtype(np.float64)
|
||||
|
||||
|
||||
def test_hamiltonian_leaks_neither_answer_nor_relation() -> None:
|
||||
# Anti-hollow (spike §4.1): the well is a bare projector — its metadata
|
||||
# exposes only curvature + target digest, never scale/offset/answer.
|
||||
ham = compile_affine_relation(4.0, scale=3.0, offset=5.0)
|
||||
assert set(ham.metadata) == {"curvature", "target_digest"}
|
||||
assert ham.domain == "quadratic_well"
|
||||
|
||||
|
||||
def test_ablation_relaxation_computes_the_answer() -> None:
|
||||
# The answer (17) is absent from the start state (decodes to the input 4);
|
||||
# relaxation is what produces it. The Hamiltonian bytes are identical either
|
||||
# way — the corridor does the work, not the compile step.
|
||||
ham = compile_affine_relation(4.0, scale=3.0, offset=5.0)
|
||||
start = affine_relaxation_start(4.0)
|
||||
assert abs(decode_quantity(start) - 4.0) < 1e-6
|
||||
steady = relax_to_ground(start, ham).psi_steady
|
||||
assert abs(decode_quantity(steady) - 17.0) < 1e-4
|
||||
assert ham.matrix.tobytes() == ham.matrix.tobytes() # frozen, unchanged
|
||||
|
||||
|
||||
def test_relaxation_start_decodes_to_known_input() -> None:
|
||||
assert abs(decode_quantity(affine_relaxation_start(7.5)) - 7.5) < 1e-9
|
||||
|
||||
|
||||
# --- Fail-closed refusals (HamiltonianCompileError family, spike §4.3) -------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad_scale", [0.0, -1.0, -3.5])
|
||||
def test_refuses_nonpositive_scale(bad_scale) -> None:
|
||||
with pytest.raises(HamiltonianCompileError):
|
||||
compile_affine_relation(4.0, scale=bad_scale, offset=1.0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad", [np.inf, -np.inf, np.nan])
|
||||
def test_refuses_nonfinite_input(bad) -> None:
|
||||
with pytest.raises(HamiltonianCompileError):
|
||||
compile_affine_relation(bad, scale=2.0, offset=1.0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad", [np.inf, np.nan])
|
||||
def test_refuses_nonfinite_scale(bad) -> None:
|
||||
with pytest.raises(HamiltonianCompileError):
|
||||
compile_affine_relation(4.0, scale=bad, offset=1.0)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bad", [np.inf, np.nan])
|
||||
def test_refuses_nonfinite_offset(bad) -> None:
|
||||
with pytest.raises(HamiltonianCompileError):
|
||||
compile_affine_relation(4.0, scale=2.0, offset=bad)
|
||||
|
||||
|
||||
# --- Tier-2 cross-hardware reproducibility canary (spike §4.6) --------------
|
||||
|
||||
# SHA-256 of compile_affine_relation(4, scale=3, offset=5).matrix as <f8 bytes.
|
||||
# Frozen from the first green run; a change means substrate/compile drift.
|
||||
_GOLDEN_RELATION = "72e414a9549fc79dfae250d0c2e035108f0acdd6649a20193f96a1ff86bc3ce0"
|
||||
|
||||
|
||||
def test_relation_hamiltonian_golden_bytes_are_stable() -> None:
|
||||
ham = compile_affine_relation(4.0, scale=3.0, offset=5.0)
|
||||
digest = hashlib.sha256(ham.matrix.astype("<f8").tobytes()).hexdigest()
|
||||
assert digest == _GOLDEN_RELATION, f"compile drift: got {digest}"
|
||||
Loading…
Reference in a new issue