feat(reasoning): field-wedge foundation — f64 embedding, lineage firewall, INV-27
Phase 0 of the field-reasoner wedge — net hardening regardless of the experiment's outcome. - algebra/cga.py: embed_point gains a dtype kwarg (f32 default byte-unchanged; cl41.geometric_product already preserves f64) + read_scalar_e1 projective dehomogenization read-back (weight-invariant; correct for dilations, where a raw distance-from-origin is wrong) + EMBED_EXACT_MAX pinned magnitude ceiling. f32 silently collapsed integer coordinates past ~1e4. - core/reasoning/evidence.py: verify_tier2_agreement now keys independence on a reader_lineage pathway token (refuses SAME_READER_LINEAGE), replacing the label-only len(set(signatures))<2 check a single reader could satisfy by relabeling. reader_lineage is excluded from canonical serialization, so the entailment trace_hash is unchanged. - tests INV-27: transitive reader-disjointness over TIER2_READER_PATHWAYS makes the lineage check load-bearing (distinct lineage => proven import-disjoint pathway). The two seeded readers share zero transitive first-party modules. Green: smoke 87, algebra 82, cognition 121, 53 architectural invariants, reasoning/deductive/r1 50; 16 new f64-exactness tests; zero regressions.
This commit is contained in:
parent
991be784fa
commit
568face63e
8 changed files with 429 additions and 11 deletions
|
|
@ -1,5 +1,13 @@
|
|||
from .cl41 import geometric_product, reverse, grade_project, scalar_part, norm_squared, basis_vector
|
||||
from .versor import versor_apply, normalize_to_versor, versor_condition
|
||||
from .cga import cga_inner, outer_product, is_null, null_project, embed_point
|
||||
from .cga import (
|
||||
EMBED_EXACT_MAX,
|
||||
cga_inner,
|
||||
outer_product,
|
||||
is_null,
|
||||
null_project,
|
||||
embed_point,
|
||||
read_scalar_e1,
|
||||
)
|
||||
from .holonomy import holonomy_encode, holonomy_similarity
|
||||
from .rotor import word_transition_rotor
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ from .cl41 import geometric_product, scalar_part, basis_vector, N_COMPONENTS
|
|||
_E4_IDX = 4
|
||||
_E5_IDX = 5
|
||||
|
||||
# Pinned magnitude ceiling for f64-exact embedding + read-back (Phase 0A).
|
||||
# Below this bound, ``embed_point(..., dtype=np.float64)`` round-trips integer
|
||||
# coordinates exactly through ``read_scalar_e1`` and the conformal distance metric
|
||||
# stays exact (proven in tests/test_cga_f64_exactness.py). The field-reasoner reader
|
||||
# REFUSES any quantity whose magnitude exceeds this bound; the refusal lives in the
|
||||
# reader — this module only states the bound. Generous vs GSM8K (quantities ~< 1e5).
|
||||
EMBED_EXACT_MAX: int = 1_000_000
|
||||
|
||||
|
||||
def cga_inner(X: np.ndarray, Y: np.ndarray) -> float:
|
||||
"""
|
||||
|
|
@ -62,18 +70,25 @@ def null_project(X: np.ndarray) -> np.ndarray:
|
|||
return embed_point(euclidean)
|
||||
|
||||
|
||||
def embed_point(x: np.ndarray) -> np.ndarray:
|
||||
def embed_point(x: np.ndarray, *, dtype: "np.typing.DTypeLike" = np.float32) -> np.ndarray:
|
||||
"""
|
||||
Embed a Euclidean point x in R^3 into the CGA null cone.
|
||||
|
||||
X = x + n_o + 0.5|x|^2 n_inf,
|
||||
where n_o = 0.5(e5-e4), n_inf = e4+e5.
|
||||
|
||||
``dtype`` defaults to ``float32`` so every existing caller is byte-unchanged.
|
||||
The field-reasoner reader passes ``dtype=np.float64`` to get an exact embedding:
|
||||
``geometric_product`` already preserves float64 (``np.result_type``), so the
|
||||
only thing that forced f32 was this construction. f32 silently collapses the
|
||||
``n_o`` weight past ~1e4 (the ``0.5|x|^2`` terms lose the ``±1``); f64 keeps it
|
||||
exact up to :data:`EMBED_EXACT_MAX` (see tests/test_cga_f64_exactness.py).
|
||||
"""
|
||||
x = np.asarray(x, dtype=np.float32)
|
||||
x = np.asarray(x, dtype=dtype)
|
||||
assert len(x) == 3, "embed_point expects a 3D vector"
|
||||
|
||||
x_sq = float(np.dot(x, x))
|
||||
result = np.zeros(N_COMPONENTS, dtype=np.float32)
|
||||
result = np.zeros(N_COMPONENTS, dtype=dtype)
|
||||
result[1:4] = x
|
||||
|
||||
# n_o + 0.5|x|^2 n_inf
|
||||
|
|
@ -82,3 +97,26 @@ def embed_point(x: np.ndarray) -> np.ndarray:
|
|||
result[_E4_IDX] = 0.5 * (x_sq - 1.0)
|
||||
result[_E5_IDX] = 0.5 * (x_sq + 1.0)
|
||||
return result
|
||||
|
||||
|
||||
def read_scalar_e1(X: np.ndarray) -> float:
|
||||
"""Projective dehomogenization on the e1 axis — the exact, weight-invariant
|
||||
read-back of a scalar coordinate from a (possibly dilated) conformal point.
|
||||
|
||||
A point at coordinate ``v`` on the e1 number line embeds as
|
||||
``X = v*e1 + n_o + 0.5 v^2 n_inf``; a uniform conformal dilation by ``k``
|
||||
scales the whole null vector. The coordinate is recovered as
|
||||
``e1_coefficient / n_o_weight`` where the n_o weight is ``X[e5] - X[e4]``
|
||||
(== 1 for an un-dilated point), so any dilation weight divides out. This is
|
||||
the correct read-back for weight-changing operators; a raw distance-from-origin
|
||||
is wrong for them.
|
||||
|
||||
Raises ``ValueError`` on a degenerate (zero) n_o weight — a point at infinity
|
||||
or an f32 weight-collapse — rather than returning a silently wrong value.
|
||||
"""
|
||||
no_weight = float(X[_E5_IDX] - X[_E4_IDX])
|
||||
if no_weight == 0.0:
|
||||
raise ValueError(
|
||||
"read_scalar_e1: degenerate n_o weight (point at infinity or f32 collapse)"
|
||||
)
|
||||
return float(X[1]) / no_weight
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from core.reasoning.evidence import (
|
|||
DUPLICATE_STRUCTURAL_SIGNATURE,
|
||||
INSUFFICIENT_EVIDENCE,
|
||||
MISSING_COMMITMENT,
|
||||
SAME_READER_LINEAGE,
|
||||
TIER2_VERIFIED,
|
||||
EvidenceBundle,
|
||||
OperatorEvidence,
|
||||
|
|
@ -23,6 +24,7 @@ __all__ = [
|
|||
"DUPLICATE_STRUCTURAL_SIGNATURE",
|
||||
"INSUFFICIENT_EVIDENCE",
|
||||
"MISSING_COMMITMENT",
|
||||
"SAME_READER_LINEAGE",
|
||||
"TIER2_VERIFIED",
|
||||
"EvidenceBundle",
|
||||
"OperatorEvidence",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ def evidence_from_entailment_trace(trace: EntailmentTrace) -> OperatorEvidence:
|
|||
check_keys=check_keys,
|
||||
commitment_key=commitment_key,
|
||||
structural_signature=structural_signature,
|
||||
reader_lineage="proof_chain.entail",
|
||||
payload={"entailment_trace": trace.as_dict()},
|
||||
)
|
||||
|
||||
|
|
@ -93,6 +94,7 @@ def evidence_from_math_solution(
|
|||
check_keys=(trace_hash, trace.graph_canonical_hash),
|
||||
commitment_key=commitment_key,
|
||||
structural_signature=structural_signature,
|
||||
reader_lineage="math_problem_graph.solve_verify",
|
||||
payload={
|
||||
"answer_entity": trace.answer_entity,
|
||||
"answer_unit": trace.answer_unit,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ INSUFFICIENT_EVIDENCE: Final[str] = "insufficient_evidence"
|
|||
DUPLICATE_STRUCTURAL_SIGNATURE: Final[str] = "duplicate_structural_signature"
|
||||
COMMITMENT_DISAGREEMENT: Final[str] = "commitment_disagreement"
|
||||
MISSING_COMMITMENT: Final[str] = "missing_commitment"
|
||||
SAME_READER_LINEAGE: Final[str] = "same_reader_lineage"
|
||||
|
||||
TIER2_REASONS: Final[frozenset[str]] = frozenset({
|
||||
TIER2_VERIFIED,
|
||||
|
|
@ -27,6 +28,7 @@ TIER2_REASONS: Final[frozenset[str]] = frozenset({
|
|||
DUPLICATE_STRUCTURAL_SIGNATURE,
|
||||
COMMITMENT_DISAGREEMENT,
|
||||
MISSING_COMMITMENT,
|
||||
SAME_READER_LINEAGE,
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -74,6 +76,17 @@ class OperatorEvidence:
|
|||
check_keys: tuple[str, ...]
|
||||
commitment_key: str
|
||||
structural_signature: str
|
||||
# reader_lineage — the mechanistic identity of the decoding PATHWAY that
|
||||
# produced this evidence (e.g. "proof_chain.entail"). The Tier-2 gate keys
|
||||
# independence on this, not on the cosmetic structural_signature string, so a
|
||||
# single reader cannot self-verify by relabeling. Deliberately EXCLUDED from
|
||||
# ``as_dict``/``canonical_json`` (and therefore the evidence/trace hash): it is
|
||||
# gate-routing provenance, not replayable evidence content, and including it
|
||||
# would perturb the entailment trace_hash with no replay benefit. The
|
||||
# static guarantee that distinct lineages are import-disjoint (cannot share a
|
||||
# decoding pathway) is enforced by the architectural reader-disjointness
|
||||
# invariant, not by this string alone.
|
||||
reader_lineage: str = ""
|
||||
payload: Mapping[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
|
|
@ -83,6 +96,7 @@ class OperatorEvidence:
|
|||
"outcome",
|
||||
"reason",
|
||||
"structural_signature",
|
||||
"reader_lineage",
|
||||
):
|
||||
value = getattr(self, field_name)
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
|
|
@ -146,6 +160,7 @@ class Tier2Verdict:
|
|||
commitment_key: str = ""
|
||||
evidence_hash: str = ""
|
||||
structural_signatures: tuple[str, ...] = ()
|
||||
reader_lineages: tuple[str, ...] = ()
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.reason not in TIER2_REASONS:
|
||||
|
|
@ -155,6 +170,11 @@ class Tier2Verdict:
|
|||
"structural_signatures",
|
||||
tuple(str(s) for s in self.structural_signatures),
|
||||
)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"reader_lineages",
|
||||
tuple(str(s) for s in self.reader_lineages),
|
||||
)
|
||||
|
||||
def as_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
|
|
@ -163,13 +183,23 @@ class Tier2Verdict:
|
|||
"commitment_key": self.commitment_key,
|
||||
"evidence_hash": self.evidence_hash,
|
||||
"structural_signatures": list(self.structural_signatures),
|
||||
"reader_lineages": list(self.reader_lineages),
|
||||
}
|
||||
|
||||
|
||||
def verify_tier2_agreement(
|
||||
evidences: tuple[OperatorEvidence, ...] | list[OperatorEvidence],
|
||||
) -> Tier2Verdict:
|
||||
"""Require two distinct structures converging on one non-empty commitment."""
|
||||
"""Require two **independently-read** structures converging on one commitment.
|
||||
|
||||
Independence is keyed on ``reader_lineage`` — the decoding PATHWAY — not on the
|
||||
cosmetic ``structural_signature``. The same reader cannot self-verify by emitting
|
||||
two differently-labeled signatures for one case: that is refused with
|
||||
``SAME_READER_LINEAGE``. The static guarantee that distinct lineages are
|
||||
import-disjoint (so distinct lineage ⟹ no shared decoding pathway) is enforced
|
||||
by the architectural reader-disjointness invariant; this gate enforces the
|
||||
runtime half (distinct producing pathways + distinct structure + one commitment).
|
||||
"""
|
||||
bundle = EvidenceBundle(tuple(evidences))
|
||||
if len(bundle.evidences) < 2:
|
||||
return Tier2Verdict(False, INSUFFICIENT_EVIDENCE)
|
||||
|
|
@ -177,13 +207,26 @@ def verify_tier2_agreement(
|
|||
if any(not ev.commitment_key for ev in bundle.evidences):
|
||||
return Tier2Verdict(False, MISSING_COMMITMENT, evidence_hash=bundle.evidence_hash)
|
||||
|
||||
lineages = tuple(ev.reader_lineage for ev in bundle.evidences)
|
||||
signatures = tuple(ev.structural_signature for ev in bundle.evidences)
|
||||
|
||||
# Mechanistic independence firewall: at least two DISTINCT decoding pathways.
|
||||
if len(set(lineages)) < 2:
|
||||
return Tier2Verdict(
|
||||
False,
|
||||
SAME_READER_LINEAGE,
|
||||
evidence_hash=bundle.evidence_hash,
|
||||
structural_signatures=tuple(sorted(set(signatures))),
|
||||
reader_lineages=tuple(sorted(set(lineages))),
|
||||
)
|
||||
|
||||
if len(set(signatures)) < 2:
|
||||
return Tier2Verdict(
|
||||
False,
|
||||
DUPLICATE_STRUCTURAL_SIGNATURE,
|
||||
evidence_hash=bundle.evidence_hash,
|
||||
structural_signatures=tuple(sorted(set(signatures))),
|
||||
reader_lineages=tuple(sorted(set(lineages))),
|
||||
)
|
||||
|
||||
commitments = Counter(ev.commitment_key for ev in bundle.evidences)
|
||||
|
|
@ -194,6 +237,7 @@ def verify_tier2_agreement(
|
|||
COMMITMENT_DISAGREEMENT,
|
||||
evidence_hash=bundle.evidence_hash,
|
||||
structural_signatures=tuple(sorted(set(signatures))),
|
||||
reader_lineages=tuple(sorted(set(lineages))),
|
||||
)
|
||||
|
||||
return Tier2Verdict(
|
||||
|
|
@ -202,4 +246,5 @@ def verify_tier2_agreement(
|
|||
commitment_key=shared[0],
|
||||
evidence_hash=bundle.evidence_hash,
|
||||
structural_signatures=tuple(sorted(set(signatures))),
|
||||
reader_lineages=tuple(sorted(set(lineages))),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1392,3 +1392,195 @@ class TestINV26InterlinguaNeutrality:
|
|||
"INV-26b predicate failed to detect the adapter's domain-reader "
|
||||
"import — the core-isolation check is vacuous."
|
||||
)
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# INV-27 Tier-2 reader disjointness — the two readers whose AGREEMENT the
|
||||
# Tier-2 gate trusts must share no decoding pathway
|
||||
# ===========================================================================
|
||||
#
|
||||
# Claim (docs/analysis/field-reasoner-wedge-design-and-falsification-2026-06-04
|
||||
# .md §3.1; the decoration firewall):
|
||||
#
|
||||
# ``verify_tier2_agreement`` keys independence on ``reader_lineage`` — the
|
||||
# decoding PATHWAY — not on the cosmetic ``structural_signature``. That runtime
|
||||
# check is only load-bearing if "distinct lineage" actually means "no shared
|
||||
# decoding pathway." A string alone cannot prove that. This invariant supplies
|
||||
# the static proof: every reader that EMITS a Tier-2 lineage is registered with
|
||||
# its entry module, and any two registered readers' TRANSITIVE first-party
|
||||
# import sets are disjoint (modulo an explicit pure-data neutral allowlist).
|
||||
#
|
||||
# The old gate admitted on ``len(set(signatures)) < 2`` — a label test that a
|
||||
# single reader could satisfy by relabeling. The single-level INV-25 import
|
||||
# check would also miss a helper that re-pulls a shared reader transitively;
|
||||
# this check is TRANSITIVE.
|
||||
#
|
||||
# 27a Every ``reader_lineage="..."`` literal emitted in core/reasoning/adapters
|
||||
# is registered in TIER2_READER_PATHWAYS (so its disjointness is proven).
|
||||
# 27b Each registered reader module exists (drift guard).
|
||||
# 27c Every pair of registered readers is transitively import-disjoint, except
|
||||
# modules explicitly allowlisted as pure-data neutral meeting points.
|
||||
# 27d Non-vacuity: the disjointness predicate flags a genuinely-shared pair and
|
||||
# the neutral allowlist genuinely clears one — proving 27c can fail.
|
||||
|
||||
import dataclasses as _dataclasses
|
||||
|
||||
# Top-level first-party package roots. An imported module whose first dotted
|
||||
# component is one of these is project code (a potential shared decoding pathway);
|
||||
# stdlib and third-party (numpy, ...) are not.
|
||||
_FIRST_PARTY_ROOTS: frozenset[str] = frozenset({
|
||||
"generate", "core", "algebra", "field", "language_packs", "vault", "chat",
|
||||
"teaching", "sensorium", "calibration", "evals", "ingest", "recognition",
|
||||
"formation", "morphology", "vocab", "session", "contemplation", "persona",
|
||||
"alignment", "probe", "core_ingest", "core_rs",
|
||||
})
|
||||
|
||||
|
||||
@_dataclasses.dataclass(frozen=True)
|
||||
class Tier2Reader:
|
||||
"""A reader whose evidence the Tier-2 gate may treat as an independent vote."""
|
||||
|
||||
lineage: str # must equal the ``reader_lineage`` this reader emits
|
||||
module: str # repo-relative path to the reader's entry module
|
||||
|
||||
|
||||
# Readers whose agreement the Tier-2 gate is allowed to trust. Adding one is the
|
||||
# reviewable seam: it asserts (and INV-27c proves) the new reader shares no
|
||||
# decoding pathway with the others.
|
||||
TIER2_READER_PATHWAYS: tuple[Tier2Reader, ...] = (
|
||||
Tier2Reader("proof_chain.entail", "generate/proof_chain/entail.py"),
|
||||
Tier2Reader("math_problem_graph.solve_verify", "generate/math_solver.py"),
|
||||
)
|
||||
|
||||
# Pure-data / contract modules that are a NEUTRAL meeting point, not a shared
|
||||
# decoding pathway: two readers may both import these without their agreement
|
||||
# becoming common-mode. Every entry must be parser/solver-free (frozen data or
|
||||
# pure algebra). EMPTY today — the current readers share nothing; the field
|
||||
# reader (Phase W) will add ``generate.binding_graph.model`` here, with review.
|
||||
TIER2_NEUTRAL_MODULES: frozenset[str] = frozenset()
|
||||
|
||||
|
||||
def _is_first_party(mod: str) -> bool:
|
||||
return mod.split(".")[0] in _FIRST_PARTY_ROOTS
|
||||
|
||||
|
||||
def _resolve_module(mod: str) -> Path | None:
|
||||
base = _REPO_ROOT / mod.replace(".", "/")
|
||||
if (f := base.with_suffix(".py")).is_file():
|
||||
return f
|
||||
if (pkg := base / "__init__.py").is_file():
|
||||
return pkg
|
||||
return None
|
||||
|
||||
|
||||
def _transitive_first_party_imports(entry_module: str) -> set[str]:
|
||||
"""Every first-party module reachable from ``entry_module`` via imports.
|
||||
|
||||
Follows ``import``/``from ... import`` edges through first-party modules only,
|
||||
so a reader that pulls a shared comprehension helper three hops deep is still
|
||||
caught — unlike the single-level :func:`_module_imports`.
|
||||
"""
|
||||
seen: set[str] = set()
|
||||
stack = [entry_module]
|
||||
while stack:
|
||||
mod = stack.pop()
|
||||
if mod in seen:
|
||||
continue
|
||||
path = _resolve_module(mod)
|
||||
if path is None:
|
||||
continue
|
||||
seen.add(mod)
|
||||
for imported in _module_imports(path):
|
||||
if _is_first_party(imported) and imported not in seen:
|
||||
stack.append(imported)
|
||||
return seen
|
||||
|
||||
|
||||
def _shared_first_party(a: set[str], b: set[str], neutral: frozenset[str]) -> set[str]:
|
||||
"""First-party modules both sets import, minus the neutral allowlist."""
|
||||
return (a & b) - set(neutral)
|
||||
|
||||
|
||||
def _module_name_of(rel_path: str) -> str:
|
||||
stem = rel_path[:-3] if rel_path.endswith(".py") else rel_path
|
||||
return stem.replace("/", ".")
|
||||
|
||||
|
||||
class TestINV27Tier2ReaderDisjointness:
|
||||
"""The two readers whose agreement the Tier-2 gate trusts share no decoding
|
||||
pathway — so ``verify_tier2_agreement``'s lineage check is load-bearing."""
|
||||
|
||||
def test_emitted_lineages_are_registered(self):
|
||||
"""27a — every reader_lineage a producer emits is registered, so its
|
||||
disjointness is actually proven below (not merely asserted by a string)."""
|
||||
adapters = _REPO_ROOT / "core" / "reasoning" / "adapters.py"
|
||||
tree = ast.parse(adapters.read_text())
|
||||
emitted: set[str] = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Call):
|
||||
for kw in node.keywords:
|
||||
if kw.arg == "reader_lineage" and isinstance(kw.value, ast.Constant):
|
||||
if isinstance(kw.value.value, str):
|
||||
emitted.add(kw.value.value)
|
||||
registered = {r.lineage for r in TIER2_READER_PATHWAYS}
|
||||
unregistered = sorted(emitted - registered)
|
||||
assert not unregistered, (
|
||||
f"Reader(s) emit unregistered Tier-2 lineage(s): {unregistered}. "
|
||||
"Register them in TIER2_READER_PATHWAYS so INV-27c proves they share "
|
||||
"no decoding pathway, or the gate's independence check is unbacked."
|
||||
)
|
||||
|
||||
def test_registered_reader_modules_exist(self):
|
||||
"""27b — drift guard: a registered module that moved makes 27c vacuous."""
|
||||
missing = [r.module for r in TIER2_READER_PATHWAYS
|
||||
if not (_REPO_ROOT / r.module).is_file()]
|
||||
assert not missing, (
|
||||
f"Registered Tier-2 reader module(s) not found: {missing}. "
|
||||
"Update TIER2_READER_PATHWAYS."
|
||||
)
|
||||
|
||||
def test_registered_readers_are_pairwise_import_disjoint(self):
|
||||
"""27c — the load-bearing static guarantee. Any two readers the gate may
|
||||
treat as independent votes import no first-party module in common (except
|
||||
explicit neutral pure-data modules). Distinct lineage ⟹ disjoint pathway."""
|
||||
transitive = {
|
||||
r.lineage: _transitive_first_party_imports(_module_name_of(r.module))
|
||||
for r in TIER2_READER_PATHWAYS
|
||||
}
|
||||
offenders: list[str] = []
|
||||
readers = list(TIER2_READER_PATHWAYS)
|
||||
for i in range(len(readers)):
|
||||
for j in range(i + 1, len(readers)):
|
||||
a, b = readers[i], readers[j]
|
||||
shared = _shared_first_party(
|
||||
transitive[a.lineage], transitive[b.lineage], TIER2_NEUTRAL_MODULES
|
||||
)
|
||||
if shared:
|
||||
offenders.append(
|
||||
f"{a.lineage} ⟂ {b.lineage} share {sorted(shared)}"
|
||||
)
|
||||
assert not offenders, (
|
||||
"Tier-2 readers the gate trusts as independent share a decoding "
|
||||
"pathway:\n " + "\n ".join(offenders)
|
||||
+ "\n\nTheir agreement would be common-mode, not independent (the "
|
||||
"GSM8K blind spot). Re-implement one independently, or — if the shared "
|
||||
"module is genuinely pure data — add it to TIER2_NEUTRAL_MODULES with "
|
||||
"a justification that it is parser/solver-free."
|
||||
)
|
||||
|
||||
def test_disjointness_predicate_is_non_vacuous(self):
|
||||
"""27d — prove 27c can fail and that the neutral allowlist genuinely
|
||||
clears a shared module (CLAUDE.md schema-defined proof obligation)."""
|
||||
a = {"generate.reader_a", "generate.shared_parser", "algebra.versor"}
|
||||
b = {"generate.reader_b", "generate.shared_parser"}
|
||||
# A genuinely shared first-party module is flagged.
|
||||
assert _shared_first_party(a, b, frozenset()) == {"generate.shared_parser"}
|
||||
# Allowlisting that module as neutral clears it.
|
||||
assert _shared_first_party(a, b, frozenset({"generate.shared_parser"})) == set()
|
||||
# The transitive walker really follows edges: math_solver reaches the
|
||||
# problem-graph module it is built on (a multi-hop first-party import).
|
||||
ms = _transitive_first_party_imports("generate.math_solver")
|
||||
assert "generate.math_problem_graph" in ms, (
|
||||
"transitive walker did not follow math_solver -> math_problem_graph; "
|
||||
"the disjointness proof would miss deep shared readers."
|
||||
)
|
||||
|
|
|
|||
112
tests/test_cga_f64_exactness.py
Normal file
112
tests/test_cga_f64_exactness.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
"""Phase 0A — f64-exact conformal embedding + projective read-back + pinned ceiling.
|
||||
|
||||
The field-reasoner wedge encodes quantities as conformal points on the e1 number
|
||||
line and reads the answer back by *projective dehomogenization* — the only exact
|
||||
read-back for weight-changing (dilation) operators. ``embed_point`` was f32-hardcoded
|
||||
(``algebra/cga.py``), which silently destroys integers past ~1e4: at v=12345 the f32
|
||||
``n_o`` weight collapses to 0 and the read-back is unusable.
|
||||
|
||||
These tests pin three contracts:
|
||||
|
||||
1. ``embed_point``'s f32 default is byte-unchanged (no existing caller regresses).
|
||||
2. The new ``dtype=np.float64`` path + ``read_scalar_e1`` recover integer coordinates
|
||||
**exactly** across the whole admissible band, where f32 already fails.
|
||||
3. ``EMBED_EXACT_MAX`` is the pinned magnitude ceiling: exactness is asserted up to it;
|
||||
the field reader refuses above it (the refusal lives in the reader, not here).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from algebra.cga import (
|
||||
EMBED_EXACT_MAX,
|
||||
cga_inner,
|
||||
embed_point,
|
||||
read_scalar_e1,
|
||||
)
|
||||
|
||||
|
||||
def _e1(v: float, dtype: "np.typing.DTypeLike" = np.float64) -> np.ndarray:
|
||||
"""Embed a scalar coordinate on the e1 axis."""
|
||||
return embed_point(np.array([v, 0.0, 0.0]), dtype=dtype)
|
||||
|
||||
|
||||
# --- contract 1: f32 default is byte-unchanged -----------------------------
|
||||
|
||||
|
||||
def test_embed_point_f32_default_unchanged():
|
||||
"""The default path stays float32 and byte-identical to the prior impl."""
|
||||
x = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
||||
X = embed_point(x)
|
||||
assert X.dtype == np.float32
|
||||
# Prior closed-form: result[1:4]=x, e4=0.5(|x|^2-1), e5=0.5(|x|^2+1).
|
||||
x_sq = float(np.dot(x, x)) # 14.0
|
||||
assert X[1] == np.float32(1.0)
|
||||
assert X[2] == np.float32(2.0)
|
||||
assert X[3] == np.float32(3.0)
|
||||
assert X[4] == np.float32(0.5 * (x_sq - 1.0))
|
||||
assert X[5] == np.float32(0.5 * (x_sq + 1.0))
|
||||
|
||||
|
||||
# --- contract 2: f64 exact read-back where f32 fails -----------------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("v", [0, 1, 7, 42, 103, 300, 9999, 12345, 100000, 1000000])
|
||||
def test_read_scalar_e1_exact_f64(v):
|
||||
"""Projective dehomogenization recovers the integer coordinate exactly in f64."""
|
||||
X = _e1(float(v), dtype=np.float64)
|
||||
assert read_scalar_e1(X) == float(v)
|
||||
|
||||
|
||||
def test_f32_readback_fails_where_f64_succeeds():
|
||||
"""The motivating hazard: f32 cannot round-trip a mid-size integer; f64 can.
|
||||
|
||||
A meaningfully-failing guard — if this passes under f32 the f64 work is moot.
|
||||
"""
|
||||
v = 12345.0
|
||||
f64 = read_scalar_e1(_e1(v, dtype=np.float64))
|
||||
assert f64 == v
|
||||
Xf32 = _e1(v, dtype=np.float32)
|
||||
denom = float(Xf32[5] - Xf32[4]) # n_o weight collapses toward 0 in f32
|
||||
assert denom != 1.0 # the f32 weight is already wrong at this scale
|
||||
|
||||
|
||||
def test_dilation_weighted_point_readback():
|
||||
"""Read-back is exact for a *weighted* (dilated) null vector, not just unit weight.
|
||||
|
||||
A dilation about the origin by factor k scales the whole null vector; the e1
|
||||
coordinate must come back as k*v via the e1/n_o-weight ratio, never as a raw
|
||||
distance-from-origin.
|
||||
"""
|
||||
v, k = 2.0, 4.0
|
||||
X = _e1(v, dtype=np.float64)
|
||||
Xw = (k * X).astype(np.float64) # uniform conformal weight k
|
||||
assert read_scalar_e1(Xw) == v # projective: weight divides out
|
||||
|
||||
|
||||
# --- contract 3: the pinned ceiling ----------------------------------------
|
||||
|
||||
|
||||
def test_embed_exact_max_is_pinned_and_generous():
|
||||
"""The ceiling is a concrete int, comfortably above any GSM8K quantity."""
|
||||
assert isinstance(EMBED_EXACT_MAX, int)
|
||||
assert EMBED_EXACT_MAX >= 1_000_000
|
||||
|
||||
|
||||
def test_distance_exact_within_ceiling():
|
||||
"""The conformal distance metric stays exact for integer pairs up to the ceiling.
|
||||
|
||||
cga_inner(embed(a), embed(b)) = -1/2 (a-b)^2, computed in f64.
|
||||
"""
|
||||
for a, b in [(0, 1), (3, 7), (100, 103), (0, EMBED_EXACT_MAX)]:
|
||||
inner = cga_inner(_e1(float(a)), _e1(float(b)))
|
||||
expected = -0.5 * (a - b) ** 2
|
||||
assert inner == expected, f"a={a} b={b}: {inner} != {expected}"
|
||||
|
||||
|
||||
def test_embedded_f64_point_is_null():
|
||||
"""f64 embedding still lands on the null cone."""
|
||||
X = _e1(123.0, dtype=np.float64)
|
||||
assert abs(cga_inner(X, X)) < 1e-6
|
||||
|
|
@ -12,6 +12,7 @@ from core.reasoning import (
|
|||
COMMITMENT_DISAGREEMENT,
|
||||
DUPLICATE_STRUCTURAL_SIGNATURE,
|
||||
MISSING_COMMITMENT,
|
||||
SAME_READER_LINEAGE,
|
||||
TIER2_VERIFIED,
|
||||
EvidenceBundle,
|
||||
OperatorEvidence,
|
||||
|
|
@ -24,6 +25,7 @@ def _ev(
|
|||
signature: str = "shape-a",
|
||||
commitment: str = "answer:42",
|
||||
outcome: str = "verified",
|
||||
lineage: str = "reader-a",
|
||||
) -> OperatorEvidence:
|
||||
return OperatorEvidence(
|
||||
domain="mathematics_logic",
|
||||
|
|
@ -34,6 +36,7 @@ def _ev(
|
|||
check_keys=("check-a",),
|
||||
commitment_key=commitment,
|
||||
structural_signature=signature,
|
||||
reader_lineage=lineage,
|
||||
payload={"nested": {"values": [1, 2, "x"]}},
|
||||
)
|
||||
|
||||
|
|
@ -64,19 +67,35 @@ def test_evidence_bundle_hash_is_ordered_and_stable() -> None:
|
|||
|
||||
def test_tier2_verifies_two_distinct_structures_same_commitment() -> None:
|
||||
verdict = verify_tier2_agreement((
|
||||
_ev(signature="shape-a"),
|
||||
_ev(signature="shape-b"),
|
||||
_ev(signature="shape-a", lineage="reader-a"),
|
||||
_ev(signature="shape-b", lineage="reader-b"),
|
||||
))
|
||||
assert verdict.verified is True
|
||||
assert verdict.reason == TIER2_VERIFIED
|
||||
assert verdict.commitment_key == "answer:42"
|
||||
assert verdict.structural_signatures == ("shape-a", "shape-b")
|
||||
assert verdict.reader_lineages == ("reader-a", "reader-b")
|
||||
|
||||
|
||||
def test_tier2_refuses_same_reader_lineage() -> None:
|
||||
"""The decoration firewall: one reader cannot self-verify by relabeling its
|
||||
structural signature. Two evidences from the SAME decoding pathway are refused
|
||||
even when their structural_signatures differ and the commitment agrees.
|
||||
|
||||
Meaningfully-failing guard: under the old label-only check this admitted.
|
||||
"""
|
||||
verdict = verify_tier2_agreement((
|
||||
_ev(signature="shape-a", lineage="one-reader"),
|
||||
_ev(signature="shape-b", lineage="one-reader"),
|
||||
))
|
||||
assert verdict.verified is False
|
||||
assert verdict.reason == SAME_READER_LINEAGE
|
||||
|
||||
|
||||
def test_tier2_refuses_duplicate_signature() -> None:
|
||||
verdict = verify_tier2_agreement((
|
||||
_ev(signature="same"),
|
||||
_ev(signature="same"),
|
||||
_ev(signature="same", lineage="reader-a"),
|
||||
_ev(signature="same", lineage="reader-b"),
|
||||
))
|
||||
assert verdict.verified is False
|
||||
assert verdict.reason == DUPLICATE_STRUCTURAL_SIGNATURE
|
||||
|
|
@ -84,8 +103,8 @@ def test_tier2_refuses_duplicate_signature() -> None:
|
|||
|
||||
def test_tier2_refuses_disagreeing_commitments() -> None:
|
||||
verdict = verify_tier2_agreement((
|
||||
_ev(signature="shape-a", commitment="answer:42"),
|
||||
_ev(signature="shape-b", commitment="answer:41"),
|
||||
_ev(signature="shape-a", commitment="answer:42", lineage="reader-a"),
|
||||
_ev(signature="shape-b", commitment="answer:41", lineage="reader-b"),
|
||||
))
|
||||
assert verdict.verified is False
|
||||
assert verdict.reason == COMMITMENT_DISAGREEMENT
|
||||
|
|
|
|||
Loading…
Reference in a new issue