draft(adr-0246): slice-1 scaffold — §6.1/§6.2 eval suite + malformed-F guard
BOUNDED AUTONOMOUS SCAFFOLD DRAFT (Fable 5) — not a PR, not merged, no status
flip, no main push. For Opus 4.8 + Shay audit before anything proceeds toward main.
Stacked on the verified §3-primitives + §3.4/3.5-ledger stack (reuse, not re-derive;
all descend from main @ 04d67ca5).
Adds (directive steps 3-4):
+ evals/adr_0246_geometric_suite/ runnable §6.1 synthetic geometric suite
(identity, pi-inversion, 90deg-permutation, mild drift, alien tilt e14,
boost e15, near-singular Gram, malformed F) + §6.2 path/holonomy suite
(lawful sequence, small-rotation session accumulation, interleaved refuse,
pack-change hard break, raw!=lawful forensic). 14/14 cases pass.
+ tests/test_adr_0246_geometric_suite.py pins every case + explicit §6.1 pins
(pi-inversion s=-1, 90deg s=0, near-singular Gram error, malformed-F error)
+ identity_manifold.py: MalformedVersorError + _validate_versor guard on
induced_action / typed_residual_energy (§6.1 fail-closed on malformed F)
+ docs/handoff/adr-0246-slice1-scaffold-notes.md placeholder list, §5-§7
uncertainties, constraint-compliance record, explicit Opus/human TODOs
+ docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt actual run output
Constraints honored: H_id={I} only; no soft-projection of unlawful A; path
composes lawful actions only (never raw product); no C_id corrector; chat/runtime,
flags, D4 gate wiring untouched (A-04 quarantine pinned); no discrimination report,
no ADR body, no claims language (deferred TODO: Opus/human); D4 plan not modified.
epsilon_turn/epsilon_session are UNCERTIFIED PLACEHOLDERS, never baked into a
module default (PathBudget is caller-supplied) — flagged in notes + run log.
[Verification]: uv run core test --suite smoke -q => 176 passed;
python -m evals.adr_0246_geometric_suite => 14/14 all_passed;
ADR-0246 suites + adjacent D4 identity surfaces => 114 passed (see run log).
This commit is contained in:
parent
6efe4ad80c
commit
ed54dddacb
7 changed files with 645 additions and 5 deletions
|
|
@ -160,6 +160,28 @@ def euclidean_norm(s: np.ndarray) -> float:
|
||||||
return float(np.linalg.norm(np.asarray(s, dtype=np.float64), ord=2))
|
return float(np.linalg.norm(np.asarray(s, dtype=np.float64), ord=2))
|
||||||
|
|
||||||
|
|
||||||
|
class MalformedVersorError(ValueError):
|
||||||
|
"""Raised when a versor handed to an ADR-0246 primitive is not a well-formed
|
||||||
|
``N_COMPONENTS``-vector of finite floats.
|
||||||
|
|
||||||
|
Fail-closed at the primitive boundary (ADR-0246 §6.1 "Malformed F → typed
|
||||||
|
error"): a NaN/inf or wrong-shape field must raise, never propagate a silent
|
||||||
|
NaN into an induced action or a residual-channel split.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_versor(versor: np.ndarray) -> np.ndarray:
|
||||||
|
"""Coerce to a finite float64 ``(N_COMPONENTS,)`` array or fail closed."""
|
||||||
|
array = np.asarray(versor, dtype=np.float64)
|
||||||
|
if array.shape != (N_COMPONENTS,):
|
||||||
|
raise MalformedVersorError(
|
||||||
|
f"versor must have shape ({N_COMPONENTS},), got {array.shape}"
|
||||||
|
)
|
||||||
|
if not np.all(np.isfinite(array)):
|
||||||
|
raise MalformedVersorError("versor has non-finite (NaN/inf) components")
|
||||||
|
return array
|
||||||
|
|
||||||
|
|
||||||
def orthogonality_defect_of_action(action: np.ndarray, gram: np.ndarray) -> float:
|
def orthogonality_defect_of_action(action: np.ndarray, gram: np.ndarray) -> float:
|
||||||
"""``‖AᵀGA − G‖_F`` for a precomputed induced action ``A`` (ADR-0246 §3.2).
|
"""``‖AᵀGA − G‖_F`` for a precomputed induced action ``A`` (ADR-0246 §3.2).
|
||||||
|
|
||||||
|
|
@ -270,8 +292,11 @@ class IdentityManifoldGeometry:
|
||||||
When ``F`` preserves the subspace isometrically, ``A`` is ``G``-orthogonal
|
When ``F`` preserves the subspace isometrically, ``A`` is ``G``-orthogonal
|
||||||
(``AᵀGA = G``). Built only from existing primitives (Gram, signed inner
|
(``AᵀGA = G``). Built only from existing primitives (Gram, signed inner
|
||||||
product, sandwich); no new algebra.
|
product, sandwich); no new algebra.
|
||||||
|
|
||||||
|
Raises :class:`MalformedVersorError` on a non-finite or wrong-shape versor
|
||||||
|
(ADR-0246 §6.1 fail-closed on malformed F).
|
||||||
"""
|
"""
|
||||||
versor = np.asarray(versor, dtype=np.float64)
|
versor = _validate_versor(versor)
|
||||||
n = len(self.axes_psi)
|
n = len(self.axes_psi)
|
||||||
overlaps = np.empty((n, n), dtype=np.float64)
|
overlaps = np.empty((n, n), dtype=np.float64)
|
||||||
for j, axis_j in enumerate(self.axes_psi):
|
for j, axis_j in enumerate(self.axes_psi):
|
||||||
|
|
@ -310,8 +335,11 @@ class IdentityManifoldGeometry:
|
||||||
|
|
||||||
Retains the positive-definite Euclidean coefficient norm (never the
|
Retains the positive-definite Euclidean coefficient norm (never the
|
||||||
indefinite ``⟨·,·⟩₀``) so a boost/e5 component cannot silently vanish.
|
indefinite ``⟨·,·⟩₀``) so a boost/e5 component cannot silently vanish.
|
||||||
|
|
||||||
|
Raises :class:`MalformedVersorError` on a non-finite or wrong-shape versor
|
||||||
|
(ADR-0246 §6.1 fail-closed on malformed F).
|
||||||
"""
|
"""
|
||||||
versor = np.asarray(versor, dtype=np.float64)
|
versor = _validate_versor(versor)
|
||||||
e4_energy = e5_energy = spatial_foreign = unclassified = total = 0.0
|
e4_energy = e5_energy = spatial_foreign = unclassified = total = 0.0
|
||||||
for axis in self.axes_psi:
|
for axis in self.axes_psi:
|
||||||
rotated = sandwich(versor, axis)
|
rotated = sandwich(versor, axis)
|
||||||
|
|
|
||||||
38
docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt
Normal file
38
docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
ADR-0246 slice-1 scaffold — local run log
|
||||||
|
branch: feat/adr-0246-slice1-scaffold
|
||||||
|
base: feat/adr-0246-path-ledger (contains §3 primitives + §3.4/3.5 ledger); all descend from main @ 04d67ca5
|
||||||
|
commit (pre-scaffold-commit HEAD): 6efe4ad80cc9a4914fc97e0e51ec92c1dd43ec72
|
||||||
|
python: Python 3.12.13
|
||||||
|
|
||||||
|
=== §6.1/§6.2 eval harness: python -m evals.adr_0246_geometric_suite ===
|
||||||
|
|
||||||
|
[geometric_suite]
|
||||||
|
PASS identity_versor
|
||||||
|
PASS inplane_pi_inversion_e12
|
||||||
|
PASS inplane_90deg_permutation_e12
|
||||||
|
PASS mild_inplane_drift_e12_0.02
|
||||||
|
PASS alien_tilt_e14_1.5
|
||||||
|
PASS boost_e15_1.0
|
||||||
|
PASS near_singular_gram
|
||||||
|
PASS malformed_f_nan
|
||||||
|
PASS malformed_f_wrong_shape
|
||||||
|
|
||||||
|
[path_suite]
|
||||||
|
PASS lawful_near_identity_sequence
|
||||||
|
PASS small_rotations_accumulate_to_session_refusal
|
||||||
|
PASS interleaved_refuse_admit
|
||||||
|
PASS hard_break_on_pack_change
|
||||||
|
PASS raw_product_differs_from_lawful
|
||||||
|
|
||||||
|
14/14 cases passed; all_passed=True
|
||||||
|
placeholders (uncertified): {'epsilon_turn': 0.1, 'epsilon_session': 0.3, 'note': 'UNCERTIFIED — D4 Phase 3 certified only gamma_id; ε not calibrated'}
|
||||||
|
|
||||||
|
=== pytest: ADR-0246 suites + adjacent identity surfaces ===
|
||||||
|
........................................................................ [ 63%]
|
||||||
|
.......................................... [100%]
|
||||||
|
114 passed in 52.42s
|
||||||
|
|
||||||
|
=== uv run core test --suite smoke -q ===
|
||||||
|
|
||||||
|
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||||
|
176 passed, 1 warning in 130.75s (0:02:10)
|
||||||
|
|
@ -54,9 +54,10 @@ structure is stabilized would instrument lawfulness on a frame the dynamics igno
|
||||||
|------|-------|--------|
|
|------|-------|--------|
|
||||||
| **Slice 0** — mismatch diagnostic | evidence-only classification of the benign mismatch (foreign leakage vs in-span-unlawful vs numerical vs path vs semantic-coupling-absent) | **merged** `main` (quarantined diagnostic artifact); found: structural e4/e5 foreign leakage, declared frame dynamically unspecial |
|
| **Slice 0** — mismatch diagnostic | evidence-only classification of the benign mismatch (foreign leakage vs in-span-unlawful vs numerical vs path vs semantic-coupling-absent) | **merged** `main` (quarantined diagnostic artifact); found: structural e4/e5 foreign leakage, declared frame dynamically unspecial |
|
||||||
| **§3 primitives** | pure `A(F)`, `d_orth`, `d_stab` vs locked `H_id={I}`, typed residual channels in `core/physics/identity_manifold.py` + `identity_action.py`; slice-0 eval rewired to consume them (single source of truth) | **committed** `feat/adr-0246-induced-action-primitives` (RED→GREEN, off-serving, flag untouched) — awaiting review |
|
| **§3 primitives** | pure `A(F)`, `d_orth`, `d_stab` vs locked `H_id={I}`, typed residual channels in `core/physics/identity_manifold.py` + `identity_action.py`; slice-0 eval rewired to consume them (single source of truth) | **committed** `feat/adr-0246-induced-action-primitives` (RED→GREEN, off-serving, flag untouched) — awaiting review |
|
||||||
| **§3.4/§3.5 path ledger** (this unit) | lawful-only composition + hard breaks: `PathBudget`, `IdentityChainScope`, `IdentityPathLedger`, `advance_identity_path` in `identity_action.py`; refused turns = break markers (never soft-projected `I`); scope change = hard break onto new chain | in progress — RED→GREEN, off-serving; stacked on the primitives branch |
|
| **§3.4/§3.5 path ledger** | lawful-only composition + hard breaks: `PathBudget`, `IdentityChainScope`, `IdentityPathLedger`, `advance_identity_path` in `identity_action.py`; refused turns = break markers (never soft-projected `I`); scope change = hard break onto new chain | **committed** `feat/adr-0246-path-ledger` (RED→GREEN, off-serving), stacked on primitives — awaiting review |
|
||||||
| §3.7 gate admit surface | wire d_orth/d_stab/typed channels into `IdentityScore` (flag default-off) | after ledger |
|
| **§6.1/§6.2 eval matrix (scaffold)** (this unit) | runnable synthetic geometric + path/holonomy suites (`evals/adr_0246_geometric_suite/`), every §6.1/§6.2 case pinned; malformed-F fail-closed (`MalformedVersorError`) | **draft scaffold** `feat/adr-0246-slice1-scaffold` (14/14 eval cases + 114 identity-surface tests + smoke green) — awaiting Opus/Shay audit (`docs/handoff/adr-0246-slice1-scaffold-notes.md`) |
|
||||||
| §6 eval matrix + §11 feasibility | synthetic + path suites + discrimination report; the invariant/semantic-grounding feasibility study runs here as the **first consumer** of the §3 primitives (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) | after gate surface |
|
| §3.7 gate admit surface | wire d_orth/d_stab/typed channels into `IdentityScore` (flag default-off); calibrate ε_turn/ε_session | after audit |
|
||||||
|
| §6.3 + §11 feasibility | discrimination report; the invariant/semantic-grounding feasibility study runs here as the **first consumer** of the §3 primitives (fixed cohort splits, synthetic recovery controls, typed e4/e5 generator analysis, precision pairs, adversarial discrimination) | after gate surface |
|
||||||
| ADR-0246 body + acceptance packet | §10 criteria; no self-Accept | last |
|
| ADR-0246 body + acceptance packet | §10 criteria; no self-Accept | last |
|
||||||
|
|
||||||
Nothing in the reordering relaxes a §7 non-goal: no `C_id` corrector, no `H_id`
|
Nothing in the reordering relaxes a §7 non-goal: no `C_id` corrector, no `H_id`
|
||||||
|
|
|
||||||
132
docs/handoff/adr-0246-slice1-scaffold-notes.md
Normal file
132
docs/handoff/adr-0246-slice1-scaffold-notes.md
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# ADR-0246 Slice-1 Scaffold — Handoff Notes (Fable 5 bounded autonomous draft)
|
||||||
|
|
||||||
|
**Status:** DRAFT SCAFFOLD ONLY — not a PR, not merged, no status flip, no `main`
|
||||||
|
push. For review by Opus 4.8 + Joshua Shay before anything proceeds toward `main`.
|
||||||
|
**Branch:** `feat/adr-0246-slice1-scaffold` (unmerged).
|
||||||
|
**D4 gate (directive stop-condition #3):** PASSED — D4 is closed. Both acceptance
|
||||||
|
packets are on `main` (`docs/audit/adr-024{4,5}-acceptance-packet-2026-07-17.md`);
|
||||||
|
ADR-0244 status line is `Accepted — ratified by Joshua Shay 2026-07-17`; `main @
|
||||||
|
04d67ca5`. Proceeded.
|
||||||
|
**Stop condition reached:** #1 — all four build steps complete and the full
|
||||||
|
§6.1 + §6.2 matrix passes locally (smoke + new tests + eval harness), with results
|
||||||
|
written to `docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt` (not asserted
|
||||||
|
from memory). No §3/§7 non-goal had to be violated; no calibration numbers were
|
||||||
|
invented into the modules (see Placeholders).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. What was built (directive steps 1–4)
|
||||||
|
|
||||||
|
| Step | Deliverable | Where |
|
||||||
|
|------|-------------|-------|
|
||||||
|
| 1 | `induced_action(F)`, `d_orth`, typed residual energy (§3.1/3.2/3.6), pure f64 | `core/physics/identity_manifold.py` |
|
||||||
|
| 2 | `H_id={I}` policy (`IdentityStabilizer`), `d_stab`, lawful-only path composition + hard-break ledger (§3.3–§3.5) | `core/physics/identity_action.py` |
|
||||||
|
| 3 | Runnable §6.1 synthetic geometric suite + §6.2 path/holonomy suite | `evals/adr_0246_geometric_suite/` |
|
||||||
|
| 4 | Unit pins for every synthetic case (fail loudly on the exact expected) | `tests/test_adr_0246_{induced_action,path_ledger,geometric_suite}.py` |
|
||||||
|
|
||||||
|
### Reuse note (transparent for audit)
|
||||||
|
Steps 1–2 are the **already-verified** work from earlier this session, reused
|
||||||
|
rather than re-derived (RED-first TDD, off-serving):
|
||||||
|
- `feat/adr-0246-induced-action-primitives` — §3 primitives (commit `4941cf18`)
|
||||||
|
- `feat/adr-0246-path-ledger` — §3.4/3.5 ledger (commit `6efe4ad8`, stacked)
|
||||||
|
|
||||||
|
`feat/adr-0246-slice1-scaffold` is stacked on top of those, so its history carries
|
||||||
|
both commits (all descend from `main @ 04d67ca5`). The two earlier branches are
|
||||||
|
therefore **subsumed** by this scaffold and can be pruned if the reviewers prefer
|
||||||
|
the single-branch deliverable. This scaffold additionally adds: the malformed-F
|
||||||
|
guard (`MalformedVersorError`), the §6.1/§6.2 eval harness, the extra §6.1 pins,
|
||||||
|
the run log, and these notes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Hard-constraint compliance (directive "Hard constraints while building")
|
||||||
|
|
||||||
|
- [x] `H_id = {I}` only — `IdentityStabilizer.singleton`; never enlarged; never
|
||||||
|
soft-projects an unlawful `A` onto `I` (refused turns are break markers).
|
||||||
|
Pinned: `test_refused_turn_is_break_and_excluded`.
|
||||||
|
- [x] Path composed from lawful/certified actions only — never raw `A_t`.
|
||||||
|
`advance_identity_path` composes only turns with `d_stab ≤ ε_turn`; refused/
|
||||||
|
ill-conditioned turns get a `path_break` marker (never an identity stand-in).
|
||||||
|
Forensic contrast pinned: `test_raw_product_differs_from_lawful`.
|
||||||
|
- [x] No nonzero geometric `C_id` / conjugate corrector — admit-or-abstain only.
|
||||||
|
Nothing in the modules rewrites `F` or `A`.
|
||||||
|
- [x] `chat/runtime.py`, flag defaults, and D4 gate wiring **untouched**. Verified:
|
||||||
|
`test_gate_flag_and_bound_untouched` (flag default-off, `_WAVE_LEAKAGE_BOUND`
|
||||||
|
unchanged); A-04 quarantine pinned by `test_*_is_pure_offserving` /
|
||||||
|
`test_suite_is_offserving`.
|
||||||
|
- [x] No discrimination report, ADR-0246 body, or acceptance-packet language
|
||||||
|
written. No "semantic inalienability" / marketing claim drafted — see §5 TODO.
|
||||||
|
- [x] `docs/handoff/ADR-0244-D4-IMPLEMENTATION-PLAN.md` **not modified**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Placeholder values used (directive: list every one + why)
|
||||||
|
|
||||||
|
| Placeholder | Value | Where | Why it is a placeholder |
|
||||||
|
|-------------|-------|-------|--------------------------|
|
||||||
|
| `epsilon_turn` | 0.1 | `evals/adr_0246_geometric_suite` `PLACEHOLDER_EPSILON_TURN`; test fixtures | **UNCERTIFIED.** D4 Phase 3 certified only `γ_id = 0.2126624458513829`. The two-level path budget (§3.4) is not yet calibrated. Value chosen only to exercise the mechanism (small single-turn drift admits; a 90° rotation refuses). NOT baked into any serve/module default — `PathBudget` is always caller-supplied. |
|
||||||
|
| `epsilon_session` | 0.3 | same | **UNCERTIFIED.** Same status; chosen so ~7 steps of a 0.05-rad rotation accumulate past it, demonstrating the accumulation guard. Not a policy value. |
|
||||||
|
| `_NONZERO` = 0.05 | 0.05 | eval harness | Not a calibration number — a "clearly nonzero" marker for the ">0" rows of the §6.1 table (d_stab, leakage). |
|
||||||
|
| test construction angles (0.02, 0.05, 1.0, 1.5, π/2, π) | — | tests/evals | Case constructions, not policy. Chosen to realize the exact §6.1/§6.2 geometric signatures. |
|
||||||
|
|
||||||
|
**Explicitly NOT invented:** `γ_id` (already certified, unchanged), `τ_max`,
|
||||||
|
`s_min`. The per-turn admit surface that would consume these (§3.7) is **not built
|
||||||
|
in this scaffold** — that is the gate-wiring unit, deliberately out of scope, so no
|
||||||
|
placeholder was needed for them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Uncertainties in §5–§7 (directive: list anything you were unsure how to satisfy)
|
||||||
|
|
||||||
|
1. **§3.2 general-pack `‖·‖_G` convention.** The brief fixes `d_stab = ‖A−H‖_G`
|
||||||
|
but leaves the general-pack weighted-norm convention to "ADR-0246 proper." I
|
||||||
|
implemented `‖M‖_G = ‖G^{1/2} M G^{-1/2}‖_F` (metric-consistent; reduces exactly
|
||||||
|
to Frobenius at `G=I`, which is the only shipped pack). **Needs review/ratification.**
|
||||||
|
2. **§3.6 `spatial_foreign` channel.** For the default pack (support = e1/e2/e3) it
|
||||||
|
is structurally ~0 (projection removes in-span components). Implemented generally
|
||||||
|
(residual energy on grade-1 spatial slots outside the axis support) but it cannot
|
||||||
|
fire until a non-default pack exists. Untested against a real non-default pack.
|
||||||
|
3. **§4.1 `IdentityActionRecord` full telemetry** (per-turn `field_digest`,
|
||||||
|
`record_digest`, gate/policy versions, admitted/refusal_reason) is **not built**;
|
||||||
|
only the §4.2 `IdentityPathLedger` (with `ledger_digest`, `chain_id`) is. The
|
||||||
|
per-turn record belongs to the gate-surface/telemetry unit (§3.7), out of scope
|
||||||
|
here. `advance_identity_path` returns a lightweight per-turn dict, not the full record.
|
||||||
|
4. **§6.1 "Malformed F → never silent legacy when wave field was supplied."** The
|
||||||
|
"silent legacy" clause is about the D4 gate's dual-mode fallback in
|
||||||
|
`identity.py` (untouched here, owned by D4). This scaffold adds a fail-closed
|
||||||
|
`MalformedVersorError` at the *pure primitive* boundary (`induced_action` /
|
||||||
|
`typed_residual_energy`). Whether the gate should route malformed-F to this same
|
||||||
|
typed error is a gate-wiring decision, deferred.
|
||||||
|
5. **Path budget semantics under a hard break.** I treated a scope change as: start
|
||||||
|
a fresh chain, and the triggering turn is turn 1 of the new chain (its own action
|
||||||
|
composes into the fresh `I`). The brief (§3.5) specifies a new `chain_id` and
|
||||||
|
that the old path is not continued, but does not pin whether the boundary turn
|
||||||
|
belongs to the old or new chain. Chose new-chain. **Confirm.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Explicitly deferred to Opus/human review (NOT written here)
|
||||||
|
|
||||||
|
> **TODO: Opus/human review** — the following were intentionally left undone per
|
||||||
|
> the directive; do not treat their absence as an oversight:
|
||||||
|
- The **discrimination report** (§6.3): benign/adversarial rates, false-refusal,
|
||||||
|
ablations, CI-bounded separation. Requires the gate-surface + live cohorts.
|
||||||
|
- The **ADR-0246 body** and any **acceptance-packet** language.
|
||||||
|
- Any **claim about what the axes mean** ("semantic inalienability", grounding).
|
||||||
|
The §11 grounding-feasibility study (fixed cohort splits, synthetic recovery
|
||||||
|
controls, generator analysis, precision pairs, adversarial discrimination) is the
|
||||||
|
first consumer of these primitives and is **not** part of this scaffold.
|
||||||
|
- The **gate admit-surface wiring** into `IdentityScore` (§3.7) and the calibration
|
||||||
|
of `ε_turn`/`ε_session`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Verification (see run log for actual output)
|
||||||
|
|
||||||
|
`docs/audit/artifacts/adr-0246-slice1-scaffold-runlog.txt`:
|
||||||
|
- `python -m evals.adr_0246_geometric_suite` → 14/14 cases passed, `all_passed=True`.
|
||||||
|
- pytest ADR-0246 suites + adjacent D4 identity surfaces → 114 passed.
|
||||||
|
- `uv run core test --suite smoke -q` → (appended to the run log).
|
||||||
|
|
||||||
|
No PR. No merge. No status flip. Report back to Shay/Opus for the audit that
|
||||||
|
decides what, if anything, proceeds toward `main`.
|
||||||
301
evals/adr_0246_geometric_suite/__init__.py
Normal file
301
evals/adr_0246_geometric_suite/__init__.py
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
"""ADR-0246 §6.1/§6.2 synthetic geometric + path/holonomy eval suite (scaffold).
|
||||||
|
|
||||||
|
A runnable, deterministic harness that constructs every case in the preflight
|
||||||
|
§6.1 synthetic geometric table and §6.2 path/holonomy table, runs the pure
|
||||||
|
ADR-0246 primitives (induced action ``A(F)``, ``d_orth``, ``d_stab`` vs the locked
|
||||||
|
singleton ``H_id={I}``, typed residual channels, lawful-only path ledger), and
|
||||||
|
checks each against its expected geometric signature. Every case reports
|
||||||
|
``{name, checks, passed}`` and the suite reports an overall ``passed``.
|
||||||
|
|
||||||
|
Scope (bounded scaffold draft — NOT an accepted ADR):
|
||||||
|
* Off-serving: imports only ``algebra`` + ``core.physics.identity_{manifold,action}``;
|
||||||
|
never ``chat.runtime`` (A-04 quarantine).
|
||||||
|
* ``H_id={I}`` only; refused turns are break markers, never soft-projected ``I``;
|
||||||
|
the path composes lawful actions only — never the raw product.
|
||||||
|
* No ``C_id`` corrector; admit-or-abstain only.
|
||||||
|
* The path-suite ε values are UNCERTIFIED PLACEHOLDERS (see
|
||||||
|
``PLACEHOLDER_EPSILON_*``): D4 Phase 3 certified only ``γ_id``; ε_turn/ε_session
|
||||||
|
are not yet calibrated. They exist here solely to exercise the mechanism and
|
||||||
|
are flagged in the run log. Do NOT read them as policy.
|
||||||
|
|
||||||
|
No discrimination report, no claims about what the axes *mean* — that is
|
||||||
|
explicitly deferred to Opus/human review (see the slice-1 scaffold notes).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from algebra.cl41 import N_COMPONENTS
|
||||||
|
from core.physics.identity_manifold import (
|
||||||
|
IdentityManifoldGeometry,
|
||||||
|
ManifoldConditioningError,
|
||||||
|
MalformedVersorError,
|
||||||
|
)
|
||||||
|
from core.physics.identity_action import (
|
||||||
|
IdentityChainScope,
|
||||||
|
PathBudget,
|
||||||
|
advance_identity_path,
|
||||||
|
raw_path_product,
|
||||||
|
stabilizer_defect_for_versor,
|
||||||
|
)
|
||||||
|
|
||||||
|
# grade-2 bivector plane indices (grade-2 block starts at index 6)
|
||||||
|
_E12, _E13, _E14, _E15, _E23, _E24, _E25 = 6, 7, 8, 9, 10, 11, 12
|
||||||
|
|
||||||
|
# --- UNCERTIFIED PLACEHOLDERS (flagged; not policy) ---------------------------
|
||||||
|
# D4 Phase 3 certified γ_id only. ε_turn / ε_session are NOT calibrated; these
|
||||||
|
# illustrative values merely exercise the two-level path budget mechanism.
|
||||||
|
PLACEHOLDER_EPSILON_TURN: float = 0.1
|
||||||
|
PLACEHOLDER_EPSILON_SESSION: float = 0.3
|
||||||
|
# "clearly nonzero" marker for the >0 rows in the §6.1 table (not a threshold).
|
||||||
|
_NONZERO = 0.05
|
||||||
|
_ZERO = 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def default_geometry() -> IdentityManifoldGeometry:
|
||||||
|
"""The shipped default declared frame span(e1,e2,e3), Gram = I3."""
|
||||||
|
return IdentityManifoldGeometry.from_directions(
|
||||||
|
((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def rotor(biv: int, theta: float) -> np.ndarray:
|
||||||
|
r = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
r[0] = np.cos(theta / 2.0)
|
||||||
|
r[biv] = np.sin(theta / 2.0)
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def boost(biv: int, theta: float) -> np.ndarray:
|
||||||
|
r = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
r[0] = np.cosh(theta / 2.0)
|
||||||
|
r[biv] = np.sinh(theta / 2.0)
|
||||||
|
return r
|
||||||
|
|
||||||
|
|
||||||
|
def identity_versor() -> np.ndarray:
|
||||||
|
v = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
v[0] = 1.0
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def _case(name: str, checks: dict[str, bool]) -> dict[str, Any]:
|
||||||
|
return {"name": name, "checks": checks, "passed": all(checks.values())}
|
||||||
|
|
||||||
|
|
||||||
|
def _expect_raises(name: str, fn: Callable[[], Any], exc: type[BaseException]) -> dict[str, Any]:
|
||||||
|
raised = False
|
||||||
|
try:
|
||||||
|
fn()
|
||||||
|
except exc:
|
||||||
|
raised = True
|
||||||
|
except Exception: # wrong exception type is a failure
|
||||||
|
raised = False
|
||||||
|
return _case(name, {f"raises_{exc.__name__}": raised})
|
||||||
|
|
||||||
|
|
||||||
|
# --- §6.1 synthetic geometric suite -------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def run_geometric_suite(geometry: IdentityManifoldGeometry | None = None) -> list[dict[str, Any]]:
|
||||||
|
geometry = geometry or default_geometry()
|
||||||
|
cases: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
# Identity versor → A≈I, ℓ≈0, s≈+1, d_orth≈0, d_stab≈0
|
||||||
|
v = identity_versor()
|
||||||
|
a = geometry.induced_action(v)
|
||||||
|
leak, self_align = geometry.axis_response(v)
|
||||||
|
cases.append(_case("identity_versor", {
|
||||||
|
"A_is_identity": bool(np.allclose(a, np.eye(3), atol=1e-12)),
|
||||||
|
"leakage_zero": max(leak) < _ZERO,
|
||||||
|
"self_align_plus_one": min(self_align) > 1.0 - _ZERO,
|
||||||
|
"d_orth_zero": geometry.orthogonality_defect(v) < _ZERO,
|
||||||
|
"d_stab_zero": stabilizer_defect_for_versor(geometry, v) < _ZERO,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# In-plane π inversion of e1/e2 → ℓ≈0, s(e1)≈-1, s(e3)≈+1, d_stab>0
|
||||||
|
v = rotor(_E12, np.pi)
|
||||||
|
leak, self_align = geometry.axis_response(v)
|
||||||
|
cases.append(_case("inplane_pi_inversion_e12", {
|
||||||
|
"leakage_zero": max(leak) < _ZERO,
|
||||||
|
"self_align_e1_minus_one": self_align[0] < -1.0 + _ZERO,
|
||||||
|
"self_align_e2_minus_one": self_align[1] < -1.0 + _ZERO,
|
||||||
|
"self_align_e3_plus_one": self_align[2] > 1.0 - _ZERO,
|
||||||
|
"d_stab_positive": stabilizer_defect_for_versor(geometry, v) > _NONZERO,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# In-plane 90° permutation e1→e2 → ℓ≈0, s(e1)≈0, d_stab>0
|
||||||
|
v = rotor(_E12, np.pi / 2.0)
|
||||||
|
leak, self_align = geometry.axis_response(v)
|
||||||
|
cases.append(_case("inplane_90deg_permutation_e12", {
|
||||||
|
"leakage_zero": max(leak) < _ZERO,
|
||||||
|
"self_align_e1_zero": abs(self_align[0]) < _ZERO,
|
||||||
|
"self_align_e2_zero": abs(self_align[1]) < _ZERO,
|
||||||
|
"d_stab_positive": stabilizer_defect_for_versor(geometry, v) > _NONZERO,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Mild in-plane drift step → small d_stab (per-turn passes ε_turn placeholder)
|
||||||
|
v = rotor(_E12, 0.02)
|
||||||
|
d_stab = stabilizer_defect_for_versor(geometry, v)
|
||||||
|
leak, _ = geometry.axis_response(v)
|
||||||
|
cases.append(_case("mild_inplane_drift_e12_0.02", {
|
||||||
|
"leakage_zero": max(leak) < _ZERO,
|
||||||
|
"d_stab_small_but_positive": _ZERO < d_stab < PLACEHOLDER_EPSILON_TURN,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Alien tilt e14 → ℓ>0, null_or_conformal channel fires, boost channel ≈0
|
||||||
|
v = rotor(_E14, 1.5)
|
||||||
|
ch = geometry.typed_residual_energy(v)
|
||||||
|
cases.append(_case("alien_tilt_e14_1.5", {
|
||||||
|
"leakage_positive": geometry.leakage_rms(v) > _NONZERO,
|
||||||
|
"null_or_conformal_fires": ch["null_or_conformal"] > _NONZERO,
|
||||||
|
"boost_like_zero": ch["boost_like"] < _ZERO,
|
||||||
|
"unclassified_clean": ch["unclassified"] < _ZERO,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Boost component (e5) → ℓ,s normalized in range, boost channel fires, d_orth>0
|
||||||
|
v = boost(_E15, 1.0)
|
||||||
|
ch = geometry.typed_residual_energy(v)
|
||||||
|
leak, self_align = geometry.axis_response(v)
|
||||||
|
cases.append(_case("boost_e15_1.0", {
|
||||||
|
"leakage_in_unit_range": 0.0 <= max(leak) <= 1.0,
|
||||||
|
"self_align_in_range": all(-1.0 - _ZERO <= s <= 1.0 + _ZERO for s in self_align),
|
||||||
|
"boost_like_fires": ch["boost_like"] > _NONZERO,
|
||||||
|
"null_or_conformal_zero": ch["null_or_conformal"] < _ZERO,
|
||||||
|
"d_orth_positive": geometry.orthogonality_defect(v) > _NONZERO,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Near-singular Gram (near-parallel axes) → ManifoldConditioningError at build
|
||||||
|
cases.append(_expect_raises(
|
||||||
|
"near_singular_gram",
|
||||||
|
lambda: IdentityManifoldGeometry.from_directions(
|
||||||
|
((1.0, 0.0, 0.0), (1.0, 1e-9, 0.0), (0.0, 0.0, 1.0))
|
||||||
|
),
|
||||||
|
ManifoldConditioningError,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Malformed F → MalformedVersorError (NaN and wrong-shape)
|
||||||
|
nan_v = identity_versor()
|
||||||
|
nan_v[0] = np.nan
|
||||||
|
cases.append(_expect_raises(
|
||||||
|
"malformed_f_nan", lambda: geometry.induced_action(nan_v), MalformedVersorError
|
||||||
|
))
|
||||||
|
cases.append(_expect_raises(
|
||||||
|
"malformed_f_wrong_shape",
|
||||||
|
lambda: geometry.induced_action(np.ones(7, dtype=np.float64)),
|
||||||
|
MalformedVersorError,
|
||||||
|
))
|
||||||
|
return cases
|
||||||
|
|
||||||
|
|
||||||
|
# --- §6.2 path / holonomy suite -----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _scope(pack: str = "packA") -> IdentityChainScope:
|
||||||
|
return IdentityChainScope(
|
||||||
|
pack_content_digest=pack,
|
||||||
|
geometry_version="geomV1",
|
||||||
|
policy_version="polV1(PLACEHOLDER_epsilons)",
|
||||||
|
session_id="sess1",
|
||||||
|
biography_epoch=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_path_suite(geometry: IdentityManifoldGeometry | None = None) -> list[dict[str, Any]]:
|
||||||
|
geometry = geometry or default_geometry()
|
||||||
|
budget = PathBudget(
|
||||||
|
epsilon_turn=PLACEHOLDER_EPSILON_TURN,
|
||||||
|
epsilon_session=PLACEHOLDER_EPSILON_SESSION,
|
||||||
|
)
|
||||||
|
gram = geometry.gram
|
||||||
|
ident = geometry.induced_action(identity_versor())
|
||||||
|
cases: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
# Sequence of lawful near-I turns → A_path near I; no false path refusal
|
||||||
|
ledger = None
|
||||||
|
for _ in range(20):
|
||||||
|
ledger, _ = advance_identity_path(ledger, _scope(), ident, gram, budget)
|
||||||
|
cases.append(_case("lawful_near_identity_sequence", {
|
||||||
|
"path_near_identity": bool(np.allclose(ledger.a_path_lawful, np.eye(3), atol=1e-12)),
|
||||||
|
"session_admit": ledger.session_admit,
|
||||||
|
"no_breaks": ledger.break_count == 0,
|
||||||
|
"composed_20": ledger.composed_turn_count == 20,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Small in-plane rotations each < ε_turn → path eventually breaches ε_session
|
||||||
|
small = geometry.induced_action(rotor(_E12, 0.05))
|
||||||
|
ledger = None
|
||||||
|
all_turns_lawful = True
|
||||||
|
for _ in range(40):
|
||||||
|
ledger, rec = advance_identity_path(ledger, _scope(), small, gram, budget)
|
||||||
|
all_turns_lawful = all_turns_lawful and rec["lawful"]
|
||||||
|
if not ledger.session_admit:
|
||||||
|
break
|
||||||
|
cases.append(_case("small_rotations_accumulate_to_session_refusal", {
|
||||||
|
"each_turn_lawful": all_turns_lawful,
|
||||||
|
"session_refused": not ledger.session_admit,
|
||||||
|
"path_d_stab_exceeds_session": ledger.d_stab_path > PLACEHOLDER_EPSILON_SESSION,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Interleaved refuse + admit → refused turns break, excluded, raw recorded
|
||||||
|
big = geometry.induced_action(rotor(_E12, np.pi / 2.0))
|
||||||
|
seq = [ident, big, ident, big, ident]
|
||||||
|
ledger = None
|
||||||
|
breaks_pattern = []
|
||||||
|
for a in seq:
|
||||||
|
ledger, rec = advance_identity_path(ledger, _scope(), a, gram, budget)
|
||||||
|
breaks_pattern.append(rec["path_break"])
|
||||||
|
cases.append(_case("interleaved_refuse_admit", {
|
||||||
|
"composed_3": ledger.composed_turn_count == 3,
|
||||||
|
"breaks_2": ledger.break_count == 2,
|
||||||
|
"break_pattern": breaks_pattern == [False, True, False, True, False],
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Pack digest change → hard break, new chain_id, old path not continued
|
||||||
|
ledger, _ = advance_identity_path(None, _scope("packA"), ident, gram, budget)
|
||||||
|
ledger, _ = advance_identity_path(ledger, _scope("packA"), small, gram, budget)
|
||||||
|
id_a, drifted = ledger.chain_id, ledger.a_path_lawful.copy()
|
||||||
|
ledger, rec = advance_identity_path(ledger, _scope("packB"), ident, gram, budget)
|
||||||
|
cases.append(_case("hard_break_on_pack_change", {
|
||||||
|
"hard_break": rec["hard_break"],
|
||||||
|
"new_chain_id": ledger.chain_id != id_a,
|
||||||
|
"old_path_not_continued": not np.allclose(ledger.a_path_lawful, drifted),
|
||||||
|
"fresh_chain": ledger.composed_turn_count == 1 and ledger.break_count == 0,
|
||||||
|
}))
|
||||||
|
|
||||||
|
# Raw product ≠ lawful product when a refused turn is present (forensic)
|
||||||
|
seq = [ident, big, ident]
|
||||||
|
ledger = None
|
||||||
|
for a in seq:
|
||||||
|
ledger, _ = advance_identity_path(ledger, _scope(), a, gram, budget)
|
||||||
|
raw = raw_path_product(seq)
|
||||||
|
cases.append(_case("raw_product_differs_from_lawful", {
|
||||||
|
"raw_neq_lawful": not np.allclose(raw, ledger.a_path_lawful),
|
||||||
|
"lawful_excludes_refused": bool(np.allclose(ledger.a_path_lawful, np.eye(3), atol=1e-12)),
|
||||||
|
}))
|
||||||
|
return cases
|
||||||
|
|
||||||
|
|
||||||
|
def build_suite_report() -> dict[str, Any]:
|
||||||
|
geometry = default_geometry()
|
||||||
|
geometric = run_geometric_suite(geometry)
|
||||||
|
path = run_path_suite(geometry)
|
||||||
|
all_cases = geometric + path
|
||||||
|
return {
|
||||||
|
"schema_version": "adr_0246_geometric_suite_v1",
|
||||||
|
"declared_frame": ["truthfulness=e1", "coherence=e2", "reverence=e3"],
|
||||||
|
"stabilizer": "H_id={I} (locked)",
|
||||||
|
"placeholders": {
|
||||||
|
"epsilon_turn": PLACEHOLDER_EPSILON_TURN,
|
||||||
|
"epsilon_session": PLACEHOLDER_EPSILON_SESSION,
|
||||||
|
"note": "UNCERTIFIED — D4 Phase 3 certified only gamma_id; ε not calibrated",
|
||||||
|
},
|
||||||
|
"geometric_suite": geometric,
|
||||||
|
"path_suite": path,
|
||||||
|
"case_count": len(all_cases),
|
||||||
|
"passed_count": sum(1 for c in all_cases if c["passed"]),
|
||||||
|
"all_passed": all(c["passed"] for c in all_cases),
|
||||||
|
}
|
||||||
42
evals/adr_0246_geometric_suite/__main__.py
Normal file
42
evals/adr_0246_geometric_suite/__main__.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
"""Run the ADR-0246 §6.1/§6.2 geometric + path eval suite and emit the report.
|
||||||
|
|
||||||
|
Usage: uv run python -m evals.adr_0246_geometric_suite [out.json]
|
||||||
|
|
||||||
|
Deterministic, off-serving. Prints a per-case pass/fail summary and the overall
|
||||||
|
verdict; optionally writes the structured JSON report. Exit code 0 iff every case
|
||||||
|
passed (so it can gate a run log), 1 otherwise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from evals.adr_0246_geometric_suite import build_suite_report
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
report = build_suite_report()
|
||||||
|
for suite in ("geometric_suite", "path_suite"):
|
||||||
|
print(f"\n[{suite}]")
|
||||||
|
for case in report[suite]:
|
||||||
|
mark = "PASS" if case["passed"] else "FAIL"
|
||||||
|
print(f" {mark} {case['name']}")
|
||||||
|
if not case["passed"]:
|
||||||
|
for check, ok in case["checks"].items():
|
||||||
|
if not ok:
|
||||||
|
print(f" ✗ {check}")
|
||||||
|
print(
|
||||||
|
f"\n{report['passed_count']}/{report['case_count']} cases passed; "
|
||||||
|
f"all_passed={report['all_passed']}"
|
||||||
|
)
|
||||||
|
print(f"placeholders (uncertified): {report['placeholders']}")
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
with open(sys.argv[1], "w", encoding="utf-8") as fh:
|
||||||
|
fh.write(json.dumps(report, indent=2, sort_keys=True) + "\n")
|
||||||
|
print(f"report written to {sys.argv[1]}")
|
||||||
|
return 0 if report["all_passed"] else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
98
tests/test_adr_0246_geometric_suite.py
Normal file
98
tests/test_adr_0246_geometric_suite.py
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
"""ADR-0246 §6.1/§6.2 eval-suite pins — every synthetic case fails loudly.
|
||||||
|
|
||||||
|
Pins the runnable ``evals.adr_0246_geometric_suite`` harness AND the specific
|
||||||
|
§6.1 rows the preflight table calls out (π-inversion ``s≈-1``, 90° permutation
|
||||||
|
``s≈0``, near-singular Gram → ``ManifoldConditioningError``, malformed F →
|
||||||
|
``MalformedVersorError``), so a regression names the exact broken case.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from algebra.cl41 import N_COMPONENTS
|
||||||
|
from core.physics.identity_manifold import (
|
||||||
|
IdentityManifoldGeometry,
|
||||||
|
ManifoldConditioningError,
|
||||||
|
MalformedVersorError,
|
||||||
|
)
|
||||||
|
from evals.adr_0246_geometric_suite import (
|
||||||
|
build_suite_report,
|
||||||
|
default_geometry,
|
||||||
|
identity_versor,
|
||||||
|
rotor,
|
||||||
|
run_geometric_suite,
|
||||||
|
run_path_suite,
|
||||||
|
)
|
||||||
|
|
||||||
|
_E12 = 6
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def geometry() -> IdentityManifoldGeometry:
|
||||||
|
return default_geometry()
|
||||||
|
|
||||||
|
|
||||||
|
def test_full_suite_all_cases_pass():
|
||||||
|
report = build_suite_report()
|
||||||
|
# every case must pass; the assertion message names any that did not
|
||||||
|
failed = [c["name"] for c in report["geometric_suite"] + report["path_suite"] if not c["passed"]]
|
||||||
|
assert failed == [], f"failing cases: {failed}"
|
||||||
|
assert report["all_passed"] is True
|
||||||
|
assert report["case_count"] == report["passed_count"] == 14
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("case", [c["name"] for c in run_geometric_suite()])
|
||||||
|
def test_each_geometric_case_passes(geometry, case):
|
||||||
|
result = {c["name"]: c for c in run_geometric_suite(geometry)}[case]
|
||||||
|
assert result["passed"], result["checks"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("case", [c["name"] for c in run_path_suite()])
|
||||||
|
def test_each_path_case_passes(geometry, case):
|
||||||
|
result = {c["name"]: c for c in run_path_suite(geometry)}[case]
|
||||||
|
assert result["passed"], result["checks"]
|
||||||
|
|
||||||
|
|
||||||
|
# --- explicit §6.1 pins (directive step 4: fail loudly on the exact expected) --
|
||||||
|
|
||||||
|
|
||||||
|
def test_pi_inversion_self_align_is_minus_one(geometry):
|
||||||
|
_, self_align = geometry.axis_response(rotor(_E12, np.pi))
|
||||||
|
assert self_align[0] == pytest.approx(-1.0, abs=1e-9) # e1 inverted
|
||||||
|
assert self_align[1] == pytest.approx(-1.0, abs=1e-9) # e2 inverted
|
||||||
|
assert self_align[2] == pytest.approx(1.0, abs=1e-9) # e3 fixed
|
||||||
|
|
||||||
|
|
||||||
|
def test_90deg_permutation_self_align_is_zero(geometry):
|
||||||
|
_, self_align = geometry.axis_response(rotor(_E12, np.pi / 2.0))
|
||||||
|
assert self_align[0] == pytest.approx(0.0, abs=1e-9) # e1 → e2, orthogonal
|
||||||
|
assert self_align[1] == pytest.approx(0.0, abs=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def test_near_singular_gram_fails_closed():
|
||||||
|
with pytest.raises(ManifoldConditioningError):
|
||||||
|
IdentityManifoldGeometry.from_directions(
|
||||||
|
((1.0, 0.0, 0.0), (1.0, 1e-9, 0.0), (0.0, 0.0, 1.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_malformed_versor_raises_typed_error(geometry):
|
||||||
|
nan_v = identity_versor()
|
||||||
|
nan_v[5] = np.inf
|
||||||
|
with pytest.raises(MalformedVersorError):
|
||||||
|
geometry.induced_action(nan_v)
|
||||||
|
with pytest.raises(MalformedVersorError):
|
||||||
|
geometry.typed_residual_energy(np.ones(N_COMPONENTS + 3, dtype=np.float64))
|
||||||
|
|
||||||
|
|
||||||
|
def test_suite_is_offserving():
|
||||||
|
import evals.adr_0246_geometric_suite as suite
|
||||||
|
|
||||||
|
assert suite.__file__ is not None
|
||||||
|
with open(suite.__file__, encoding="utf-8") as fh:
|
||||||
|
src = fh.read()
|
||||||
|
# no actual import of serve modules (the A-04 note in the docstring names
|
||||||
|
# chat.runtime as forbidden, so match import statements, not the substring)
|
||||||
|
assert "import chat" not in src and "from chat" not in src
|
||||||
Loading…
Reference in a new issue