Commit graph

3 commits

Author SHA1 Message Date
Shay
34cc345d7e
feat(ADR-0141): multiply as CGA dilator versor (positive non-zero) (#216)
* feat(ADR-0141): multiply as CGA dilator versor (positive non-zero)

Adds `multiply(scale)` to `generate/math_versor_arithmetic.py` as the
standard CGA dilator for multiplicative scaling along e1, restricted to
`scale > 0`.  All ten ADR-0141 assertion families pass.

Preliminary measurement confirmed:
  N = n_o ∧ n_inf: component -1 at index 15 (blade (3,4) = e4∧e5)
  N² = +1.0 (pure scalar) → closed-form D_s = cosh(α/2) + sinh(α/2)·N
  n_o · n_inf = -1;  n_o² = n_inf² = 0

Because N² = +1, the cosh/sinh expansion is exact in float64 and
D_s · ~D_s = cosh² − sinh² = 1 holds to machine epsilon.

The sandwich D_s·X·~D_s produces a null point with n_inf normalization
1/s.  `decode_quantity` is updated to divide by that factor, recovering
value · s.  For translator outputs (normalization = 1) the result is
identical to the previous direct e1 read; all 152 prior add/subtract
tests pass unchanged.

`embed_quantity` is updated to embed directly in float64, eliminating
float32 quantization error for values like 0.01 (float32(0.01) ≠ 0.01);
all prior test-case values were exactly representable in float32.

* docs(ADR-0141): add decision document for multiply-as-dilator spike

The ADR doc was drafted in a separate branch and not present when the
implementation worktree was created from origin/main. Adding it now so
the decision record lands on main with the implementation it specifies.

Content unchanged from the draft — same spec the implementation already
satisfies (10 assertion families, fixed test cases, falsification
discipline, deferred scope for negative / zero / divide / Rate).

No code or test changes in this commit.
2026-05-24 09:09:53 -07:00
Shay
622919019d
feat(ADR-0140): subtract as inverse translator + additive group closure (#215)
Extends generate/math_versor_arithmetic.py with one new function:

    def subtract(addend: float) -> np.ndarray:
        return translator(-float(addend))

Single-line delegate to translator(); no new algebra.

Adds tests/test_arithmetic_subtract_and_group.py covering all nine
ADR-0140 acceptance families:

  Families 1-6 (ADR-0139 families applied to subtract):
    1. Embedding well-formedness — null cone preserved for subtract cases
    2. Translator-of-negative well-formedness — versor_condition < 1e-6
    3. Closure — sandwich result stays on null cone
    4. Arithmetic correctness — decoded value == a − b within 1e-9
    5. Replay determinism — byte-identical across runs
    6. Composability — subtract(c) ∘ subtract(b) decodes to a − b − c

  New group-property families (structural verification of ADR-0139 claim):
    7. Inverse composition — T_{-b} * T_b = identity (max residual: 0.000e+00)
    8. Round-trip closure — versor_apply(T_{-b}, versor_apply(T_b, X)) → (a, u)
    9a. Sum composition — T_a * T_b = T_{a+b} (max residual: 0.000e+00)
    9b. Commutativity — T_a * T_b byte-equals T_b * T_a (all 10 cases)

All 96 tests pass. Group residuals are exactly 0.0 in float64.
The additive subgroup of Cl(4,1) translators along e1 is abelian and
closed; ADR-0139's algebraic claim holds at the group level.
2026-05-24 08:34:35 -07:00
Shay
589297b79a
feat(ADR-0139): arithmetic-as-versor spike — add closes exactly in Cl(4,1) (#212)
First step of the Engine A lift program (CLAUDE.md commits the project to a
single deterministic cognitive engine; Engine B / math pipeline was always
intentional scaffolding per math_solver.py:24). Proves the load-bearing
unknown: one arithmetic operation can be represented as a closed versor at
the required tolerance, with no new normalization and no weakened invariant.

Scope (frozen by ADR-0139):
- One operation: add
- Single-axis embedding: quantities on e1 axis
- No graph wiring, no pipeline integration, no GSM8K case routed
- Unit carried as caller metadata

Construction:
- embed_quantity(v, u) = embed_point([v, 0, 0])  (existing CGA primitive)
- translator(b)         = 1 - 0.5 * (b*e1 * n_inf)   (textbook CGA translator)
- decode_quantity(F, u) = (F[1], u)                  (e1 coordinate)

Measured values (all 11 fixed cases + composability):

      a         b      vcond(T)         |<R,R>|     decode_err
    0.0       0.0     0.000e+00       0.000e+00      0.000e+00
    0.0       1.0     0.000e+00       0.000e+00      0.000e+00
    1.0       0.0     0.000e+00       0.000e+00      0.000e+00
    3.0       4.0     0.000e+00       0.000e+00      0.000e+00
    7.0      -3.0     0.000e+00       0.000e+00      0.000e+00
   0.25      0.75     0.000e+00       0.000e+00      0.000e+00
    1.5       2.5     0.000e+00       0.000e+00      0.000e+00
   -5.0       5.0     0.000e+00       0.000e+00      0.000e+00
   -2.0      -3.0     0.000e+00       0.000e+00      0.000e+00
  100.0       1.0     0.000e+00       0.000e+00      0.000e+00
    1.0     100.0     0.000e+00       0.000e+00      0.000e+00
  compose (2, 3, 5) → 10:   |<R2,R2>| = 0.000e+00, decode_err = 0.000e+00

Every residual is exactly 0.0 in float64. The construction is algebraically
closed: T_t * reverse(T_t) = 1 - 0.25*B^2 where B = t*n_inf, and B^2 = 0
because (e14)^2 + (e15)^2 = -1 + 1 and cross-terms cancel. No machine-epsilon
drift accumulates because the relevant cancellation happens at the algebraic
level before float arithmetic.

ADR-0139 acceptance items 1-6 (one parametrized test family each):
  1. Embedding well-formedness   — test_family1_embedding_is_null         (11 cases)
  2. Translator well-formedness  — test_family2_translator_unit_versor    (11 cases)
  3. Closure                     — test_family3_sandwich_preserves_null   (11 cases)
  4. Arithmetic correctness      — test_family4_decode_matches_sum        (11 cases)
  5. Replay determinism          — test_family5_replay_byte_identical     (11 cases)
  6. Composability               — test_family6_two_translators_compose   (1 case)
  Total: 56 tests, all passing.

Lift program decision: proceeds. Follow-on ADRs (subtract, multiply, Rate,
compare, MathProblemGraph → PropositionGraph, pipeline integration, first
GSM8K case end-to-end through Engine A) are now justified by a concrete
algebraic foundation rather than design speculation.

Out of scope per ADR-0139:
- No modifications to algebra/, core/cognition/, chat/, math_solver.py,
  math_verifier.py, math_realizer.py, math_candidate_parser.py
- No GSM8K runner changes
- No pack changes
- Engine B continues serving GSM8K unchanged; the 3/50 admission set is
  preserved

CLI lanes intentionally not run — main has known test-rot orthogonal to
this PR. The 56 new tests are self-contained and the diff touches only
three new files.
2026-05-24 06:57:39 -07:00