feat(third-door): #21 trajectory invariants + ADR-DAG embedding (Python)
Geometry-first Python authorities for the last Third-Door blueprint gaps: - trajectory_invariants: relative holonomy, divergence integral, energy boundary, zero-fabrication refusals (R&D §2.2) - core/adr/validator: SHA-256→bivector Ψ(M), simple project, master blade, proposal drift (R&D §2.4); not a parallel GeometricDelta ABI validator Ledger rows 7–8 green; optional Rust Ring-1 port deferred.
This commit is contained in:
parent
9f5c8c222f
commit
db924550fc
7 changed files with 584 additions and 7 deletions
23
core/adr/__init__.py
Normal file
23
core/adr/__init__.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
"""ADR-DAG conformal embedding (R&D-Revised §2.4 / issue #21).
|
||||||
|
|
||||||
|
Deterministic embedding of ADR markdown into Cl(4,1) bivector space and
|
||||||
|
master-blade drift checks for proposal coherence.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from core.adr.validator import (
|
||||||
|
AdrDagValidationError,
|
||||||
|
embed_adr_markdown,
|
||||||
|
master_architecture_blade,
|
||||||
|
proposal_drift,
|
||||||
|
simple_bivector_project,
|
||||||
|
validate_proposal_against_master,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AdrDagValidationError",
|
||||||
|
"embed_adr_markdown",
|
||||||
|
"master_architecture_blade",
|
||||||
|
"proposal_drift",
|
||||||
|
"simple_bivector_project",
|
||||||
|
"validate_proposal_against_master",
|
||||||
|
]
|
||||||
168
core/adr/validator.py
Normal file
168
core/adr/validator.py
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
"""
|
||||||
|
core/adr/validator.py
|
||||||
|
|
||||||
|
ADR-DAG conformal embedding Ψ(M) (R&D-Revised §2.4 / #21).
|
||||||
|
|
||||||
|
SHA-256(M) → 10×3-byte segments → c_k ∈ [−1, 1]
|
||||||
|
→ 10 basis bivectors (planes 6..15) → simple-bivector projection
|
||||||
|
→ master blade = successive wedge of load-bearing ADR embeddings
|
||||||
|
→ proposal drift = ‖B_p ∧ A_master‖
|
||||||
|
|
||||||
|
Cross-check: does **not** reimplement GeometricDelta ABI validation
|
||||||
|
(``core/abi/geometric_delta_validator.py``). This module embeds ADR text into
|
||||||
|
geometry; that module validates GeometricDelta envelopes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from algebra.cl41 import N_COMPONENTS, geometric_product, grade_project
|
||||||
|
|
||||||
|
_BIVECTOR_PLANES = tuple(range(6, 16)) # 10 planes
|
||||||
|
_NEAR_ZERO = 1e-12
|
||||||
|
_SIMPLE_G4_TOL = 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
class AdrDagValidationError(ValueError):
|
||||||
|
"""Fail-closed refusal from ADR-DAG embedding / drift checks."""
|
||||||
|
|
||||||
|
def __init__(self, reason: str, **disclosure) -> None:
|
||||||
|
self.reason = reason
|
||||||
|
self.disclosure = dict(disclosure)
|
||||||
|
super().__init__(f"adr_dag refused [{reason}]: {self.disclosure}")
|
||||||
|
|
||||||
|
|
||||||
|
def _grade_mass(v: np.ndarray) -> int:
|
||||||
|
for g in range(5, -1, -1):
|
||||||
|
if float(np.linalg.norm(grade_project(v, g))) > _NEAR_ZERO:
|
||||||
|
return g
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def multivector_wedge(A: np.ndarray, B: np.ndarray) -> np.ndarray:
|
||||||
|
"""Grade-raising wedge approximation: grade-project of geometric product.
|
||||||
|
|
||||||
|
For pure blades this matches the outer product grade. Used for master-blade
|
||||||
|
assembly and drift (B_p ∧ A_master).
|
||||||
|
"""
|
||||||
|
a = np.asarray(A, dtype=np.float64)
|
||||||
|
b = np.asarray(B, dtype=np.float64)
|
||||||
|
ga, gb = _grade_mass(a), _grade_mass(b)
|
||||||
|
target = min(5, ga + gb)
|
||||||
|
if target == 0:
|
||||||
|
return grade_project(geometric_product(a, b), 0)
|
||||||
|
return grade_project(geometric_product(a, b), target).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def simple_bivector_project(B: np.ndarray) -> np.ndarray:
|
||||||
|
"""Project a multivector generator onto a simple bivector.
|
||||||
|
|
||||||
|
Spec intent: pure grade-2 support that is *simple* (single plane). Pure
|
||||||
|
multiplane grade-2 has nontrivial ``⟨B B⟩₄``; we always collapse to the
|
||||||
|
dominant plane when more than one plane is occupied (deterministic).
|
||||||
|
"""
|
||||||
|
arr = np.asarray(B, dtype=np.float64)
|
||||||
|
if arr.shape != (N_COMPONENTS,):
|
||||||
|
raise AdrDagValidationError("bad_shape", shape=tuple(arr.shape))
|
||||||
|
B2 = grade_project(arr, 2).astype(np.float64)
|
||||||
|
occupied = [i for i in _BIVECTOR_PLANES if abs(float(B2[i])) > _NEAR_ZERO]
|
||||||
|
if len(occupied) <= 1:
|
||||||
|
return B2
|
||||||
|
# Multiplane → dominant-plane collapse (simple by construction).
|
||||||
|
best_i = max(occupied, key=lambda i: abs(float(B2[i])))
|
||||||
|
out = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
out[best_i] = float(B2[best_i])
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def embed_adr_markdown(markdown: str) -> np.ndarray:
|
||||||
|
"""Ψ(M): deterministic SHA-256 → 10 bivector coefficients → simple project.
|
||||||
|
|
||||||
|
Identical markdown ⇒ identical 32-vector (replay pin).
|
||||||
|
"""
|
||||||
|
if not isinstance(markdown, str):
|
||||||
|
raise AdrDagValidationError("not_str", type=type(markdown).__name__)
|
||||||
|
# Empty markdown is a valid document identity (still deterministic).
|
||||||
|
digest = hashlib.sha256(markdown.encode("utf-8")).digest() # 32 bytes
|
||||||
|
B = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
for k, plane in enumerate(_BIVECTOR_PLANES):
|
||||||
|
# 3-byte segments; last 2 hash bytes unused (spec: 10×3=30).
|
||||||
|
chunk = digest[k * 3 : k * 3 + 3]
|
||||||
|
u = int.from_bytes(chunk, "big") # 0 .. 2^24-1
|
||||||
|
c = (u / float(0xFFFFFF)) * 2.0 - 1.0 # [-1, 1]
|
||||||
|
B[plane] = c
|
||||||
|
return simple_bivector_project(B)
|
||||||
|
|
||||||
|
|
||||||
|
def master_architecture_blade(
|
||||||
|
embeddings: Sequence[np.ndarray],
|
||||||
|
) -> np.ndarray:
|
||||||
|
"""Assemble load-bearing ADR embeddings into a master architecture blade.
|
||||||
|
|
||||||
|
Prefer successive wedge when non-degenerate; if a wedge step vanishes
|
||||||
|
(parallel simple planes after projection), fall back to algebraic sum of
|
||||||
|
simple bivectors so the master never fabricates a zero blade.
|
||||||
|
"""
|
||||||
|
if not embeddings:
|
||||||
|
raise AdrDagValidationError("empty_master_set")
|
||||||
|
simples = [
|
||||||
|
simple_bivector_project(np.asarray(e, dtype=np.float64))
|
||||||
|
for e in embeddings
|
||||||
|
]
|
||||||
|
wedge = simples[0].copy()
|
||||||
|
for i, e in enumerate(simples[1:], start=1):
|
||||||
|
w = multivector_wedge(wedge, e)
|
||||||
|
if float(np.linalg.norm(w)) > _NEAR_ZERO:
|
||||||
|
wedge = w
|
||||||
|
# else: parallel/collinear under wedge — keep prior wedge, continue
|
||||||
|
if float(np.linalg.norm(wedge)) > _NEAR_ZERO:
|
||||||
|
return wedge.astype(np.float64)
|
||||||
|
# Full wedge chain degenerate: superposition master (still deterministic).
|
||||||
|
acc = np.zeros(N_COMPONENTS, dtype=np.float64)
|
||||||
|
for e in simples:
|
||||||
|
acc = acc + e
|
||||||
|
if float(np.linalg.norm(acc)) < _NEAR_ZERO:
|
||||||
|
raise AdrDagValidationError("degenerate_master_blade", at_index=0)
|
||||||
|
return simple_bivector_project(acc)
|
||||||
|
|
||||||
|
|
||||||
|
def proposal_drift(B_proposal: np.ndarray, A_master: np.ndarray) -> float:
|
||||||
|
"""Drift = ‖B_p ∧ A_master‖ (Euclidean coeff norm of the wedge)."""
|
||||||
|
Bp = simple_bivector_project(np.asarray(B_proposal, dtype=np.float64))
|
||||||
|
Am = np.asarray(A_master, dtype=np.float64)
|
||||||
|
if Am.shape != (N_COMPONENTS,):
|
||||||
|
raise AdrDagValidationError("bad_master_shape", shape=tuple(Am.shape))
|
||||||
|
w = multivector_wedge(Bp, Am)
|
||||||
|
return float(np.linalg.norm(w))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_proposal_against_master(
|
||||||
|
proposal_markdown: str,
|
||||||
|
master_markdowns: Sequence[str],
|
||||||
|
*,
|
||||||
|
max_drift: float = 1.0,
|
||||||
|
) -> tuple[bool, float, np.ndarray, np.ndarray]:
|
||||||
|
"""Embed proposal + masters; return (ok, drift, B_p, A_master)."""
|
||||||
|
if not master_markdowns:
|
||||||
|
raise AdrDagValidationError("empty_master_set")
|
||||||
|
masters = [embed_adr_markdown(m) for m in master_markdowns]
|
||||||
|
A = master_architecture_blade(masters)
|
||||||
|
Bp = embed_adr_markdown(proposal_markdown)
|
||||||
|
d = proposal_drift(Bp, A)
|
||||||
|
ok = bool(d <= float(max_drift) + _NEAR_ZERO)
|
||||||
|
return ok, d, Bp, A
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"AdrDagValidationError",
|
||||||
|
"embed_adr_markdown",
|
||||||
|
"master_architecture_blade",
|
||||||
|
"multivector_wedge",
|
||||||
|
"proposal_drift",
|
||||||
|
"simple_bivector_project",
|
||||||
|
"validate_proposal_against_master",
|
||||||
|
]
|
||||||
|
|
@ -69,6 +69,14 @@ from core.physics.temporal_gate import (
|
||||||
)
|
)
|
||||||
from core.physics.self_authorship import AuthorshipProposal, SelfAuthorshipMiner
|
from core.physics.self_authorship import AuthorshipProposal, SelfAuthorshipMiner
|
||||||
from core.physics.wave_manifold import WaveManifold
|
from core.physics.wave_manifold import WaveManifold
|
||||||
|
from core.physics.trajectory_invariants import (
|
||||||
|
TrajectoryAssessment,
|
||||||
|
TrajectoryInvariantError,
|
||||||
|
assess_trajectory,
|
||||||
|
energy_boundary_ok,
|
||||||
|
relative_holonomy,
|
||||||
|
trajectory_divergence,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"SalienceOperator", "SalienceMap", "FieldRegion",
|
"SalienceOperator", "SalienceMap", "FieldRegion",
|
||||||
|
|
@ -99,4 +107,7 @@ __all__ = [
|
||||||
"TemporalDecision", "TemporalVerdict",
|
"TemporalDecision", "TemporalVerdict",
|
||||||
"AuthorshipProposal", "SelfAuthorshipMiner",
|
"AuthorshipProposal", "SelfAuthorshipMiner",
|
||||||
"WaveManifold",
|
"WaveManifold",
|
||||||
|
"TrajectoryAssessment", "TrajectoryInvariantError",
|
||||||
|
"assess_trajectory", "energy_boundary_ok",
|
||||||
|
"relative_holonomy", "trajectory_divergence",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
172
core/physics/trajectory_invariants.py
Normal file
172
core/physics/trajectory_invariants.py
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
"""
|
||||||
|
core/physics/trajectory_invariants.py
|
||||||
|
|
||||||
|
Continuous-space trajectory invariants + zero-fabrication (R&D-Revised §2.2 / #21).
|
||||||
|
|
||||||
|
Python geometry-first surface (algebra/*). A Ring-1 Rust port may later mirror
|
||||||
|
this contract under ``core-rs``; this module is the behavioral source of truth.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
* Relative holonomy H(t) = V_i · reverse(V_{i+1})
|
||||||
|
* Divergence D = Σ log(1 + ‖H·reverse(H) − 1‖_F) · Δt (discrete integral)
|
||||||
|
* Replay bound D < ε_trajectory
|
||||||
|
* Hamiltonian energy boundary E_exertion ≤ κ · E_sensory
|
||||||
|
* Zero-fabrication: refuse empty / non-closed trajectory steps
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from algebra.cl41 import N_COMPONENTS, geometric_product, reverse
|
||||||
|
from algebra.versor import versor_condition, versor_unit_residual
|
||||||
|
|
||||||
|
_CLOSURE_TOL = 1e-6
|
||||||
|
_DEFAULT_EPS_TRAJECTORY = 1e-5
|
||||||
|
_NEAR_ZERO = 1e-15
|
||||||
|
|
||||||
|
|
||||||
|
class TrajectoryInvariantError(ValueError):
|
||||||
|
"""Fail-closed refusal from trajectory invariant checks."""
|
||||||
|
|
||||||
|
def __init__(self, reason: str, **disclosure) -> None:
|
||||||
|
self.reason = reason
|
||||||
|
self.disclosure = dict(disclosure)
|
||||||
|
super().__init__(f"trajectory_invariant refused [{reason}]: {self.disclosure}")
|
||||||
|
|
||||||
|
|
||||||
|
def _as_versor(V: np.ndarray, name: str) -> np.ndarray:
|
||||||
|
arr = np.asarray(V, dtype=np.float64)
|
||||||
|
if arr.shape != (N_COMPONENTS,):
|
||||||
|
raise TrajectoryInvariantError(
|
||||||
|
"bad_shape", name=name, shape=tuple(arr.shape)
|
||||||
|
)
|
||||||
|
cond = float(versor_condition(arr))
|
||||||
|
if cond >= _CLOSURE_TOL:
|
||||||
|
raise TrajectoryInvariantError(
|
||||||
|
"not_closed", name=name, versor_condition=cond
|
||||||
|
)
|
||||||
|
return arr
|
||||||
|
|
||||||
|
|
||||||
|
def relative_holonomy(V1: np.ndarray, V2: np.ndarray) -> np.ndarray:
|
||||||
|
"""H = V1 · reverse(V2) — relative transport between consecutive steps."""
|
||||||
|
a = _as_versor(V1, "V1")
|
||||||
|
b = _as_versor(V2, "V2")
|
||||||
|
return geometric_product(a, reverse(b)).astype(np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def holonomy_unit_residual(H: np.ndarray) -> float:
|
||||||
|
"""‖H · reverse(H) − 1‖_F (dual-checked via versor_unit_residual)."""
|
||||||
|
H_arr = np.asarray(H, dtype=np.float64)
|
||||||
|
if H_arr.shape != (N_COMPONENTS,):
|
||||||
|
raise TrajectoryInvariantError("bad_shape", name="H", shape=tuple(H_arr.shape))
|
||||||
|
r = float(versor_unit_residual(H_arr))
|
||||||
|
r_rev = float(versor_unit_residual(reverse(H_arr)))
|
||||||
|
return max(r, r_rev)
|
||||||
|
|
||||||
|
|
||||||
|
def trajectory_divergence(
|
||||||
|
versors: Sequence[np.ndarray],
|
||||||
|
*,
|
||||||
|
dt: float = 1.0,
|
||||||
|
) -> float:
|
||||||
|
"""Discrete divergence integral D = Σ log(1 + residual(H_i)) · Δt.
|
||||||
|
|
||||||
|
Zero-fabrication: empty or single-step trajectories refused (no confabulated
|
||||||
|
path). Each step must be a closed unit versor.
|
||||||
|
"""
|
||||||
|
if not versors:
|
||||||
|
raise TrajectoryInvariantError("empty_trajectory")
|
||||||
|
if len(versors) < 2:
|
||||||
|
raise TrajectoryInvariantError(
|
||||||
|
"trajectory_too_short", n=len(versors)
|
||||||
|
)
|
||||||
|
if float(dt) <= 0.0:
|
||||||
|
raise TrajectoryInvariantError("non_positive_dt", dt=float(dt))
|
||||||
|
|
||||||
|
closed = [_as_versor(v, f"versors[{i}]") for i, v in enumerate(versors)]
|
||||||
|
D = 0.0
|
||||||
|
for i in range(len(closed) - 1):
|
||||||
|
H = relative_holonomy(closed[i], closed[i + 1])
|
||||||
|
r = holonomy_unit_residual(H)
|
||||||
|
D += math.log1p(max(r, 0.0)) * float(dt)
|
||||||
|
return float(D)
|
||||||
|
|
||||||
|
|
||||||
|
def energy_boundary_ok(
|
||||||
|
E_exertion: float,
|
||||||
|
E_sensory: float,
|
||||||
|
*,
|
||||||
|
kappa: float = 1.0,
|
||||||
|
) -> bool:
|
||||||
|
"""Hamiltonian energy boundary: E_exertion ≤ κ · E_sensory.
|
||||||
|
|
||||||
|
Refuse negative energies (zero-fabrication of free action).
|
||||||
|
"""
|
||||||
|
ee = float(E_exertion)
|
||||||
|
es = float(E_sensory)
|
||||||
|
k = float(kappa)
|
||||||
|
if ee < -_NEAR_ZERO or es < -_NEAR_ZERO:
|
||||||
|
raise TrajectoryInvariantError(
|
||||||
|
"negative_energy", E_exertion=ee, E_sensory=es
|
||||||
|
)
|
||||||
|
if k < 0.0:
|
||||||
|
raise TrajectoryInvariantError("negative_kappa", kappa=k)
|
||||||
|
return ee <= k * es + _NEAR_ZERO
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class TrajectoryAssessment:
|
||||||
|
"""Result of assessing a finite trajectory against #21 invariants."""
|
||||||
|
|
||||||
|
divergence: float
|
||||||
|
within_replay_bound: bool
|
||||||
|
energy_ok: bool
|
||||||
|
n_steps: int
|
||||||
|
eps_trajectory: float
|
||||||
|
kappa: float
|
||||||
|
E_exertion: float
|
||||||
|
E_sensory: float
|
||||||
|
|
||||||
|
|
||||||
|
def assess_trajectory(
|
||||||
|
versors: Sequence[np.ndarray],
|
||||||
|
*,
|
||||||
|
E_exertion: float,
|
||||||
|
E_sensory: float,
|
||||||
|
eps_trajectory: float = _DEFAULT_EPS_TRAJECTORY,
|
||||||
|
kappa: float = 1.0,
|
||||||
|
dt: float = 1.0,
|
||||||
|
) -> TrajectoryAssessment:
|
||||||
|
"""Full trajectory gate: divergence bound + energy boundary."""
|
||||||
|
D = trajectory_divergence(versors, dt=dt)
|
||||||
|
eps = float(eps_trajectory)
|
||||||
|
if eps <= 0.0:
|
||||||
|
raise TrajectoryInvariantError("non_positive_eps", eps_trajectory=eps)
|
||||||
|
e_ok = energy_boundary_ok(E_exertion, E_sensory, kappa=kappa)
|
||||||
|
return TrajectoryAssessment(
|
||||||
|
divergence=float(D),
|
||||||
|
within_replay_bound=bool(D < eps),
|
||||||
|
energy_ok=bool(e_ok),
|
||||||
|
n_steps=len(versors),
|
||||||
|
eps_trajectory=eps,
|
||||||
|
kappa=float(kappa),
|
||||||
|
E_exertion=float(E_exertion),
|
||||||
|
E_sensory=float(E_sensory),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"TrajectoryAssessment",
|
||||||
|
"TrajectoryInvariantError",
|
||||||
|
"assess_trajectory",
|
||||||
|
"energy_boundary_ok",
|
||||||
|
"holonomy_unit_residual",
|
||||||
|
"relative_holonomy",
|
||||||
|
"trajectory_divergence",
|
||||||
|
]
|
||||||
|
|
@ -35,8 +35,8 @@
|
||||||
| 4 | GoldTether residual + α law | Super §2.3, R&D §2.3/§5 | 🟢 residual+α + bootstrap gates + principal-axis prune (#24+#18) | #18 |
|
| 4 | GoldTether residual + α law | Super §2.3, R&D §2.3/§5 | 🟢 residual+α + bootstrap gates + principal-axis prune (#24+#18) | #18 |
|
||||||
| 5 | Grade-5 pseudoscalar invariant | Super §3.3 | ⚪ RETIRED — vacuous in odd-dim Cl(4,1) | #19 (closed) |
|
| 5 | Grade-5 pseudoscalar invariant | Super §3.3 | ⚪ RETIRED — vacuous in odd-dim Cl(4,1) | #19 (closed) |
|
||||||
| 6 | Surprise residual operator | Super §3.2 | 🟢 math + DiscoveryCandidate wiring landed (#26 + #31) | #20 |
|
| 6 | Surprise residual operator | Super §3.2 | 🟢 math + DiscoveryCandidate wiring landed (#26 + #31) | #20 |
|
||||||
| 7 | Trajectory invariants + zero-fabrication | R&D §2.2 | ⚫ absent | #21 |
|
| 7 | Trajectory invariants + zero-fabrication | R&D §2.2 | 🟢 Python geometry surface (`trajectory_invariants.py`) | #21 |
|
||||||
| 8 | ADR-DAG conformal embedding | R&D §2.4 | ⚫ absent | #21 |
|
| 8 | ADR-DAG conformal embedding | R&D §2.4 | 🟢 Python surface (`core/adr/validator.py`) | #21 |
|
||||||
| W1 | WaveManifold unitary / sandwich step | ADR-0241 §2 | 🟢 | ADR-0241 |
|
| W1 | WaveManifold unitary / sandwich step | ADR-0241 §2 | 🟢 | ADR-0241 |
|
||||||
| W2 | Spectral leakage surprise | ADR-0241 §2.4B | 🟢 subsumed into `surprise_residual` | ADR-0241 |
|
| W2 | Spectral leakage surprise | ADR-0241 §2.4B | 🟢 subsumed into `surprise_residual` | ADR-0241 |
|
||||||
| W3 | Wave polar + multi-pair conjugacy | ADR-0241 §2.4A | 🟢 single polar + multi-pair thin wrap | ADR-0241 |
|
| W3 | Wave polar + multi-pair conjugacy | ADR-0241 §2.4A | 🟢 single polar + multi-pair thin wrap | ADR-0241 |
|
||||||
|
|
@ -206,10 +206,23 @@ The null add-on is **untested**: `test_signature_aware_pca_keeps_nulls` classifi
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Absent whole proposals — ⚫ (#21)
|
## 9. Trajectory invariants + ADR-DAG — 🟢 Python surfaces (#21)
|
||||||
|
|
||||||
- **Trajectory invariants + zero-fabrication (R&D §2.2 → `core-rs/src/sensorimotor.rs`):** relative holonomy `H(t)=V₁Ṽ₂`, divergence integral `D < ε_trajectory`, Hamiltonian energy boundary `E_exertion ≤ κ·E_sensory`. Not landed. Gated by the Zig/Rust substrate doctrine (Ring-1 only).
|
> **Landed (2026-07-14):** geometry-first Python authorities. Rust Ring-1 port remains optional acceleration, not a second truth.
|
||||||
- **ADR-DAG conformal embedding (R&D §2.4 → `core/adr/validator.py`):** `Ψ(M)` = SHA-256 → 10 bivector coeffs → simple-bivector projection → master-blade wedge → drift check. Not landed. Cross-check `core/abi/geometric_delta_validator.py` before adding a parallel validator.
|
|
||||||
|
### Trajectory invariants (R&D §2.2 → `core/physics/trajectory_invariants.py`)
|
||||||
|
- Relative holonomy `H = V_i · reverse(V_{i+1})`
|
||||||
|
- Divergence `D = Σ log(1 + ‖H reverse(H) − 1‖) · Δt`; bound `D < ε_trajectory`
|
||||||
|
- Energy boundary `E_exertion ≤ κ · E_sensory`
|
||||||
|
- Zero-fabrication: empty / short / non-closed steps refused
|
||||||
|
- Tests: `tests/test_third_door_trajectory_invariants.py`
|
||||||
|
|
||||||
|
### ADR-DAG conformal embedding (R&D §2.4 → `core/adr/validator.py`)
|
||||||
|
- `Ψ(M)`: SHA-256 → 10×3-byte → `c_k ∈ [−1,1]` → planes 6..15 → simple-bivector project
|
||||||
|
- Master blade = successive wedge of load-bearing ADR embeddings
|
||||||
|
- Proposal drift = `‖B_p ∧ A_master‖`
|
||||||
|
- Does **not** parallel `core/abi/geometric_delta_validator.py` (that validates GeometricDelta ABI envelopes)
|
||||||
|
- Tests: `tests/test_third_door_adr_dag_embedding.py`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -297,7 +310,7 @@ PY
|
||||||
- Durable holographic memory **vault store** (CRDT-backed standing-wave spectrum) — session registry only today.
|
- Durable holographic memory **vault store** (CRDT-backed standing-wave spectrum) — session registry only today.
|
||||||
- Rust/MLX acceleration of exp-map / cross-spectral (ADR-0235 later).
|
- Rust/MLX acceleration of exp-map / cross-spectral (ADR-0235 later).
|
||||||
- Full ADR-0092 reviewer-service integration (promote remains caller-gated).
|
- Full ADR-0092 reviewer-service integration (promote remains caller-gated).
|
||||||
- R&D #21 trajectory invariants + ADR-DAG embedding.
|
- Optional Rust Ring-1 port of trajectory invariants (Python is authority today).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -310,7 +323,7 @@ PY
|
||||||
| GoldTether gold-set + harmonized residual + α=Φ(R) + bootstrap/prune — 🟢 | #18 |
|
| GoldTether gold-set + harmonized residual + α=Φ(R) + bootstrap/prune — 🟢 | #18 |
|
||||||
| Grade-5 pseudoscalar preservation gate — ⚪ RETIRED (vacuous; see §5) | #19 (closed) |
|
| Grade-5 pseudoscalar preservation gate — ⚪ RETIRED (vacuous; see §5) | #19 (closed) |
|
||||||
| Surprise: metric projection + productivity polarity + DiscoveryCandidate wiring — 🟢 done | #20 (math #26; wiring #31) |
|
| Surprise: metric projection + productivity polarity + DiscoveryCandidate wiring — 🟢 done | #20 (math #26; wiring #31) |
|
||||||
| Absent proposals: sensorimotor + ADR-DAG | #21 |
|
| Trajectory invariants + ADR-DAG embedding — 🟢 Python surfaces | #21 |
|
||||||
| Wave-field substrate + operator subsumption (W1–W6) — 🟢 on branch | ADR-0241 |
|
| Wave-field substrate + operator subsumption (W1–W6) — 🟢 on branch | ADR-0241 |
|
||||||
| `core_ha` deprecation — 🟢 no live tree + hygiene pin | ADR-0241 / deprecation plan |
|
| `core_ha` deprecation — 🟢 no live tree + hygiene pin | ADR-0241 / deprecation plan |
|
||||||
| Durable holographic vault spectrum — deferred | ADR-0241 follow-on |
|
| Durable holographic vault spectrum — deferred | ADR-0241 follow-on |
|
||||||
|
|
|
||||||
98
tests/test_third_door_adr_dag_embedding.py
Normal file
98
tests/test_third_door_adr_dag_embedding.py
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
"""#21 B — ADR-DAG conformal embedding Ψ(M) (R&D §2.4)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from algebra.cl41 import grade_project
|
||||||
|
from core.adr.validator import (
|
||||||
|
AdrDagValidationError,
|
||||||
|
embed_adr_markdown,
|
||||||
|
master_architecture_blade,
|
||||||
|
proposal_drift,
|
||||||
|
simple_bivector_project,
|
||||||
|
validate_proposal_against_master,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_embed_deterministic_replay():
|
||||||
|
m = "# ADR-TEST\n\nBody of the decision.\n"
|
||||||
|
a = embed_adr_markdown(m)
|
||||||
|
b = embed_adr_markdown(m)
|
||||||
|
assert np.array_equal(a, b)
|
||||||
|
assert a.shape == (32,)
|
||||||
|
|
||||||
|
|
||||||
|
def test_embed_differs_for_different_markdown():
|
||||||
|
a = embed_adr_markdown("# ADR-A\nfoo")
|
||||||
|
b = embed_adr_markdown("# ADR-B\nbar")
|
||||||
|
assert not np.allclose(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
def test_embed_is_grade2_supported():
|
||||||
|
B = embed_adr_markdown("# ADR-G2\ncontent")
|
||||||
|
# After simple project: only grade-2 (and zeros elsewhere)
|
||||||
|
for g in (0, 1, 3, 4, 5):
|
||||||
|
assert float(np.linalg.norm(grade_project(B, g))) < 1e-12
|
||||||
|
assert float(np.linalg.norm(grade_project(B, 2))) > 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_simple_bivector_project_collapses_multiplane():
|
||||||
|
B = np.zeros(32, dtype=np.float64)
|
||||||
|
B[6] = 0.9
|
||||||
|
B[7] = 0.8
|
||||||
|
B[8] = 0.1
|
||||||
|
S = simple_bivector_project(B)
|
||||||
|
# Dominant plane kept
|
||||||
|
assert abs(S[6] - 0.9) < 1e-12 or abs(S[7] - 0.8) < 1e-12
|
||||||
|
# At most one plane nonzero after collapse when multiplane non-simple
|
||||||
|
n_planes = sum(1 for i in range(6, 16) if abs(S[i]) > 1e-12)
|
||||||
|
assert n_planes == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_master_blade_refuses_empty():
|
||||||
|
with pytest.raises(AdrDagValidationError, match="empty"):
|
||||||
|
master_architecture_blade([])
|
||||||
|
|
||||||
|
|
||||||
|
def test_master_blade_from_two_adrs():
|
||||||
|
e1 = embed_adr_markdown("# ADR-0003\nCoordinate dissolution.")
|
||||||
|
e2 = embed_adr_markdown("# ADR-0006\nField energy.")
|
||||||
|
A = master_architecture_blade([e1, e2])
|
||||||
|
assert A.shape == (32,)
|
||||||
|
assert float(np.linalg.norm(A)) > 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_proposal_drift_nonneg_and_deterministic():
|
||||||
|
masters = [
|
||||||
|
embed_adr_markdown("# M1\none"),
|
||||||
|
embed_adr_markdown("# M2\ntwo"),
|
||||||
|
]
|
||||||
|
A = master_architecture_blade(masters)
|
||||||
|
Bp = embed_adr_markdown("# Proposal\nnew idea")
|
||||||
|
d1 = proposal_drift(Bp, A)
|
||||||
|
d2 = proposal_drift(Bp, A)
|
||||||
|
assert d1 == d2
|
||||||
|
assert d1 >= 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_proposal_returns_ok_flag():
|
||||||
|
masters_md = ["# ADR-A\nalpha", "# ADR-B\nbeta"]
|
||||||
|
ok, drift, Bp, A = validate_proposal_against_master(
|
||||||
|
"# Proposal\ngamma", masters_md, max_drift=1e9
|
||||||
|
)
|
||||||
|
assert ok is True
|
||||||
|
assert drift >= 0.0
|
||||||
|
assert Bp.shape == (32,)
|
||||||
|
assert A.shape == (32,)
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_proposal_tight_max_drift_can_fail():
|
||||||
|
masters_md = ["# ADR-A\nalpha", "# ADR-B\nbeta"]
|
||||||
|
ok, drift, _Bp, _A = validate_proposal_against_master(
|
||||||
|
"# Proposal\nentirely different body xyz", masters_md, max_drift=-1.0
|
||||||
|
)
|
||||||
|
# max_drift < 0 forces fail unless drift is negative (impossible)
|
||||||
|
assert ok is False
|
||||||
|
assert drift >= 0.0
|
||||||
92
tests/test_third_door_trajectory_invariants.py
Normal file
92
tests/test_third_door_trajectory_invariants.py
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
"""#21 A — trajectory invariants + zero-fabrication (R&D §2.2)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from algebra.rotor import make_rotor_from_angle
|
||||||
|
from algebra.versor import versor_condition
|
||||||
|
from core.physics.trajectory_invariants import (
|
||||||
|
TrajectoryInvariantError,
|
||||||
|
assess_trajectory,
|
||||||
|
energy_boundary_ok,
|
||||||
|
relative_holonomy,
|
||||||
|
trajectory_divergence,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _id() -> np.ndarray:
|
||||||
|
v = np.zeros(32, dtype=np.float64)
|
||||||
|
v[0] = 1.0
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def test_relative_holonomy_identity_pair_is_identity():
|
||||||
|
H = relative_holonomy(_id(), _id())
|
||||||
|
assert versor_condition(H) < 1e-6
|
||||||
|
assert abs(H[0] - 1.0) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
|
def test_relative_holonomy_order_sensitive():
|
||||||
|
A = make_rotor_from_angle(0.3, bivector_idx=6)
|
||||||
|
B = make_rotor_from_angle(0.9, bivector_idx=7)
|
||||||
|
H_ab = relative_holonomy(A, B)
|
||||||
|
H_ba = relative_holonomy(B, A)
|
||||||
|
assert not np.allclose(H_ab, H_ba, atol=1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_divergence_zero_on_constant_path():
|
||||||
|
path = [_id(), _id(), _id()]
|
||||||
|
assert trajectory_divergence(path) == 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_divergence_positive_on_moving_path():
|
||||||
|
path = [
|
||||||
|
make_rotor_from_angle(0.1 * i, bivector_idx=6) for i in range(1, 6)
|
||||||
|
]
|
||||||
|
D = trajectory_divergence(path)
|
||||||
|
assert D > 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_divergence_deterministic():
|
||||||
|
path = [make_rotor_from_angle(0.2 * i) for i in range(1, 4)]
|
||||||
|
assert trajectory_divergence(path) == trajectory_divergence(path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_divergence_refuses_empty_and_short():
|
||||||
|
with pytest.raises(TrajectoryInvariantError, match="empty"):
|
||||||
|
trajectory_divergence([])
|
||||||
|
with pytest.raises(TrajectoryInvariantError, match="too_short"):
|
||||||
|
trajectory_divergence([_id()])
|
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_divergence_refuses_non_closed():
|
||||||
|
dirty = np.zeros(32, dtype=np.float64)
|
||||||
|
dirty[0] = 0.5
|
||||||
|
dirty[1] = 0.5
|
||||||
|
with pytest.raises(TrajectoryInvariantError, match="not_closed"):
|
||||||
|
trajectory_divergence([_id(), dirty])
|
||||||
|
|
||||||
|
|
||||||
|
def test_energy_boundary_ok_and_refuse_negative():
|
||||||
|
assert energy_boundary_ok(1.0, 2.0, kappa=1.0) is True
|
||||||
|
assert energy_boundary_ok(3.0, 2.0, kappa=1.0) is False
|
||||||
|
assert energy_boundary_ok(3.0, 2.0, kappa=2.0) is True
|
||||||
|
with pytest.raises(TrajectoryInvariantError, match="negative_energy"):
|
||||||
|
energy_boundary_ok(-0.1, 1.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_assess_trajectory_replay_bound():
|
||||||
|
path = [make_rotor_from_angle(0.05 * i) for i in range(1, 4)]
|
||||||
|
a = assess_trajectory(
|
||||||
|
path, E_exertion=0.5, E_sensory=1.0, eps_trajectory=1.0, kappa=1.0
|
||||||
|
)
|
||||||
|
assert a.energy_ok is True
|
||||||
|
assert a.n_steps == 3
|
||||||
|
assert a.divergence >= 0.0
|
||||||
|
# Tight eps → may fail replay bound on moving path
|
||||||
|
tight = assess_trajectory(
|
||||||
|
path, E_exertion=0.5, E_sensory=1.0, eps_trajectory=1e-30, kappa=1.0
|
||||||
|
)
|
||||||
|
assert tight.within_replay_bound is False
|
||||||
Loading…
Reference in a new issue