Spaces:
Paused
Paused
File size: 23,054 Bytes
fb867c3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | #!/usr/bin/env python3
"""
Mathematical validation script for the Felix Framework.
This script validates mathematical properties derived in the formal documentation
against the implementation, ensuring theoretical consistency and correctness.
Validates:
1. Parametric helix equations and properties
2. Geometric invariants (curvature, torsion)
3. Agent distribution functions
4. Communication complexity bounds
5. Attention focusing mechanism properties
Mathematical references:
- docs/mathematical_model.md: Formal mathematical specifications
- docs/hypothesis_mathematics.md: Statistical formulations and proofs
"""
import math
import numpy as np
from typing import List, Tuple
import time
from src.core.helix_geometry import HelixGeometry
from src.agents.agent import generate_spawn_times, create_openscad_agents
from src.communication import CentralPost
class MathematicalValidator:
"""Validates mathematical properties against implementation."""
def __init__(self, tolerance: float = 1e-10):
"""Initialize validator with numerical tolerance."""
self.tolerance = tolerance
self.helix = HelixGeometry(33.0, 0.001, 33.0, 33) # OpenSCAD parameters
self.validation_results = {}
def validate_parametric_equations(self) -> bool:
"""
Validate parametric helix equations from mathematical_model.md Section 1.2.
Tests:
1. Boundary conditions: r(0) and r(1)
2. Radius tapering function R(t)
3. Angular progression ΞΈ(t)
4. Height function z(t) = Ht
"""
print("=== Validating Parametric Equations ===")
# Test boundary conditions
pos_0 = self.helix.get_position(0.0)
pos_1 = self.helix.get_position(1.0)
# At t=0: should be at (R_bottom, 0, 0)
expected_0 = (self.helix.bottom_radius, 0.0, 0.0)
diff_0 = max(abs(a - b) for a, b in zip(pos_0, expected_0))
# At t=1: should be at (R_top, 0, height) after n full turns
# After 33 turns, angle = 33 * 2Ο, so cos(33*2Ο) = cos(0) = 1
expected_1 = (self.helix.top_radius, 0.0, self.helix.height)
diff_1 = max(abs(a - b) for a, b in zip(pos_1, expected_1))
boundary_valid = diff_0 < self.tolerance and diff_1 < self.tolerance
print(f"Boundary conditions: {'β
' if boundary_valid else 'β'}")
print(f" t=0: {pos_0} vs {expected_0}, diff={diff_0:.2e}")
print(f" t=1: {pos_1} vs {expected_1}, diff={diff_1:.2e}")
# Test radius function R(t) = R_bottom * (R_top/R_bottom)^t
radius_valid = True
for t in [0.0, 0.25, 0.5, 0.75, 1.0]:
z = self.helix.height * t
actual_radius = self.helix.get_radius(z)
expected_radius = self.helix.bottom_radius * pow(
self.helix.top_radius / self.helix.bottom_radius, t
)
diff = abs(actual_radius - expected_radius)
if diff > self.tolerance:
radius_valid = False
print(f" R({t:.2f}): {actual_radius:.6f} vs {expected_radius:.6f}, diff={diff:.2e}")
print(f"Radius function: {'β
' if radius_valid else 'β'}")
# Test monotonicity of R(t) - should be strictly increasing
monotonic_valid = True
prev_radius = 0.0
for t in np.linspace(0, 1, 100):
z = self.helix.height * t
radius = self.helix.get_radius(z)
if radius <= prev_radius and t > 0:
monotonic_valid = False
break
prev_radius = radius
print(f"Radius monotonicity: {'β
' if monotonic_valid else 'β'}")
self.validation_results['parametric_equations'] = {
'boundary_conditions': boundary_valid,
'radius_function': radius_valid,
'monotonicity': monotonic_valid,
'overall': boundary_valid and radius_valid and monotonic_valid
}
return boundary_valid and radius_valid and monotonic_valid
def validate_geometric_properties(self) -> bool:
"""
Validate geometric properties from mathematical_model.md Section 3.
Tests:
1. Arc length approximation convergence
2. Tangent vector properties
3. Smoothness (differentiability)
"""
print("\n=== Validating Geometric Properties ===")
# Test arc length approximation convergence
# Should converge as number of segments increases
segments_list = [100, 500, 1000, 2000]
arc_lengths = []
for segments in segments_list:
length = self.helix.approximate_arc_length(segments=segments)
arc_lengths.append(length)
# Check convergence: difference between consecutive approximations should decrease
convergence_valid = True
for i in range(1, len(arc_lengths)):
diff_curr = abs(arc_lengths[i] - arc_lengths[i-1])
if i > 1:
diff_prev = abs(arc_lengths[i-1] - arc_lengths[i-2])
if diff_curr >= diff_prev: # Should be decreasing
convergence_valid = False
print(f" Arc length ({segments_list[i]} segments): {arc_lengths[i]:.3f}, "
f"diff from previous: {diff_curr:.3f}")
print(f"Arc length convergence: {'β
' if convergence_valid else 'β'}")
# Test tangent vector properties
tangent_valid = True
for t in [0.1, 0.3, 0.5, 0.7, 0.9]:
tangent = self.helix.get_tangent_vector(t)
# Tangent vector should be normalized (length β 1)
length = math.sqrt(sum(component**2 for component in tangent))
if abs(length - 1.0) > self.tolerance:
tangent_valid = False
print(f" Tangent at t={t}: length={length:.6f}")
print(f"Tangent vector normalization: {'β
' if tangent_valid else 'β'}")
# Test smoothness by checking continuity of positions
smoothness_valid = True
epsilon = 1e-6
for t in np.linspace(0.1, 0.9, 20):
pos1 = self.helix.get_position(t - epsilon)
pos2 = self.helix.get_position(t + epsilon)
# Distance should be small for small epsilon
distance = math.sqrt(sum((a - b)**2 for a, b in zip(pos1, pos2)))
expected_distance = 2 * epsilon * self.helix.approximate_arc_length(segments=1000)
if distance > 10 * expected_distance: # Allow some tolerance
smoothness_valid = False
print(f"Smoothness (continuity): {'β
' if smoothness_valid else 'β'}")
self.validation_results['geometric_properties'] = {
'arc_length_convergence': convergence_valid,
'tangent_normalization': tangent_valid,
'smoothness': smoothness_valid,
'overall': convergence_valid and tangent_valid and smoothness_valid
}
return convergence_valid and tangent_valid and smoothness_valid
def validate_agent_distribution(self) -> bool:
"""
Validate agent distribution functions from mathematical_model.md Section 4.
Tests:
1. Uniform spawn time distribution U(0,1)
2. Agent density evolution properties
3. Statistical properties of large samples
"""
print("\n=== Validating Agent Distribution ===")
# Test spawn time distribution
N = 10000
spawn_times = generate_spawn_times(N, seed=12345)
# Test uniformity using Kolmogorov-Smirnov test approximation
sorted_times = sorted(spawn_times)
max_deviation = 0.0
for i, t in enumerate(sorted_times):
empirical_cdf = (i + 1) / N
theoretical_cdf = t # For U(0,1), CDF(t) = t
deviation = abs(empirical_cdf - theoretical_cdf)
max_deviation = max(max_deviation, deviation)
# For U(0,1), critical value at Ξ±=0.05 is approximately 1.36/βN
critical_value = 1.36 / math.sqrt(N)
uniformity_valid = max_deviation < critical_value
print(f"Spawn time uniformity: {'β
' if uniformity_valid else 'β'}")
print(f" Max KS deviation: {max_deviation:.6f}, critical: {critical_value:.6f}")
# Test mean and variance
mean_time = np.mean(spawn_times)
var_time = np.var(spawn_times)
# For U(0,1): mean = 0.5, variance = 1/12 β 0.0833
mean_valid = abs(mean_time - 0.5) < 3 * math.sqrt(1/12/N) # 3-sigma test
var_valid = abs(var_time - 1/12) < 0.01 # Allow reasonable tolerance
print(f"Mean (expected 0.5): {mean_time:.6f}, valid: {'β
' if mean_valid else 'β'}")
print(f"Variance (expected 0.0833): {var_time:.6f}, valid: {'β
' if var_valid else 'β'}")
# Test reproducibility with same seed
spawn_times_2 = generate_spawn_times(N, seed=12345)
reproducibility_valid = spawn_times == spawn_times_2
print(f"Reproducibility with same seed: {'β
' if reproducibility_valid else 'β'}")
self.validation_results['agent_distribution'] = {
'uniformity': uniformity_valid,
'mean': mean_valid,
'variance': var_valid,
'reproducibility': reproducibility_valid,
'overall': uniformity_valid and mean_valid and var_valid and reproducibility_valid
}
return uniformity_valid and mean_valid and var_valid and reproducibility_valid
def validate_attention_focusing(self) -> bool:
"""
Validate attention focusing mechanism from hypothesis_mathematics.md Section H3.
Tests:
1. Attention density A(t) = k / (2ΟR(t))
2. Monotonic increase: dA/dt > 0
3. Exponential growth toward narrow end
"""
print("\n=== Validating Attention Focusing Mechanism ===")
# Calculate attention density at different positions
attention_densities = []
t_values = np.linspace(0.1, 0.9, 20)
for t in t_values:
z = self.helix.height * t
radius = self.helix.get_radius(z)
# Attention density A(t) = 1 / (2Ο * R(t))
attention = 1.0 / (2 * math.pi * radius)
attention_densities.append(attention)
# Test monotonic decrease (attention decreases as radius increases toward top)
monotonic_decrease = True
for i in range(1, len(attention_densities)):
if attention_densities[i] >= attention_densities[i-1]:
monotonic_decrease = False
print(f" Non-monotonic at i={i}: A[{i-1}]={attention_densities[i-1]:.6f}, "
f"A[{i}]={attention_densities[i]:.6f}")
break
print(f"Attention density monotonic decrease (correct): {'β
' if monotonic_decrease else 'β'}")
# Test exponential decay rate (attention decreases exponentially)
# dA/dt should be negative and proportional to -A(t) * ln(R_top/R_bottom)
ln_ratio = math.log(self.helix.top_radius / self.helix.bottom_radius)
exponential_decay_valid = True
for i in range(1, len(attention_densities) - 1):
# Numerical derivative
dt = t_values[i+1] - t_values[i-1]
dA_dt = (attention_densities[i+1] - attention_densities[i-1]) / dt
# Expected: dA/dt = -A(t) * ln_ratio (negative because attention decreases)
expected_derivative = -attention_densities[i] * ln_ratio
relative_error = abs(dA_dt - expected_derivative) / abs(expected_derivative) if expected_derivative != 0 else 1
if relative_error > 0.15: # Allow 15% numerical error for derivative approximation
exponential_decay_valid = False
if i < 5: # Print first few for debugging
print(f" t={t_values[i]:.2f}: dA/dt={dA_dt:.3f}, "
f"expected={expected_derivative:.3f}, error={relative_error:.2%}")
print(f"Exponential decay rate: {'β
' if exponential_decay_valid else 'β'}")
# Test focusing factor: ratio of attention at bottom vs top (bottom has higher attention)
focusing_ratio = attention_densities[0] / attention_densities[-1] if attention_densities[-1] > 0 else 0
expected_ratio = (self.helix.top_radius / self.helix.bottom_radius) # R_top/R_bottom ratio
focusing_valid = focusing_ratio > 100 # Should show significant focusing at bottom
print(f"Attention at bottom (t=0.1): {attention_densities[0]:.6f}")
print(f"Attention at top (t=0.9): {attention_densities[-1]:.6f}")
print(f"Focusing ratio (A_bottom/A_top): {focusing_ratio:.1f}")
print(f"Significant focusing (>100x): {'β
' if focusing_valid else 'β'}")
self.validation_results['attention_focusing'] = {
'monotonic_decrease': monotonic_decrease,
'exponential_decay': exponential_decay_valid,
'significant_focusing': focusing_valid,
'overall': monotonic_decrease and exponential_decay_valid and focusing_valid
}
return monotonic_decrease and exponential_decay_valid and focusing_valid
def validate_communication_complexity(self) -> bool:
"""
Validate communication complexity from hypothesis_mathematics.md Section H2.
Tests:
1. O(N) scaling for spoke-based system
2. Message count bounds
3. Maximum communication distance
"""
print("\n=== Validating Communication Complexity ===")
# Test scaling with different agent counts
agent_counts = [10, 20, 50, 100]
message_counts = []
processing_times = []
for N in agent_counts:
central_post = CentralPost(max_agents=N, enable_metrics=True)
agents = create_openscad_agents(self.helix, number_of_nodes=N, random_seed=42069)
# Register agents and time the operation
start_time = time.perf_counter()
for agent in agents:
central_post.register_agent(agent)
registration_time = time.perf_counter() - start_time
processing_times.append(registration_time)
message_counts.append(N) # Each agent creates one connection
# Test O(N) scaling - processing time should be roughly linear
linear_scaling_valid = True
for i in range(1, len(agent_counts)):
ratio_agents = agent_counts[i] / agent_counts[i-1]
ratio_time = processing_times[i] / processing_times[i-1]
# Allow factor of 3 deviation from linear scaling
if ratio_time > 3 * ratio_agents or ratio_time < ratio_agents / 3:
linear_scaling_valid = False
print(f" N={agent_counts[i]}: time={processing_times[i]:.6f}s, "
f"scaling ratio={ratio_time:.2f} (expected β{ratio_agents:.2f})")
print(f"Linear scaling O(N): {'β
' if linear_scaling_valid else 'β'}")
# Test maximum communication distance
max_spoke_distance = self.helix.top_radius
actual_distances = []
for t in np.linspace(0, 1, 100):
pos = self.helix.get_position(t)
# Distance from agent to central axis at same height
distance = math.sqrt(pos[0]**2 + pos[1]**2) # Should equal R(t)
actual_distances.append(distance)
max_actual_distance = max(actual_distances)
distance_bound_valid = abs(max_actual_distance - max_spoke_distance) < self.tolerance
print(f"Maximum spoke distance: {max_actual_distance:.6f} "
f"(expected {max_spoke_distance:.6f})")
print(f"Distance bound valid: {'β
' if distance_bound_valid else 'β'}")
# Test message complexity O(N) vs theoretical O(NΒ²) mesh
complexity_advantage = True
for N in agent_counts:
spoke_messages = N # Each agent to central post
mesh_messages = N * (N - 1) // 2 # All pairs
efficiency_ratio = mesh_messages / spoke_messages
expected_ratio = (N - 1) / 2
if abs(efficiency_ratio - expected_ratio) > 0.1:
complexity_advantage = False
print(f" N={N}: spoke={spoke_messages}, mesh={mesh_messages}, "
f"advantage={efficiency_ratio:.1f}x")
print(f"Message complexity advantage: {'β
' if complexity_advantage else 'β'}")
self.validation_results['communication_complexity'] = {
'linear_scaling': linear_scaling_valid,
'distance_bounds': distance_bound_valid,
'complexity_advantage': complexity_advantage,
'overall': linear_scaling_valid and distance_bound_valid and complexity_advantage
}
return linear_scaling_valid and distance_bound_valid and complexity_advantage
def validate_numerical_precision(self) -> bool:
"""
Validate numerical precision and stability.
Tests:
1. Consistency with OpenSCAD validation
2. Numerical stability across parameter ranges
3. Error accumulation in iterative calculations
"""
print("\n=== Validating Numerical Precision ===")
# Test against OpenSCAD validation (should be < 1e-12)
from validate_openscad import validate_implementation
openscad_valid = validate_implementation()
print(f"OpenSCAD validation: {'β
' if openscad_valid else 'β'}")
# Test numerical stability at extreme parameter values
stability_valid = True
# Test very small t values
small_t_values = [1e-10, 1e-8, 1e-6, 1e-4]
for t in small_t_values:
try:
pos = self.helix.get_position(t)
# Should be close to (R_bottom, 0, 0)
if not all(math.isfinite(x) for x in pos):
stability_valid = False
except Exception:
stability_valid = False
# Test values very close to 1
large_t_values = [1 - 1e-10, 1 - 1e-8, 1 - 1e-6, 1 - 1e-4]
for t in large_t_values:
try:
pos = self.helix.get_position(t)
if not all(math.isfinite(x) for x in pos):
stability_valid = False
except Exception:
stability_valid = False
print(f"Numerical stability: {'β
' if stability_valid else 'β'}")
# Test error accumulation in arc length calculation
# Compare high-precision vs standard precision
arc_length_high = self.helix.approximate_arc_length(segments=10000)
arc_length_std = self.helix.approximate_arc_length(segments=1000)
relative_error = abs(arc_length_high - arc_length_std) / arc_length_high
error_acceptable = relative_error < 0.01 # 1% tolerance for numerical approximation
print(f"Arc length error: {relative_error:.4%} (high vs standard precision)")
print(f"Error accumulation acceptable: {'β
' if error_acceptable else 'β'}")
self.validation_results['numerical_precision'] = {
'openscad_validation': openscad_valid,
'numerical_stability': stability_valid,
'error_accumulation': error_acceptable,
'overall': openscad_valid and stability_valid and error_acceptable
}
return openscad_valid and stability_valid and error_acceptable
def run_full_validation(self) -> bool:
"""Run complete mathematical validation suite."""
print("=" * 60)
print("FELIX FRAMEWORK MATHEMATICAL VALIDATION")
print("=" * 60)
validators = [
('Parametric Equations', self.validate_parametric_equations),
('Geometric Properties', self.validate_geometric_properties),
('Agent Distribution', self.validate_agent_distribution),
('Attention Focusing', self.validate_attention_focusing),
('Communication Complexity', self.validate_communication_complexity),
('Numerical Precision', self.validate_numerical_precision),
]
all_valid = True
results_summary = []
for name, validator in validators:
try:
result = validator()
results_summary.append((name, result))
if not result:
all_valid = False
except Exception as e:
print(f"ERROR in {name}: {e}")
results_summary.append((name, False))
all_valid = False
# Print summary
print("\n" + "=" * 60)
print("VALIDATION SUMMARY")
print("=" * 60)
for name, result in results_summary:
status = "β
PASSED" if result else "β FAILED"
print(f"{name:<25}: {status}")
overall_status = "β
ALL VALIDATIONS PASSED" if all_valid else "β SOME VALIDATIONS FAILED"
print(f"\nOverall Result: {overall_status}")
if all_valid:
print("\nπ Mathematical implementation is validated!")
print(" Ready for hypothesis testing and research publication.")
else:
print("\nβ οΈ Mathematical validation failures detected.")
print(" Review implementation before proceeding with experiments.")
return all_valid
if __name__ == "__main__":
validator = MathematicalValidator(tolerance=1e-10)
success = validator.run_full_validation()
# Save validation results for research documentation
import json
# Convert numpy booleans to regular booleans for JSON serialization
def convert_numpy_types(obj):
if hasattr(obj, 'item'):
return obj.item()
elif isinstance(obj, dict):
return {k: convert_numpy_types(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_numpy_types(v) for v in obj]
return obj
serializable_results = convert_numpy_types(validator.validation_results)
with open("mathematical_validation_results.json", "w") as f:
json.dump(serializable_results, f, indent=2)
print(f"\nValidation results saved to: mathematical_validation_results.json")
exit(0 if success else 1) |