core/tests/test_rotor_power.py
Shay 2050b77ab2 feat(third-door): real Cartan–Iwasawa null-point peel + full Kabsch-conformal Procrustes (#16 #17)
- Cartan: recover_dilation → peel D → recover_translation → peel T;
  Spin remainder for non-similarities; strict close (no seed-to-rotor);
  recon residual fallback. Flips fidelity xfail.
- Procrustes: full 5-D Kabsch on null-point clouds; field conjugacy via
  raw sandwich + Spin GN; delete word_transition_rotor averaging path.
  Non-vacuous harness fixture.
- rotor_power: null-bivector power (a+B)^α = a^α + α a^{α-1} B so
  translators no longer silently zero under dual-slerp.
- Ledger scorecard: #2 and #3🟢; #4 remains 🟡 (bootstrap deferred).

549 passed (fidelity + ADR-0239 + null_point + 0240 + rotor_power).
2026-07-13 17:07:42 -07:00

96 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tests for algebra.rotor.rotor_power — manifold-preserving rotor scaling.
The drift-fix #2 originally used linear interpolation between a rotor and
identity, which produced multivectors with versor_condition ≈ 10⁻², violating
the non-negotiable 1e-6 invariant. ``rotor_power`` replaces that with a proper
slerp on the rotor manifold: identity -> R^α stays on the manifold for any α.
"""
from __future__ import annotations
import numpy as np
import pytest
from algebra.rotor import make_rotor_from_angle, rotor_power, word_transition_rotor
from algebra.versor import versor_condition
_TOL = 1e-6
@pytest.mark.parametrize("angle", [0.05, 0.3, 0.7, 1.2, np.pi / 2])
@pytest.mark.parametrize("alpha", [0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0])
def test_rotor_power_preserves_versor_closure(angle: float, alpha: float) -> None:
"""For any rotation rotor and any fractional power, output is a closed unit rotor."""
R = make_rotor_from_angle(angle, bivector_idx=7)
R_alpha = rotor_power(R, alpha)
assert versor_condition(R_alpha) < _TOL, (
f"rotor_power(R(angle={angle}), {alpha}) violates closure: "
f"versor_condition = {versor_condition(R_alpha):.3e}"
)
def test_rotor_power_alpha_zero_returns_identity() -> None:
R = make_rotor_from_angle(0.7, bivector_idx=7)
R_zero = rotor_power(R, 0.0)
expected = np.zeros(32, dtype=R_zero.dtype)
expected[0] = 1.0
np.testing.assert_allclose(R_zero, expected, atol=1e-9)
def test_rotor_power_alpha_one_returns_input() -> None:
R = make_rotor_from_angle(0.4, bivector_idx=7)
R_one = rotor_power(R, 1.0)
np.testing.assert_allclose(R_one, R, atol=1e-9)
def test_rotor_power_half_angle_halves_rotation() -> None:
"""R^0.5 applied twice equals R."""
from algebra.cl41 import geometric_product
R = make_rotor_from_angle(0.8, bivector_idx=7)
R_half = rotor_power(R, 0.5)
R_half_squared = geometric_product(R_half, R_half)
np.testing.assert_allclose(R_half_squared, R, atol=1e-6)
def test_rotor_power_handles_identity_input() -> None:
"""Identity rotor under any power stays identity."""
identity = np.zeros(32, dtype=np.float64)
identity[0] = 1.0
for alpha in [0.0, 0.3, 1.0, 1.5]:
result = rotor_power(identity, alpha)
np.testing.assert_allclose(result, identity, atol=1e-9)
def test_rotor_power_on_word_transition_preserves_closure() -> None:
"""The real-world case: rotors produced by word_transition_rotor."""
A = np.zeros(32, dtype=np.float64)
A[0] = 1.0
B = np.zeros(32, dtype=np.float64)
B[0] = np.cos(0.4)
B[7] = np.sin(0.4)
R = word_transition_rotor(A, B)
for alpha in [0.05, 0.2, 0.5, 0.8, 0.95]:
R_alpha = rotor_power(R, alpha)
cond = versor_condition(R_alpha)
assert cond < _TOL, f"alpha={alpha}: versor_condition = {cond:.3e}"
def test_rotor_power_null_translator_scales_translation() -> None:
"""B²=0 (CGA translator): T^α = 1 + αB, not identity (Cartan dual-slerp)."""
from algebra.null_point import recover_translation, translator
T = translator(np.array([2.0, 0.0, 0.0], dtype=np.float64))
half = rotor_power(T, 0.5)
assert versor_condition(half) < _TOL
a, _ = recover_translation(half)
np.testing.assert_allclose(a, [1.0, 0.0, 0.0], atol=1e-9)
# Full power recovers the original translator.
full = rotor_power(T, 1.0)
np.testing.assert_allclose(full, T, atol=1e-9)
def test_rotor_power_rejects_wrong_shape() -> None:
with pytest.raises(ValueError):
rotor_power(np.zeros(16, dtype=np.float64), 0.5)