mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
433 lines
14 KiB
Python
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()
|