fix(algebra): P11a physics hot paths via algebra.backend (Rust-ready)
Stop wave/Third-Door physics from bypassing native dispatch: - Route geometric_product / versor_apply / versor_condition / cga_inner through algebra.backend in wave_manifold, goldtether, trajectory, dynamic_manifold, surprise, holographic_vault, atlas_packing, biography, self_authorship. - Backend: dtype-aware Rust use — f32 workloads use core_rs; f64 wave residual pins keep Python SOT until f64 GP parity exists. Coerce arrays for PyO3 bindings; fail soft to Python. - AST hygiene pin: tests/test_physics_backend_dispatch_hygiene.py - Docs: RUST.md, runtime_contracts, fidelity (ADR-0235 / UMA hygiene). Verified: wave + cohesion suites green default and CORE_BACKEND=rust (with core_rs built). MLX still exploratory off-serve.
This commit is contained in:
parent
db6430ed4e
commit
44f7258b16
14 changed files with 208 additions and 25 deletions
|
|
@ -48,9 +48,36 @@ def _build_cga_inner_metric() -> np.ndarray:
|
|||
_CGA_INNER_METRIC: np.ndarray = _build_cga_inner_metric()
|
||||
|
||||
|
||||
def _f32_1d32(x: np.ndarray) -> np.ndarray:
|
||||
"""Contiguous f32 (32,) for core_rs PyReadonlyArray1 bindings."""
|
||||
return np.ascontiguousarray(
|
||||
np.asarray(x, dtype=np.float32).reshape(-1)[:32], dtype=np.float32
|
||||
)
|
||||
|
||||
|
||||
def _is_f32_workload(*arrays: np.ndarray) -> bool:
|
||||
"""True when all arrays are float32 (Rust f32 kernel is parity-safe).
|
||||
|
||||
float64 wave residual pins require Python SOT (or future f64 Rust GP).
|
||||
Forcing f64→f32 would break 1e-9 chiral / leakage pins (ADR-0241).
|
||||
"""
|
||||
return all(np.asarray(a).dtype == np.float32 for a in arrays)
|
||||
|
||||
|
||||
def geometric_product(A: np.ndarray, B: np.ndarray) -> np.ndarray:
|
||||
if _RUST:
|
||||
return np.asarray(_rs.geometric_product(A, B), dtype=np.float32)
|
||||
"""Cl(4,1) geometric product via Rust f32 when enabled, else Python.
|
||||
|
||||
float64 inputs always use the pure-Python product (semantic SOT for
|
||||
wave-field residual math). float32 field-graph workloads get Rust.
|
||||
"""
|
||||
if _RUST and _is_f32_workload(A, B):
|
||||
try:
|
||||
return np.asarray(
|
||||
_rs.geometric_product(_f32_1d32(A), _f32_1d32(B)),
|
||||
dtype=np.float32,
|
||||
)
|
||||
except (AttributeError, TypeError, ValueError, Exception):
|
||||
pass
|
||||
from algebra.cl41 import geometric_product as _gp
|
||||
return _gp(A, B)
|
||||
|
||||
|
|
@ -67,25 +94,34 @@ def versor_apply(V: np.ndarray, F: np.ndarray) -> np.ndarray:
|
|||
"""
|
||||
if _RUST:
|
||||
try:
|
||||
Vc = np.ascontiguousarray(V, dtype=np.float64)
|
||||
Fc = np.ascontiguousarray(F, dtype=np.float64)
|
||||
return np.asarray(_rs.versor_apply_with_closure_f64(Vc, Fc), dtype=np.float64)
|
||||
except (AttributeError, Exception):
|
||||
Vc = np.ascontiguousarray(V, dtype=np.float64).reshape(-1)[:32]
|
||||
Fc = np.ascontiguousarray(F, dtype=np.float64).reshape(-1)[:32]
|
||||
return np.asarray(
|
||||
_rs.versor_apply_with_closure_f64(Vc, Fc), dtype=np.float64
|
||||
)
|
||||
except (AttributeError, TypeError, ValueError, Exception):
|
||||
pass
|
||||
from algebra.versor import versor_apply as _va
|
||||
return _va(V, F)
|
||||
|
||||
|
||||
def versor_condition(F: np.ndarray) -> float:
|
||||
if _RUST:
|
||||
return float(_rs.versor_condition(F))
|
||||
"""Versor residual. Rust f32 path only for float32 inputs (see GP note)."""
|
||||
if _RUST and _is_f32_workload(F):
|
||||
try:
|
||||
return float(_rs.versor_condition(_f32_1d32(F)))
|
||||
except (AttributeError, TypeError, ValueError, Exception):
|
||||
pass
|
||||
from algebra.versor import versor_condition as _vc
|
||||
return _vc(F)
|
||||
|
||||
|
||||
def cga_inner(X: np.ndarray, Y: np.ndarray) -> float:
|
||||
if _RUST:
|
||||
return float(_rs.cga_inner(X, Y))
|
||||
if _RUST and _is_f32_workload(X, Y):
|
||||
try:
|
||||
return float(_rs.cga_inner(_f32_1d32(X), _f32_1d32(Y)))
|
||||
except (AttributeError, TypeError, ValueError, Exception):
|
||||
pass
|
||||
from algebra.cga import cga_inner as _ci
|
||||
return _ci(X, Y)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ from typing import Sequence
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cga import cga_inner, embed_point
|
||||
from algebra.backend import cga_inner
|
||||
from algebra.cga import embed_point
|
||||
from core.physics.wave_manifold import WaveManifold
|
||||
|
||||
PHI = (1.0 + math.sqrt(5.0)) / 2.0
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ from typing import Any, Sequence
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.backend import versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from algebra.holonomy import holonomy_encode, holonomy_similarity
|
||||
from algebra.versor import unitize_versor, versor_condition
|
||||
from algebra.versor import unitize_versor
|
||||
from core.physics.wave_manifold import WaveManifold
|
||||
|
||||
_CLOSURE_TOL = 1e-6
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ from typing import Optional, Sequence, Tuple, Union
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.backend import geometric_product, versor_condition
|
||||
from algebra.cga import is_null
|
||||
from algebra.cl41 import N_COMPONENTS, geometric_product, grade_project, reverse
|
||||
from algebra.cl41 import N_COMPONENTS, grade_project, reverse
|
||||
from algebra.null_point import (
|
||||
NullPointRecoveryError,
|
||||
dilator,
|
||||
|
|
@ -26,7 +27,6 @@ from algebra.null_point import (
|
|||
translator,
|
||||
)
|
||||
from algebra.rotor import rotor_power, word_transition_rotor
|
||||
from algebra.versor import versor_condition
|
||||
|
||||
_CLOSURE_TOL = 1e-6
|
||||
_NEAR_ZERO = 1e-12
|
||||
|
|
|
|||
|
|
@ -28,9 +28,10 @@ from typing import Any, Literal, Optional, Tuple
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cl41 import N_COMPONENTS, geometric_product, reverse
|
||||
from algebra.backend import geometric_product, versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS, reverse
|
||||
from algebra.rotor import rotor_power, word_transition_rotor
|
||||
from algebra.versor import versor_condition, versor_unit_residual
|
||||
from algebra.versor import versor_unit_residual
|
||||
from core.physics.wave_manifold import WaveManifold
|
||||
|
||||
_CLOSURE_TOL = 1e-6
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ from typing import Any, Optional
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.backend import versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from algebra.versor import versor_condition
|
||||
from core.physics.wave_manifold import WaveManifold
|
||||
from teaching.epistemic import EpistemicStatus
|
||||
from vault.store import VaultStore, _parse_entry_status, _status_admits
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ from typing import Any, Mapping, Sequence
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.backend import versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS
|
||||
from algebra.versor import versor_condition
|
||||
from core.physics.dynamic_manifold import conformal_procrustes
|
||||
from core.physics.goldtether import GoldTetherMonitor, coherence_residual
|
||||
from core.physics.surprise import dual_procrustes_surprise, surprise_residual
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ from typing import Optional, Sequence, Tuple, Union
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cga import cga_inner
|
||||
from algebra.backend import cga_inner, versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS, grade_project
|
||||
from algebra.versor import versor_condition
|
||||
from core.physics.dynamic_manifold import conformal_procrustes
|
||||
from core.physics.wave_manifold import WaveManifold, WaveSpectralLeakageError
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ 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
|
||||
from algebra.backend import geometric_product, versor_condition
|
||||
from algebra.cl41 import N_COMPONENTS, reverse
|
||||
from algebra.versor import versor_unit_residual
|
||||
|
||||
_CLOSURE_TOL = 1e-6
|
||||
_DEFAULT_EPS_TRAJECTORY = 1e-5
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ Continuous multivector wave fields ψ ∈ ℝ³² under:
|
|||
|
||||
Algebra-native only (algebra/*). No scipy-as-truth. No teaching/vault imports.
|
||||
Off-serving until explicit gates; dual-checked unitary residual.
|
||||
|
||||
**Backend dispatch (P11a / ADR-0235):** Cl(4,1) hot ops go through
|
||||
``algebra.backend`` so ``CORE_BACKEND=rust`` can accelerate them when
|
||||
``core_rs`` is built. Python remains semantic source of truth when Rust
|
||||
is unset. Helpers without a Rust path (``reverse``, ``scalar_part``,
|
||||
``versor_unit_residual``) stay on pure algebra modules.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -20,9 +26,14 @@ from typing import Any, Sequence, Tuple
|
|||
|
||||
import numpy as np
|
||||
|
||||
from algebra.cga import cga_inner
|
||||
from algebra.cl41 import N_COMPONENTS, geometric_product, reverse, scalar_part
|
||||
from algebra.versor import versor_apply, versor_condition, versor_unit_residual
|
||||
from algebra.backend import (
|
||||
cga_inner,
|
||||
geometric_product,
|
||||
versor_apply,
|
||||
versor_condition,
|
||||
)
|
||||
from algebra.cl41 import N_COMPONENTS, reverse, scalar_part
|
||||
from algebra.versor import versor_unit_residual
|
||||
|
||||
_CLOSURE_TOL = 1e-6
|
||||
_NEAR_ZERO = 1e-12
|
||||
|
|
|
|||
26
docs/RUST.md
26
docs/RUST.md
|
|
@ -1,5 +1,31 @@
|
|||
# Rust Extension (core-rs)
|
||||
|
||||
## Physics hot-path hygiene (P11a)
|
||||
|
||||
Third-Door / ADR-0241 physics modules must import Cl(4,1) multiplies and
|
||||
closure residual helpers from **`algebra.backend`**, not directly from
|
||||
`algebra.cl41` / `algebra.versor` / `algebra.cga` for:
|
||||
|
||||
- `geometric_product`
|
||||
- `versor_apply`
|
||||
- `versor_condition`
|
||||
- `cga_inner`
|
||||
|
||||
Pinned by `tests/test_physics_backend_dispatch_hygiene.py`.
|
||||
|
||||
```bash
|
||||
# Default: pure Python (semantic SOT)
|
||||
uv run pytest tests/test_adr_0241_wave_manifold.py -q
|
||||
|
||||
# Apple Silicon / native acceleration (after maturin build)
|
||||
export CORE_BACKEND=rust
|
||||
uv run --with maturin maturin develop --release --manifest-path core-rs/Cargo.toml
|
||||
uv run python -c "from algebra.backend import using_rust; assert using_rust()"
|
||||
```
|
||||
|
||||
MLX remains an **exploratory** UMA lane (ADR-0235); not required for serving.
|
||||
|
||||
|
||||
## Why Rust
|
||||
|
||||
The active Rust extension is an opt-in native substrate for parity-gated hot
|
||||
|
|
|
|||
|
|
@ -306,6 +306,7 @@ PY
|
|||
| Fibonacci-word scheduler (V4) | 🟢 `fibonacci_word_schedule` (telemetry only) |
|
||||
| Fibonacci anyons (V5) | 🟢 quarantine package only; zero production imports |
|
||||
| Sensorium → ψ feed (I-04) | 🟢 `sensorium_wave_feed` (fake packets + real \(\rho\)) |
|
||||
| Physics Cl(4,1) via `algebra.backend` (P11a) | 🟢 wave/goldtether/trajectory/procrustes/surprise/vault/packing; AST pin; Rust when `CORE_BACKEND=rust` |
|
||||
| Contemplation Trace A SPECULATIVE holographic seal (P9) | 🟢 `core/contemplation/wave_seam.py` (hypothesis vs COHERENT evidence) |
|
||||
| Energy boundary + multi-scale τ (P10 Trace B) | 🟢 `wave_energy_boundary` (wave residual → energy/trajectory; τ_n=F_n·τ_0; E0–E1 crystallization) |
|
||||
|
||||
|
|
|
|||
|
|
@ -919,3 +919,17 @@ Acceptance inventory: `docs/audit/adr_0241_cohesion_acceptance_checklist.md`.
|
|||
|
||||
Every wave transition still obeys `versor_condition(F) < 1e-6`. Residual breach
|
||||
is **fail-closed** — no hot-path nearest-versor drift repair.
|
||||
|
||||
### Algebra backend / Apple Silicon (P11a hygiene)
|
||||
|
||||
Cl(4,1) hot ops in physics (`geometric_product`, `versor_apply`,
|
||||
`versor_condition`, `cga_inner`) must import from **`algebra.backend`**, not
|
||||
direct pure-algebra modules. Pin: `tests/test_physics_backend_dispatch_hygiene.py`.
|
||||
|
||||
| Mode | How |
|
||||
|------|-----|
|
||||
| Default | Pure Python (semantic source of truth) |
|
||||
| Native accel | `CORE_BACKEND=rust` + `core_rs` built (`maturin develop --release -m core-rs/Cargo.toml`) |
|
||||
| float64 wave residual pins | Stay on Python product when inputs are f64 (Rust f32 GP not parity-safe for 1e-9 pins yet) |
|
||||
| float32 field graphs | Rust f32 GP / residual when enabled |
|
||||
| MLX / UMA | Exploratory (ADR-0235); not serving until parity gates |
|
||||
|
|
|
|||
92
tests/test_physics_backend_dispatch_hygiene.py
Normal file
92
tests/test_physics_backend_dispatch_hygiene.py
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
"""P11a — physics hot paths must dispatch Cl(4,1) ops via algebra.backend.
|
||||
|
||||
Prevents silent drift back to pure-Python-only imports for geometric_product /
|
||||
versor_apply / cga_inner / versor_condition in wave-field and related modules.
|
||||
Python remains default when CORE_BACKEND is unset; Rust accelerates when
|
||||
CORE_BACKEND=rust and core_rs is built (ADR-0235).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
_ROOT = Path(__file__).resolve().parents[1]
|
||||
_PHYSICS = _ROOT / "core" / "physics"
|
||||
|
||||
# Modules that perform Cl(4,1) multiplies / residuals in the cognitive physics
|
||||
# layer — must take the load-bearing ops from algebra.backend.
|
||||
_BACKEND_HOT_MODULES = (
|
||||
"wave_manifold.py",
|
||||
"goldtether.py",
|
||||
"trajectory_invariants.py",
|
||||
"dynamic_manifold.py",
|
||||
"surprise.py",
|
||||
"holographic_vault.py",
|
||||
"atlas_packing.py",
|
||||
"biography.py",
|
||||
"self_authorship.py",
|
||||
)
|
||||
|
||||
# Names that must not be imported from algebra.cl41 / algebra.versor / algebra.cga
|
||||
# in those modules (use backend instead).
|
||||
_BANNED_FROM_PURE = frozenset(
|
||||
{
|
||||
"geometric_product",
|
||||
"versor_apply",
|
||||
"versor_condition",
|
||||
"cga_inner",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _imports_from(module: str, path: Path) -> set[str]:
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
names: set[str] = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ImportFrom) and node.module == module:
|
||||
for alias in node.names:
|
||||
names.add(alias.name)
|
||||
return names
|
||||
|
||||
|
||||
def test_hot_modules_import_backend_for_algebra_ops():
|
||||
for name in _BACKEND_HOT_MODULES:
|
||||
path = _PHYSICS / name
|
||||
assert path.is_file(), f"missing {path}"
|
||||
backend_names = _imports_from("algebra.backend", path)
|
||||
# Each file should pull at least one of the dispatch ops.
|
||||
assert backend_names & _BANNED_FROM_PURE, (
|
||||
f"{name} must import Cl(4,1) hot ops from algebra.backend; "
|
||||
f"got backend imports {sorted(backend_names)}"
|
||||
)
|
||||
|
||||
|
||||
def test_hot_modules_do_not_import_hot_ops_from_pure_algebra():
|
||||
pure_modules = ("algebra.cl41", "algebra.versor", "algebra.cga")
|
||||
for name in _BACKEND_HOT_MODULES:
|
||||
path = _PHYSICS / name
|
||||
for mod in pure_modules:
|
||||
pure_names = _imports_from(mod, path)
|
||||
offenders = pure_names & _BANNED_FROM_PURE
|
||||
assert not offenders, (
|
||||
f"{name} imports {sorted(offenders)} from {mod}; "
|
||||
"route through algebra.backend for CORE_BACKEND=rust"
|
||||
)
|
||||
|
||||
|
||||
def test_wave_manifold_uses_backend_geometric_product_and_versor_apply():
|
||||
path = _PHYSICS / "wave_manifold.py"
|
||||
backend = _imports_from("algebra.backend", path)
|
||||
assert "geometric_product" in backend
|
||||
assert "versor_apply" in backend
|
||||
assert "versor_condition" in backend
|
||||
assert "cga_inner" in backend
|
||||
|
||||
|
||||
def test_backend_using_rust_api_exists():
|
||||
from algebra.backend import using_rust
|
||||
|
||||
# Default env: Python path (no silent force of Rust).
|
||||
assert using_rust() is False or using_rust() is True # bool only
|
||||
assert isinstance(using_rust(), bool)
|
||||
Loading…
Reference in a new issue