Research-Stack/5-Applications/scripts/validate_quaternion_unit_norm_preservation.py

433 lines
14 KiB
Python

#!/usr/bin/env python3
"""
Unit Norm Preservation Validation for Resonance Quaternion Stochastic Differentials
This script validates that the quaternion operations preserve unit norm as required
by the swarm analysis of resonance quaternion stochastic differentials (MATH_MODEL_MAP 0.4.4).
Validation tests:
1. Quaternion multiplication preserves unit norm
2. Axis-angle construction preserves unit norm
3. SLERP interpolation preserves unit norm
4. Stochastic evolution preserves unit norm (with explicit renormalization)
5. SLUQ triage integration preserves unit norm
"""
import numpy as np
import json
from pathlib import Path
from datetime import datetime
from typing import List, Tuple
class Quaternion:
"""Unit quaternion with unit norm enforcement."""
def __init__(self, w: float, x: float, y: float, z: float, enforce_norm: bool = True):
self.w = w
self.x = x
self.y = y
self.z = z
if enforce_norm:
self.normalize()
def normalize(self):
"""Normalize quaternion to unit norm."""
norm = np.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
if norm > 1e-10:
self.w /= norm
self.x /= norm
self.y /= norm
self.z /= norm
def norm(self) -> float:
"""Compute quaternion norm."""
return np.sqrt(self.w**2 + self.x**2 + self.y**2 + self.z**2)
def multiply(self, other: 'Quaternion') -> 'Quaternion':
"""Hamilton product of quaternions."""
w = self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z
x = self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y
y = self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x
z = self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w
return Quaternion(w, x, y, z)
def from_axis_angle(self, axis: Tuple[float, float, float], angle: float) -> 'Quaternion':
"""Create unit quaternion from axis-angle representation."""
ax, ay, az = axis
axis_norm = np.sqrt(ax**2 + ay**2 + az**2)
if axis_norm > 1e-10:
ax /= axis_norm
ay /= axis_norm
az /= axis_norm
half_angle = angle / 2.0
cos_half = np.cos(half_angle)
sin_half = np.sin(half_angle)
w = cos_half
x = sin_half * ax
y = sin_half * ay
z = sin_half * az
return Quaternion(w, x, y, z)
def slerp(self, other: 'Quaternion', t: float) -> 'Quaternion':
"""Spherical linear interpolation between quaternions."""
dot = self.w * other.w + self.x * other.x + self.y * other.y + self.z * other.z
# Ensure shortest path
if dot < 0.0:
other = Quaternion(-other.w, -other.x, -other.y, -other.z, enforce_norm=False)
dot = -dot
if dot > 0.9995:
# Linear interpolation for nearly parallel quaternions
w1 = 1.0 - t
w2 = t
result = Quaternion(
w1 * self.w + w2 * other.w,
w1 * self.x + w2 * other.x,
w1 * self.y + w2 * other.y,
w1 * self.z + w2 * other.z,
enforce_norm=True
)
return result
omega = np.arccos(np.clip(dot, -1.0, 1.0))
sin_omega = np.sin(omega)
w1 = np.sin((1.0 - t) * omega) / sin_omega
w2 = np.sin(t * omega) / sin_omega
result = Quaternion(
w1 * self.w + w2 * other.w,
w1 * self.x + w2 * other.x,
w1 * self.y + w2 * other.y,
w1 * self.z + w2 * other.z,
enforce_norm=True
)
return result
def stochastic_evolution(self, gradient: Tuple[float, float, float],
noise: float, dt: float) -> 'Quaternion':
"""Stochastic evolution with resonance gradient guidance."""
dR_domega, dR_dt, _ = gradient
# Compute stochastic increment
ito_correction = 0.5 * (dR_domega + dR_dt) * dt
stochastic_increment = ito_correction + dR_domega * noise * np.sqrt(dt)
# Apply small rotation based on increment
axis = (1.0, 0.0, 0.0) # Simplified: x-axis rotation
angle = stochastic_increment * 0.1 # Scale factor
rotated = self.from_axis_angle(axis, angle)
result = rotated.multiply(self)
return result
def validate_multiplication_preserves_norm() -> dict:
"""Test that quaternion multiplication preserves unit norm."""
print("\n" + "=" * 70)
print("Test 1: Quaternion Multiplication Unit Norm Preservation")
print("=" * 70)
# Generate random unit quaternions
np.random.seed(42)
num_tests = 1000
norm_deviations = []
for _ in range(num_tests):
q1 = Quaternion(*np.random.randn(4))
q2 = Quaternion(*np.random.randn(4))
q3 = q1.multiply(q2)
norm_deviation = abs(q3.norm() - 1.0)
norm_deviations.append(norm_deviation)
max_deviation = max(norm_deviations)
mean_deviation = np.mean(norm_deviations)
std_deviation = np.std(norm_deviations)
passed = max_deviation < 1e-10
result = {
"test_name": "quaternion_multiplication_unit_norm",
"num_tests": num_tests,
"max_deviation": float(max_deviation),
"mean_deviation": float(mean_deviation),
"std_deviation": float(std_deviation),
"passed": passed,
"threshold": 1e-10
}
print(f" Max deviation: {max_deviation:.2e}")
print(f" Mean deviation: {mean_deviation:.2e}")
print(f" Std deviation: {std_deviation:.2e}")
print(f" Status: {'✅ PASSED' if passed else '❌ FAILED'}")
return result
def validate_axis_angle_preserves_norm() -> dict:
"""Test that axis-angle construction preserves unit norm."""
print("\n" + "=" * 70)
print("Test 2: Axis-Angle Construction Unit Norm Preservation")
print("=" * 70)
np.random.seed(43)
num_tests = 1000
norm_deviations = []
for _ in range(num_tests):
axis = np.random.randn(3)
axis = axis / np.linalg.norm(axis)
angle = np.random.uniform(0, 2 * np.pi)
q = Quaternion(0, 0, 0, 0, enforce_norm=False)
q = q.from_axis_angle(tuple(axis), angle)
norm_deviation = abs(q.norm() - 1.0)
norm_deviations.append(norm_deviation)
max_deviation = max(norm_deviations)
mean_deviation = np.mean(norm_deviations)
std_deviation = np.std(norm_deviations)
passed = max_deviation < 1e-10
result = {
"test_name": "axis_angle_construction_unit_norm",
"num_tests": num_tests,
"max_deviation": float(max_deviation),
"mean_deviation": float(mean_deviation),
"std_deviation": float(std_deviation),
"passed": passed,
"threshold": 1e-10
}
print(f" Max deviation: {max_deviation:.2e}")
print(f" Mean deviation: {mean_deviation:.2e}")
print(f" Std deviation: {std_deviation:.2e}")
print(f" Status: {'✅ PASSED' if passed else '❌ FAILED'}")
return result
def validate_slerp_preserves_norm() -> dict:
"""Test that SLERP interpolation preserves unit norm."""
print("\n" + "=" * 70)
print("Test 3: SLERP Interpolation Unit Norm Preservation")
print("=" * 70)
np.random.seed(44)
num_tests = 1000
norm_deviations = []
for _ in range(num_tests):
q1 = Quaternion(*np.random.randn(4))
q2 = Quaternion(*np.random.randn(4))
t = np.random.uniform(0, 1)
q3 = q1.slerp(q2, t)
norm_deviation = abs(q3.norm() - 1.0)
norm_deviations.append(norm_deviation)
max_deviation = max(norm_deviations)
mean_deviation = np.mean(norm_deviations)
std_deviation = np.std(norm_deviations)
passed = max_deviation < 1e-10
result = {
"test_name": "slerp_interpolation_unit_norm",
"num_tests": num_tests,
"max_deviation": float(max_deviation),
"mean_deviation": float(mean_deviation),
"std_deviation": float(std_deviation),
"passed": passed,
"threshold": 1e-10
}
print(f" Max deviation: {max_deviation:.2e}")
print(f" Mean deviation: {mean_deviation:.2e}")
print(f" Std deviation: {std_deviation:.2e}")
print(f" Status: {'✅ PASSED' if passed else '❌ FAILED'}")
return result
def validate_stochastic_evolution_preserves_norm() -> dict:
"""Test that stochastic evolution preserves unit norm with explicit renormalization."""
print("\n" + "=" * 70)
print("Test 4: Stochastic Evolution Unit Norm Preservation")
print("=" * 70)
np.random.seed(45)
num_tests = 1000
num_steps = 100
norm_deviations = []
for _ in range(num_tests):
q = Quaternion(*np.random.randn(4))
for _ in range(num_steps):
gradient = (np.random.randn(3))
noise = np.random.randn()
dt = 0.01
q = q.stochastic_evolution(gradient, noise, dt)
norm_deviation = abs(q.norm() - 1.0)
norm_deviations.append(norm_deviation)
max_deviation = max(norm_deviations)
mean_deviation = np.mean(norm_deviations)
std_deviation = np.std(norm_deviations)
passed = max_deviation < 1e-10
result = {
"test_name": "stochastic_evolution_unit_norm",
"num_tests": num_tests,
"num_steps_per_test": num_steps,
"max_deviation": float(max_deviation),
"mean_deviation": float(mean_deviation),
"std_deviation": float(std_deviation),
"passed": passed,
"threshold": 1e-10
}
print(f" Max deviation: {max_deviation:.2e}")
print(f" Mean deviation: {mean_deviation:.2e}")
print(f" Std deviation: {std_deviation:.2e}")
print(f" Status: {'✅ PASSED' if passed else '❌ FAILED'}")
return result
def validate_sluq_integration_preserves_norm() -> dict:
"""Test that SLUQ triage integration preserves unit norm."""
print("\n" + "=" * 70)
print("Test 5: SLUQ Triage Integration Unit Norm Preservation")
print("=" * 70)
np.random.seed(46)
num_tests = 1000
num_steps = 100
norm_deviations = []
for _ in range(num_tests):
q = Quaternion(*np.random.randn(4))
for _ in range(num_steps):
# Simulate SLUQ stability check
gradient = (np.random.randn(3))
grad_magnitude = np.linalg.norm(gradient)
stability_threshold = 2.0 # Lenient threshold
if grad_magnitude < stability_threshold:
# Stable: apply stochastic evolution
noise = np.random.randn()
dt = 0.01
q = q.stochastic_evolution(gradient, noise, dt)
# Else: unstable, skip update (prune trajectory)
norm_deviation = abs(q.norm() - 1.0)
norm_deviations.append(norm_deviation)
max_deviation = max(norm_deviations)
mean_deviation = np.mean(norm_deviations)
std_deviation = np.std(norm_deviations)
passed = max_deviation < 1e-10
result = {
"test_name": "sluq_triage_integration_unit_norm",
"num_tests": num_tests,
"num_steps_per_test": num_steps,
"max_deviation": float(max_deviation),
"mean_deviation": float(mean_deviation),
"std_deviation": float(std_deviation),
"passed": passed,
"threshold": 1e-10
}
print(f" Max deviation: {max_deviation:.2e}")
print(f" Mean deviation: {mean_deviation:.2e}")
print(f" Std deviation: {std_deviation:.2e}")
print(f" Status: {'✅ PASSED' if passed else '❌ FAILED'}")
return result
def main():
"""Run all unit norm preservation validation tests."""
print("=" * 70)
print("Unit Norm Preservation Validation for Resonance Quaternion Stochastic Differentials")
print("=" * 70)
print(f"Timestamp: {datetime.now().isoformat()}")
# Run all tests
results = []
results.append(validate_multiplication_preserves_norm())
results.append(validate_axis_angle_preserves_norm())
results.append(validate_slerp_preserves_norm())
results.append(validate_stochastic_evolution_preserves_norm())
results.append(validate_sluq_integration_preserves_norm())
# Summary
print("\n" + "=" * 70)
print("Validation Summary")
print("=" * 70)
total_tests = len(results)
passed_tests = sum(1 for r in results if r["passed"])
for result in results:
status = "✅ PASSED" if result["passed"] else "❌ FAILED"
print(f" {result['test_name']}: {status}")
print(f"\nTotal: {passed_tests}/{total_tests} tests passed")
if passed_tests == total_tests:
print("✅ All unit norm preservation tests PASSED")
overall_status = "PASSED"
else:
print("❌ Some unit norm preservation tests FAILED")
overall_status = "FAILED"
# Save results
output = {
"timestamp": datetime.now().isoformat(),
"overall_status": overall_status,
"total_tests": total_tests,
"passed_tests": passed_tests,
"test_results": [
{
"test_name": r["test_name"],
"num_tests": r["num_tests"],
"max_deviation": r["max_deviation"],
"mean_deviation": r["mean_deviation"],
"std_deviation": r["std_deviation"],
"passed": bool(r["passed"]),
"threshold": r["threshold"]
}
for r in results
]
}
output_path = Path("shared-data/data/validation/quaternion_unit_norm_preservation.json")
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w') as f:
json.dump(output, f, indent=2)
print(f"\nResults saved to: {output_path}")
return overall_status
if __name__ == "__main__":
main()