Merge pull request 'feat(adr-0249): reader→Hamiltonian compiler — design spike + P1 quantity kernel' (#66) from feat/reader-hamiltonian-compiler into main
Reviewed-on: #66
This commit is contained in:
commit
aca98d857d
3 changed files with 532 additions and 0 deletions
119
core/physics/quantity_kernel.py
Normal file
119
core/physics/quantity_kernel.py
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
"""core.physics.quantity_kernel — conformal quantity encoding (ADR-0249 P1).
|
||||||
|
|
||||||
|
Substrate-native representation of real quantities as null points on the
|
||||||
|
Cl(4,1) conformal line, with affine transport (add/scale by known constants)
|
||||||
|
realized as versor sandwiches. This is the standing hand the reader→Hamiltonian
|
||||||
|
compiler builds on: numbers live on the field before arithmetic relations can
|
||||||
|
become field constraints.
|
||||||
|
|
||||||
|
Encoding (spike §3, verified against algebra/cl41.py):
|
||||||
|
n_inf = e4 + e5, n_o = ½(e5 − e4) (null basis of the line)
|
||||||
|
P(q) = n_o + q·e1 + ½q²·n_inf (null point at coordinate q)
|
||||||
|
|
||||||
|
Transport (exact, versor-native):
|
||||||
|
translate by a: T_a = 1 − ½ a·e1·n_inf, T_a P(b) T̃_a = P(a+b)
|
||||||
|
scale by e^{−α}: D_α = exp(+½ α·e4e5), D_α P(b) D̃_α = w·P(e^{−α}·b)
|
||||||
|
|
||||||
|
Dilation carries a conformal weight w ≠ 1, so transported targets are decoded
|
||||||
|
*projectively* — q = e1-coeff / (e5-coeff − e4-coeff) — which is scale-invariant.
|
||||||
|
The corridor requires unit-norm states, so relation compilation (P2) normalizes
|
||||||
|
before building the well; projective decode makes that lossless.
|
||||||
|
|
||||||
|
Reproducibility (spike §4.6, Tier 2): every construction is explicit float64.
|
||||||
|
`algebra.cl41.geometric_product` silently truncates to float32 unless handed
|
||||||
|
f64 arrays; this module never lets that happen. Serve-quarantined (A-04):
|
||||||
|
`core/physics/` is never imported by `chat/runtime.py`.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from algebra import cl41 as cl
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"QuantityKernelError",
|
||||||
|
"embed_quantity",
|
||||||
|
"translate_quantity",
|
||||||
|
"dilate_quantity",
|
||||||
|
"decode_quantity",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Grade-1 component indices (e1..e5 occupy indices 1..5 in the blade ordering).
|
||||||
|
_E1, _E4, _E5 = 1, 4, 5
|
||||||
|
|
||||||
|
# Minimum |conformal weight| below which projective decode is undefined.
|
||||||
|
_MIN_WEIGHT = 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
class QuantityKernelError(ValueError):
|
||||||
|
"""Typed, fail-closed refusal for the quantity kernel (no guessed values)."""
|
||||||
|
|
||||||
|
|
||||||
|
def _e(i: int) -> np.ndarray:
|
||||||
|
"""i-th basis vector (0-indexed e1..e5) as an f64 multivector."""
|
||||||
|
return cl.basis_vector(i).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
# Null basis of the conformal line, built once in f64.
|
||||||
|
_N_INF = _e(3) + _e(4) # e4 + e5
|
||||||
|
_N_O = 0.5 * (_e(4) - _e(3)) # ½(e5 − e4)
|
||||||
|
_E1_NINF = cl.geometric_product(_e(0), _N_INF) # e1·n_inf, for the translator
|
||||||
|
_E4E5 = cl.geometric_product(_e(3), _e(4)) # e4e5, generator of the dilator
|
||||||
|
|
||||||
|
|
||||||
|
def _f64(value: float, *, what: str) -> float:
|
||||||
|
v = float(value)
|
||||||
|
if not math.isfinite(v):
|
||||||
|
raise QuantityKernelError(f"{what}_not_finite")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def _sandwich(versor: np.ndarray, point: np.ndarray) -> np.ndarray:
|
||||||
|
"""versor · point · reverse(versor), all in f64."""
|
||||||
|
v = np.asarray(versor, dtype=np.float64)
|
||||||
|
p = np.asarray(point, dtype=np.float64)
|
||||||
|
return cl.geometric_product(cl.geometric_product(v, p), cl.reverse(v))
|
||||||
|
|
||||||
|
|
||||||
|
def embed_quantity(q: float) -> np.ndarray:
|
||||||
|
"""Null point P(q) = n_o + q·e1 + ½q²·n_inf as an f64 (32,) multivector."""
|
||||||
|
qf = _f64(q, what="quantity")
|
||||||
|
return (_N_O + qf * _e(0) + 0.5 * qf * qf * _N_INF).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def translate_quantity(point: np.ndarray, shift: float) -> np.ndarray:
|
||||||
|
"""Add a known constant: T_a P(b) T̃_a = P(a+b), exact and weight-preserving."""
|
||||||
|
a = _f64(shift, what="shift")
|
||||||
|
translator = np.zeros(cl.N_COMPONENTS, dtype=np.float64)
|
||||||
|
translator[0] = 1.0
|
||||||
|
translator = translator - 0.5 * a * _E1_NINF
|
||||||
|
return _sandwich(translator, point).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def dilate_quantity(point: np.ndarray, alpha: float) -> np.ndarray:
|
||||||
|
"""Scale by e^{−α}: D_α = exp(+½α·e4e5). Carries a conformal weight (decode projectively)."""
|
||||||
|
a = _f64(alpha, what="alpha")
|
||||||
|
half = 0.5 * a
|
||||||
|
dilator = np.zeros(cl.N_COMPONENTS, dtype=np.float64)
|
||||||
|
dilator[0] = math.cosh(half)
|
||||||
|
dilator = dilator + math.sinh(half) * _E4E5
|
||||||
|
return _sandwich(dilator, point).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def decode_quantity(point: np.ndarray) -> float:
|
||||||
|
"""Projective (scale-invariant) recovery of q from a null point.
|
||||||
|
|
||||||
|
q = e1-coeff / (e5-coeff − e4-coeff). Refuses when the conformal weight is
|
||||||
|
degenerate — an unweighted direction has no finite line coordinate.
|
||||||
|
"""
|
||||||
|
arr = np.asarray(point, dtype=np.float64)
|
||||||
|
if arr.shape != (cl.N_COMPONENTS,):
|
||||||
|
raise QuantityKernelError("point_bad_shape")
|
||||||
|
if not np.all(np.isfinite(arr)):
|
||||||
|
raise QuantityKernelError("point_not_finite")
|
||||||
|
weight = float(arr[_E5]) - float(arr[_E4])
|
||||||
|
if abs(weight) < _MIN_WEIGHT:
|
||||||
|
raise QuantityKernelError("degenerate_conformal_weight")
|
||||||
|
return float(arr[_E1]) / weight
|
||||||
274
docs/research/reader-hamiltonian-compiler-spike-2026-07-18.md
Normal file
274
docs/research/reader-hamiltonian-compiler-spike-2026-07-18.md
Normal file
|
|
@ -0,0 +1,274 @@
|
||||||
|
# Reader → Hamiltonian Compiler — Design Spike
|
||||||
|
|
||||||
|
**Status**: COMPLETE — design ready for review; all bindings verified against the live tree,
|
||||||
|
algebra claims verified in-tree. Implementation (P1–P5) starts after the §8 rulings.
|
||||||
|
**Date**: 2026-07-18
|
||||||
|
**Base**: `forgejo/main @ 6ff73aa7` (intelligence-loop arc merged; ADR-0246/0247/0248 Accepted)
|
||||||
|
**Branch**: `feat/reader-hamiltonian-compiler`
|
||||||
|
**Provenance**: Next-arc candidate #1 from `docs/handoff/ADR-0246-Acceptance-Evidence.md`; the
|
||||||
|
generalized-lift instrument recorded GSM8K/NL non-ingestibility as the composition frontier —
|
||||||
|
this spike designs the mechanism that crosses it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Problem statement
|
||||||
|
|
||||||
|
The corridor can solve what it can ingest, and it can ingest almost nothing. Today's compilable
|
||||||
|
domains are (a) propositional entailment over ≤5 atoms and (b) quadratic wells around a known
|
||||||
|
target versor. Real problems — GSM8K arithmetic, multi-step deduction — have no path onto the
|
||||||
|
substrate. The measurement harness (Phase 4 instrument) is built and waiting; the compiler is
|
||||||
|
the missing feeder. This is the recorded composition wall from the GSM8K learning history
|
||||||
|
finally getting its mechanism.
|
||||||
|
|
||||||
|
**Done-when (spike)**: a ratifiable design that (1) compiles real reader output into corridor
|
||||||
|
Hamiltonians deterministically, (2) provably encodes *relations, not answers* (anti-hollow
|
||||||
|
invariant, §4.1), (3) reuses the merged Ring-1/2/3 machinery for composition, and (4) names its
|
||||||
|
governing ADR obligations line-by-line. Implementation phases follow only after the design is
|
||||||
|
reviewed.
|
||||||
|
|
||||||
|
## 2. Design thesis: composition = certified turn sequences
|
||||||
|
|
||||||
|
The wall is COMPOSITION (ADR-0191/0192/0193 lesson: single-layer widening is metric-inert).
|
||||||
|
The freshly merged arc supplies exactly the composition machinery: per-turn
|
||||||
|
RelaxationCertificates, egress routing, the Ring-2 residual protocol's replay-verified path
|
||||||
|
ledger, Ring-3 handoff, and the S3 tether. Therefore:
|
||||||
|
|
||||||
|
> **The compiler does not compile a problem into one big Hamiltonian. It compiles a problem
|
||||||
|
> into a *turn program* — an ordered DAG of small relation-Hamiltonians, each solvable in one
|
||||||
|
> lifecycle turn, chained through the Ring-2 path ledger.**
|
||||||
|
|
||||||
|
- Each turn's H is ≤ the substrate's native 32-dim space (the ≤5-atom limit *is* 2⁵ = 32 —
|
||||||
|
the truth-table basis saturates the Cl(4,1) blade basis; this is a feature to design around,
|
||||||
|
not an accident).
|
||||||
|
- Composition depth lives in the *certified chain*, not in matrix size. Multi-step problems
|
||||||
|
become multi-turn programs whose end-to-end integrity is the path ledger's job — the same
|
||||||
|
ledger that already discriminates `port:identity:d_stab>epsilon_turn`.
|
||||||
|
- This unifies the two flagship domains under one design: arithmetic chains (each step = one
|
||||||
|
relation well) and >5-atom deduction (ROBDD-partitioned ≤5-atom slices per turn).
|
||||||
|
|
||||||
|
## 3. Substrate-native quantity encoding (the standing hand)
|
||||||
|
|
||||||
|
Numbers must live on the field before arithmetic relations can be field constraints. The
|
||||||
|
CGA-native choice — no bespoke machinery — is the conformal embedding of the real line:
|
||||||
|
|
||||||
|
- **Quantity**: `q ↦ P(q) = e_o + q·e₁ + ½q²·e_∞` (null point on the line's horosphere).
|
||||||
|
- **Addition by a known constant**: translator versor `T_a = 1 − ½ a e₁e_∞`;
|
||||||
|
`T_a P(b) T̃_a = P(a + b)` — exact, versor-native.
|
||||||
|
- **Scaling by a known constant**: dilator versor `D_s`; `D_s P(b) D̃_s = P(s·b)` — exact.
|
||||||
|
- **Subtraction / division**: inverse versors of the above.
|
||||||
|
- **Relation as constraint**: "x = a·y + b" compiles to a quadratic well whose minimum is the
|
||||||
|
versor-transported target — i.e. the *existing* quadratic-well machinery centered on
|
||||||
|
`(T_b D_a) ψ_y (T_b D_a)~`, never on a precomputed number.
|
||||||
|
- **Weight honesty**: versor transport of null points is exact up to conformal weight (dilators
|
||||||
|
rescale the point's overall factor), and the corridor requires unit-norm states
|
||||||
|
(`compile_quadratic_well` refuses `target_not_unit` at 1e-9). The compiler therefore
|
||||||
|
normalizes transported targets, and quantity *decode is projective* — `q` is recovered from
|
||||||
|
coefficient ratios (e₁-coefficient over e_o-coefficient), which are scale-invariant, so
|
||||||
|
normalization loses nothing.
|
||||||
|
|
||||||
|
Scope honesty (Tier 1): products of two *unknowns* are not versor operations. Tier-1 scope is
|
||||||
|
**affine DAGs** — each step multiplies/divides by problem-text constants and adds/subtracts
|
||||||
|
known or previously-certified quantities. This covers the dominant GSM8K step shape; the
|
||||||
|
non-affine remainder is recorded, not silently capped (§6 risks).
|
||||||
|
|
||||||
|
**Verified in-tree (2026-07-18)**: all four algebra claims checked numerically against
|
||||||
|
`algebra/cl41.py`'s own multiplication table (signature `(+,+,+,+,−)`, basis e1..e5;
|
||||||
|
`n_∞ = e4+e5`, `n_o = ½(e5−e4)`), 200 random cases, q ∈ [−50, 50]:
|
||||||
|
nullity `|P(q)²| < 3e-10`; translator exactness `|T_a P(b) T̃_a − P(a+b)| < 2e-10` (f64
|
||||||
|
rounding only — the identity is algebraically exact, translators preserve weight); dilator
|
||||||
|
`D = exp(+α/2·e4e5)` scales `s = e^{−α}` (sign convention pinned here) with conformal weight
|
||||||
|
rescale confirmed (w ≈ 2.01 ≠ 1 at α=0.7 — normalization required); projective decode
|
||||||
|
`q = e1c/(e5c − e4c)` scale-invariant to < 1e-9.
|
||||||
|
|
||||||
|
## 4. Design invariants (pins before code)
|
||||||
|
|
||||||
|
### 4.1 Relation-not-answer (anti-hollow)
|
||||||
|
The compiler must never evaluate the arithmetic or logic it encodes. If the compiler computes
|
||||||
|
`a+b` to center a well, the corridor confirms rather than solves — a hollow instrument.
|
||||||
|
Enforcement: (i) purity pin — no arithmetic on problem quantities in the compiler module
|
||||||
|
beyond versor construction; (ii) property test — compiled artifacts for a relation are
|
||||||
|
invariant to which variable is designated unknown; (iii) ablation test — corrupting the
|
||||||
|
relaxation changes the answer while compiler output is bit-identical.
|
||||||
|
|
||||||
|
### 4.2 Determinism + content addressing
|
||||||
|
Compiled Hamiltonians are frozen, content-addressed per ADR-0245 §2.3/§2.4 (full SHA-256,
|
||||||
|
canonical JSON, explicit `<f8` LE bytes; `_cached_eigh` keying unchanged). Same parse ⇒ same
|
||||||
|
`hamiltonian_id`, byte-identical.
|
||||||
|
|
||||||
|
### 4.3 Fail-closed compilation
|
||||||
|
Unsupported relation shapes, unparseable steps, or out-of-scope structures refuse with typed
|
||||||
|
errors in the *existing* `HamiltonianCompileError` family (`cognitive_lifecycle.py` already
|
||||||
|
carries the taxonomy: `bad_shape`, `not_symmetric`, `target_not_unit`, `atom_count_out_of_range`,
|
||||||
|
…) — extend that family upstream rather than inventing a parallel one. Never a guessed or
|
||||||
|
partial Hamiltonian.
|
||||||
|
|
||||||
|
### 4.4 Reader boundary (fix upstream)
|
||||||
|
The compiler consumes the existing readers' *typed* output only. If a reader's output shape is
|
||||||
|
insufficient, extend the reader's typed surface upstream — the compiler never parses raw text.
|
||||||
|
|
||||||
|
### 4.5 Governance
|
||||||
|
Evals-quarantined (A-04): the compiler and turn-program executor live off-serving; serving
|
||||||
|
consumes nothing from this arc. Sealed practice vs serving split per ADR-0175; wrong=0 guard
|
||||||
|
held on the deductive flagship; honest-NULL protocol on the instrument; proposal-only learning
|
||||||
|
(I-03) untouched.
|
||||||
|
|
||||||
|
### 4.6 Byte-identity & cross-hardware reproducibility (three tiers, honestly bounded)
|
||||||
|
The determinism claim splits into three tiers; only the first two are unconditionally
|
||||||
|
achievable, and the design says so rather than promising universal bit-identity.
|
||||||
|
|
||||||
|
- **Tier 1 — content address: SOLVED, unconditional.** `hamiltonian_id` = SHA-256 over
|
||||||
|
canonical JSON + explicit `<f8` LE matrix bytes (ADR-0244 §2.7). Endianness — the one real
|
||||||
|
architecture variable in hashing — is already coerced. Same matrix ⇒ same id on any machine.
|
||||||
|
- **Tier 2 — matrix construction: ACHIEVABLE, enforced in P1.** `algebra.cl41.geometric_product`
|
||||||
|
(`:108-125`) is a fixed-order scatter-add (`result[idx] += sign·aᵢ·bⱼ`, canonical i,j order),
|
||||||
|
no BLAS / `dot` / reduction ⇒ IEEE-754 correctly-rounded and hardware-deterministic. The
|
||||||
|
translator/dilator identities have integer/half-integer coefficients, so well *centers* are
|
||||||
|
bit-clean (verified §3). **Trap:** `geometric_product` and `reverse` silently fall back to
|
||||||
|
**float32** unless handed f64 arrays (`:110-112`, `:134-135`). P1 constructs every versor and
|
||||||
|
sandwich in explicit f64, and pins the result with **golden-bytes tests** — the frozen
|
||||||
|
SHA-256 of a canonical compiled-Hamiltonian set as literal constants, so any numpy / toolchain
|
||||||
|
/ hardware drift in the last bit fails the test loudly (same discipline as the biography
|
||||||
|
`<f8` hash pin and the `_cached_eigh` memoization tests).
|
||||||
|
- **Tier 3 — eigensolve → answer: GENUINELY hardware-bounded; do not overclaim.** The affine
|
||||||
|
well `H = c(Id − ψψᵀ)` is dense, so `relax_to_ground` takes the LAPACK branch
|
||||||
|
(`cognitive_lifecycle.py:590-593`). Its excited eigenspace is **31-fold degenerate** (all at
|
||||||
|
energy c), and LAPACK fills that basis arbitrarily per build ⇒ `evecs`/`propagator` **bytes
|
||||||
|
are not cross-hardware identical**. Mitigation, in order:
|
||||||
|
1. **Analytic ground recovery.** The compiler *constructs* the target ψ (versor transport),
|
||||||
|
so the corridor's job is to *confirm* relaxation reaches span(ψ), not to discover it. The
|
||||||
|
converged steady state projects onto the unique 1-D ground space, so the degenerate-basis
|
||||||
|
arbitrariness cancels in the limit; pin the **basis-invariant** `phase_correlation/2`
|
||||||
|
agreement to ψ, never the propagator bytes.
|
||||||
|
2. Where a future non-affine tier yields a genuinely dense non-degenerate H, record
|
||||||
|
cross-hardware eigen-reproducibility as an explicit limitation (no-silent-caps) and pin
|
||||||
|
the **decision** (`_certified` already uses tolerances, not bit-equality), not the bytes.
|
||||||
|
|
||||||
|
## 5. In-tree inventory (survey bindings)
|
||||||
|
|
||||||
|
### 5.1 Hamiltonian construction contract (verified)
|
||||||
|
|
||||||
|
- `ProblemHamiltonian` (`core/physics/cognitive_lifecycle.py:262-302`): frozen 32×32
|
||||||
|
real-symmetric f64; refuses `bad_shape` / `non_finite_matrix` / `not_symmetric` (≤1e-12,
|
||||||
|
never symmetrized); `hamiltonian_id` = SHA-256 of canonical JSON over domain + LE-f64 matrix
|
||||||
|
bytes + stringified metadata; `is_diagonal` auto-derived → diagonal compilers get the
|
||||||
|
no-LAPACK fast path for free (`relax_to_ground` :581-588).
|
||||||
|
- `compile_quadratic_well` (`:305-319`): `H = curvature·(Id − ψ₀ψ₀ᵀ)`; refuses
|
||||||
|
`target_not_unit` (1e-9) — hence §3's normalize-then-projective-decode.
|
||||||
|
- `compile_propositional` (`:382-401`) + `PropositionalProblem` (`:326-361`, `_MAX_ATOMS = 5`
|
||||||
|
at `:84`): diagonal falsification-count H over the 2⁵ = 32 assignment↔blade bijection
|
||||||
|
derived through real geometric products (`_build_subset_component_map` `:183-203`).
|
||||||
|
`propositional_entails` (`:714-750`) reads exact integer ground energies — no eigensolve.
|
||||||
|
- `_cached_eigh` (`:512-530`): LRU keyed `(hamiltonian_id, matrix bytes)`; ADR-0245 §3 perf
|
||||||
|
gate (0 allocations / 0 LAPACK on repeated static H) binds compiler emissions.
|
||||||
|
- Sensorium compilers emit unit-versor *field states*, never Hamiltonians; the only H built
|
||||||
|
from sensorium data is a quadratic well on the compiled percept
|
||||||
|
(`evals/adr_0243_cognitive_lifecycle/__init__.py:114`).
|
||||||
|
|
||||||
|
### 5.2 Governing authorities (cite in the ADR)
|
||||||
|
|
||||||
|
- **ADR-0243 §2.2** — the definition this compiler implements: problem constraints formulated
|
||||||
|
as potential wells in H_problem; "zero room for intermediate fabrication". §2.4/§4.2 —
|
||||||
|
one-mutation-path; speculative H changes quarantined in `evals/` until signed certificate.
|
||||||
|
- **ADR-0244 §2.5/§2.7/§2.8** — f64 relaxation, full-SHA content addressing, frozen H.
|
||||||
|
- **ADR-0245 §2.2–2.4, §3** — serving-boundary cast, `_cached_eigh`, perf gate.
|
||||||
|
- **ADR-0012** — deterministic ingest governance; an LLM/D3 oracle upstream of the gate is
|
||||||
|
explicitly rejected → the compiler's text side must ride the existing deterministic readers.
|
||||||
|
- **ADR-0247 §1 props 3–6** — purity/fail-closed/replay obligations when compiler outputs
|
||||||
|
flow through ports. ADR-0246/0247/0248 are otherwise silent on problem-domain expansion
|
||||||
|
(no conflict). AGENTS.md:99 — no stochastic generation inside the deterministic path.
|
||||||
|
- **ADR-0175 / 0191 / 0192 / 0193** — eval-entry law: sealed-practice vs serving split;
|
||||||
|
completeness obligation (every source quantity consumed or refuse); class-not-list firewall;
|
||||||
|
composition wall (single-layer widening is metric-inert). "ADR-0190" is citation-only —
|
||||||
|
no such file exists; cite the instrument-before-consumption lesson via ADR-0193.
|
||||||
|
- Numbering: next free ADR number is **0249** (0240–0248 collision-free; re-check at landing).
|
||||||
|
|
||||||
|
### 5.3 Prior art this arc builds ON (reconciliation required)
|
||||||
|
|
||||||
|
- **ADR-0217 R2 finite-integer constraint compiler (RATIFIED, BUILT, off-serving)** —
|
||||||
|
`problem text → entities → quantities → relations → CONSTRAINTS → goal → solver → verifier`.
|
||||||
|
The reader→Hamiltonian compiler should be designed as a **constraint-IR → Hamiltonian
|
||||||
|
backend** behind this ratified front-end, not a new text parser (§4.4 fix-upstream).
|
||||||
|
- **`docs/research/field-reasoner-wedge-selection.md`** — standing two-compiler independence
|
||||||
|
design: §6 symbolic compiler, §7 field compiler MUST NOT import the symbolic one, §5 oracle
|
||||||
|
imports neither, §10 GSM8K re-entry rule, §13 stop conditions; chosen wedge =
|
||||||
|
ratio/proportion; state: 0A–0C green, 0D+W next. This arc must either continue that wedge
|
||||||
|
or explicitly absorb it — not silently fork it (§8 decision).
|
||||||
|
- **`docs/research/independent-comprehension-agreement-gate.md` §3/§7** — two independent
|
||||||
|
readers must construct the same canonical structure before any field-backed promotion.
|
||||||
|
Resolution honoring both constraints: the Hamiltonian backend consumes the *post-agreement
|
||||||
|
canonical structure*, so reader independence lives upstream of it and the backend imports
|
||||||
|
neither reader.
|
||||||
|
|
||||||
|
### 5.4 Reader stack bindings (verified)
|
||||||
|
|
||||||
|
- **Arithmetic IR (Tier-1 input)**: Reader A — `generate/math_candidate_graph.py:589`
|
||||||
|
`parse_and_solve` → `MathProblemGraph` (`generate/math_problem_graph.py:449`:
|
||||||
|
`entities / initial_state / operations / unknown`, typed `Operation` kinds, canonical
|
||||||
|
bytes). This is the affine DAG §3 compiles. **Reader B (`generate/derivation/pool.py:69`
|
||||||
|
`resolve_pooled`) is sealed-practice only and MUST NOT feed the compiler** (standing
|
||||||
|
two-reader disjointness; no bridge without sealed wrong=0).
|
||||||
|
- **Deduction IR (Tier-1 input)**: `generate/meaning_graph/reader.py:330` `comprehend`
|
||||||
|
(NL prose, refusal-first) → `projectors.py:153` `to_deductive_logic` → `(premises, query)`
|
||||||
|
formula strings. The corridor side consumes `PropositionalProblem` (CNF tuples, ≤5 atoms).
|
||||||
|
**The only missing piece on this leg is a structural formula→CNF converter** —
|
||||||
|
syntax-directed distribution over the restricted grammar with typed budget/atom-count
|
||||||
|
refusals, never truth-table construction (which would evaluate, not translate). Today the
|
||||||
|
bridge exists only ad hoc and in reverse (`evals/adr_0243_cognitive_lifecycle/
|
||||||
|
propositional_falsifier.py`, CNF → formula strings).
|
||||||
|
- **In-tree precedent for §3's mechanism**: the field wedge's
|
||||||
|
`generate/relational_field_reader.py:125` already reads relational problems into CGA
|
||||||
|
*translator versors* (import-disjoint from its symbolic control arm, INV-27). The quantity
|
||||||
|
kernel generalizes this proven mechanism into the corridor; the wedge's own 0D+W trajectory
|
||||||
|
continues unaffected, and any future merge is an explicit ADR decision, not silent
|
||||||
|
absorption.
|
||||||
|
- Every reader in the tree is refusal-first with typed refusal vocabularies and frozen-slots
|
||||||
|
canonical outputs — the compiler inherits a uniform, well-typed upstream surface.
|
||||||
|
|
||||||
|
## 6. Risks (honest register)
|
||||||
|
|
||||||
|
| Risk | Level | Handling |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| Turn-program chaining exposes drift the single-turn arc never exercised | Medium — this is *the point* | The path ledger + tether are the instruments; failures here are findings, not embarrassments |
|
||||||
|
| Tier-1 affine scope covers less of GSM8K than expected | Medium | Coverage measured on the real holdout slice and RECORDED (no silent caps); non-affine remainder scopes Tier 2 |
|
||||||
|
| Anti-hollow invariant leaks (compiler smuggles evaluation) | High-harm if missed | §4.1 triple enforcement: purity pin + invariance property + ablation test |
|
||||||
|
| Quantity decode needs vocab the manifold lacks | Low | Quantity readback decodes null points geometrically, not via word vocab; word-level articulation of results remains gated by vocab coverage (separate arc) |
|
||||||
|
| Instrument shows corridor NULL vs symbolic evaluator baseline | High-likelihood, low-harm | Honest NULL recorded; the instrument exists to make this decidable |
|
||||||
|
|
||||||
|
## 7. Phase plan (post-review)
|
||||||
|
|
||||||
|
- **P1 — Quantity kernel** (`core/physics/`): line embedding `P(q)`, translator/dilator
|
||||||
|
construction, normalize + projective decode; property tests for exactness (§3's verified
|
||||||
|
claims become pinned tests), weight-rescale honesty, refusals on non-finite q.
|
||||||
|
- **P2 — Relation compiler v0**: affine relation → versor-transported, normalized
|
||||||
|
quadratic-well `ProblemHamiltonian` (reusing `compile_quadratic_well`); typed refusals in
|
||||||
|
the `HamiltonianCompileError` family for non-affine/out-of-scope relations.
|
||||||
|
- **P3 — Formula→CNF converter** (deduction leg): syntax-directed CNF over the restricted
|
||||||
|
grammar → `PropositionalProblem`; typed refusals `atom_count_out_of_range` (>5) /
|
||||||
|
clause-budget; closes the prose → `comprehend` → `to_deductive_logic` → CNF → corridor path.
|
||||||
|
- **P4 — Turn-program compiler + executor** (evals quarantine): `MathProblemGraph` → ordered
|
||||||
|
turn program (one relation well per step, previously-certified quantities as transported
|
||||||
|
targets); executor chains lifecycle turns through the Ring-2 residual protocol (per-turn
|
||||||
|
certificates, replay-verified path ledger); tether readings per §S3.
|
||||||
|
- **P5 — Instrument integration**: arithmetic-chain domain added to the generalized-lift
|
||||||
|
instrument; **real GSM8K holdout slice, never the 150 templated cases**; baseline = the
|
||||||
|
symbolic evaluator over the same `MathProblemGraph`; honest-NULL protocol; Tier-1 affine
|
||||||
|
coverage of the slice measured and RECORDED.
|
||||||
|
- **P6 (next arc, not this one)** — >5-atom deduction via ROBDD-partitioned turn programs.
|
||||||
|
|
||||||
|
Each phase: own PR, smoke-gated, TDD-first. New machinery ⇒ **ADR-0249** (Proposed;
|
||||||
|
re-verify number at landing), acceptance evidence assembled as in the intelligence-loop arc;
|
||||||
|
no self-Accept.
|
||||||
|
|
||||||
|
## 8. Rulings (RESOLVED 2026-07-18)
|
||||||
|
|
||||||
|
All four APPROVED by Shay:
|
||||||
|
1. **Tier-1 = affine-only.** Unknown×unknown deferred; keep the versor story exact.
|
||||||
|
2. **>5-atom deduction = next arc**, via ROBDD-partitioned turn programs. NOTE the honest
|
||||||
|
reason: this is a *sequencing* choice (one capability per arc), **not** a claim that
|
||||||
|
>5-atom deduction is impossible or needs probabilistic approximation — composition handles
|
||||||
|
it deterministically and exactly. The 32-blade ceiling only forbids doing it *natively in
|
||||||
|
one Hamiltonian*; the certified turn chain crosses it losslessly.
|
||||||
|
3. **One ADR (0249)** for quantity kernel + relation compiler + CNF converter + turn programs.
|
||||||
|
4. **Field wedge continues independently** (0D+W); this arc generalizes the same versor
|
||||||
|
mechanism into the corridor; compiler imports neither wedge arm (INV-27 intact); any merge
|
||||||
|
is a later explicit ADR.
|
||||||
139
tests/test_adr_0249_quantity_kernel.py
Normal file
139
tests/test_adr_0249_quantity_kernel.py
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
"""ADR-0249 P1 — conformal quantity kernel pins.
|
||||||
|
|
||||||
|
Verifies the substrate-native encoding of quantities as null points on the
|
||||||
|
Cl(4,1) conformal line, and affine transport by translator/dilator versors.
|
||||||
|
Every claim here was first checked numerically against algebra/cl41.py's own
|
||||||
|
multiplication table in the design spike
|
||||||
|
(docs/research/reader-hamiltonian-compiler-spike-2026-07-18.md §3); these are
|
||||||
|
the pinned, permanent form.
|
||||||
|
|
||||||
|
Reproducibility discipline (spike §4.6):
|
||||||
|
- Tier 2 (matrix/versor construction) MUST be f64 and hardware-deterministic;
|
||||||
|
the golden-bytes test is the cross-hardware canary.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from algebra import cl41 as cl
|
||||||
|
from core.physics.quantity_kernel import (
|
||||||
|
QuantityKernelError,
|
||||||
|
decode_quantity,
|
||||||
|
dilate_quantity,
|
||||||
|
embed_quantity,
|
||||||
|
translate_quantity,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _null_defect(psi: np.ndarray) -> float:
|
||||||
|
return abs(cl.scalar_part(cl.geometric_product(psi, psi)))
|
||||||
|
|
||||||
|
|
||||||
|
# --- Embedding: P(q) is a null point, in f64 ------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("q", [-50.0, -3.0, 0.0, 1.0, 7.5, 42.0])
|
||||||
|
def test_embedding_is_null(q: float) -> None:
|
||||||
|
assert _null_defect(embed_quantity(q)) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_embedding_is_float64() -> None:
|
||||||
|
# Guards the cl41 silent-f32 fallback (spike §4.6 Tier 2 trap).
|
||||||
|
assert embed_quantity(3.0).dtype == np.dtype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def test_embedding_shape() -> None:
|
||||||
|
assert embed_quantity(3.0).shape == (cl.N_COMPONENTS,)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Translator: exact, weight-preserving (algebraic identity) ------------
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("a", "b"), [(3.0, 4.0), (-2.0, 9.0), (0.5, 0.5), (10.0, -10.0)])
|
||||||
|
def test_translate_is_exact_addition(a: float, b: float) -> None:
|
||||||
|
got = translate_quantity(embed_quantity(b), a)
|
||||||
|
want = embed_quantity(a + b)
|
||||||
|
# Integer/half-integer coefficients ⇒ f64 rounding only.
|
||||||
|
assert np.max(np.abs(got - want)) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_translate_stays_null() -> None:
|
||||||
|
assert _null_defect(translate_quantity(embed_quantity(4.0), 3.0)) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_translate_output_is_float64() -> None:
|
||||||
|
assert translate_quantity(embed_quantity(4.0), 3.0).dtype == np.dtype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Dilator: scales by e^{-alpha} (SIGN PINNED, spike §3), weight != 1 ----
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("alpha", "b"), [(0.7, 3.0), (-1.1, 5.0), (2.0, -4.0)])
|
||||||
|
def test_dilate_scales_by_exp_minus_alpha(alpha: float, b: float) -> None:
|
||||||
|
q_dec = decode_quantity(dilate_quantity(embed_quantity(b), alpha))
|
||||||
|
assert abs(q_dec - np.exp(-alpha) * b) < 1e-6
|
||||||
|
|
||||||
|
|
||||||
|
def test_dilate_carries_conformal_weight() -> None:
|
||||||
|
# Weight must genuinely differ from 1 → normalize-then-projective-decode is required.
|
||||||
|
x = dilate_quantity(embed_quantity(3.0), 0.7)
|
||||||
|
weight = float(x[5]) - float(x[4])
|
||||||
|
assert abs(weight - 1.0) > 1e-3
|
||||||
|
|
||||||
|
|
||||||
|
# --- Decode: projective, scale-invariant, round-trips ---------------------
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("q", [-50.0, -3.0, 0.0, 1.0, 7.5, 42.0])
|
||||||
|
def test_decode_round_trips_embedding(q: float) -> None:
|
||||||
|
assert abs(decode_quantity(embed_quantity(q)) - q) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("scale", [0.1, 1.0, 3.3, 10.0])
|
||||||
|
def test_decode_is_scale_invariant(scale: float) -> None:
|
||||||
|
psi = embed_quantity(7.5)
|
||||||
|
assert abs(decode_quantity(scale * psi) - decode_quantity(psi)) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_affine_chain_composes(a_mul: float = 3.0, add: float = 5.0, y: float = 4.0) -> None:
|
||||||
|
# "x = a*y + b" as versor transport. Dilation scales by e^{-alpha} (spike §3),
|
||||||
|
# so multiplying by a_mul needs alpha = -ln(a_mul), then translate by b.
|
||||||
|
psi = translate_quantity(dilate_quantity(embed_quantity(y), -np.log(a_mul)), add)
|
||||||
|
assert abs(decode_quantity(psi) - (a_mul * y + add)) < 1e-6
|
||||||
|
|
||||||
|
|
||||||
|
# --- Fail-closed on non-finite input --------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("bad", [np.inf, -np.inf, np.nan])
|
||||||
|
def test_embedding_refuses_non_finite(bad: float) -> None:
|
||||||
|
with pytest.raises(QuantityKernelError):
|
||||||
|
embed_quantity(bad)
|
||||||
|
|
||||||
|
|
||||||
|
def test_translate_refuses_non_finite_shift() -> None:
|
||||||
|
with pytest.raises(QuantityKernelError):
|
||||||
|
translate_quantity(embed_quantity(1.0), np.nan)
|
||||||
|
|
||||||
|
|
||||||
|
def test_decode_refuses_degenerate_weight() -> None:
|
||||||
|
# A point with zero conformal weight cannot be projectively decoded.
|
||||||
|
psi = np.zeros(cl.N_COMPONENTS, dtype=np.float64)
|
||||||
|
psi[1] = 1.0 # e1 only: weight (e5c - e4c) = 0
|
||||||
|
with pytest.raises(QuantityKernelError):
|
||||||
|
decode_quantity(psi)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Tier-2 cross-hardware reproducibility canary (golden bytes) ----------
|
||||||
|
|
||||||
|
# SHA-256 of embed_quantity(3.0).astype("<f8").tobytes(). Frozen from the first
|
||||||
|
# green run; a change means the substrate math or dtype drifted (spike §4.6).
|
||||||
|
_GOLDEN_EMBED_3 = "be50e6f65ebe0e528055912aaaa3ddc2af3eb363e00cdccc711aa26c32548719"
|
||||||
|
|
||||||
|
|
||||||
|
def test_embedding_golden_bytes_are_stable() -> None:
|
||||||
|
digest = hashlib.sha256(embed_quantity(3.0).astype("<f8").tobytes()).hexdigest()
|
||||||
|
assert digest == _GOLDEN_EMBED_3, f"substrate drift: got {digest}"
|
||||||
Loading…
Reference in a new issue