mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
340 lines
12 KiB
Python
340 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
gwl_deterministic_em_demo.py
|
||
|
||
Demonstration of deterministic EM-like field propagation in GWL/TSM.
|
||
|
||
This validates the deterministic backbone before stochastic extensions.
|
||
"""
|
||
|
||
import numpy as np
|
||
import matplotlib.pyplot as plt
|
||
from dataclasses import dataclass
|
||
from typing import List, Tuple
|
||
|
||
|
||
@dataclass
|
||
class EMSeed:
|
||
"""μ-seed with electromagnetic field components."""
|
||
x: float # Position
|
||
E: float # Electric-like field
|
||
B: float # Magnetic-like field
|
||
tau: int # Temporal phase (0-15)
|
||
pi: int # Rotation/polarization (0-15)
|
||
chi: int # Chirality (0=D, 1=L)
|
||
omega: float # Frequency mode
|
||
|
||
|
||
class DeterministicEMField:
|
||
"""
|
||
Deterministic EM-like field on TSM topology.
|
||
|
||
Update law (no randomness):
|
||
E_i(t+1) = E_i(t) + α * Σ_j w_ij * (B_j - B_i)
|
||
B_i(t+1) = B_i(t) - β * Σ_j w_ij * (E_j - E_i)
|
||
tau_i(t+1) = (tau_i + omega) % 16
|
||
"""
|
||
|
||
def __init__(self, n_seeds: int = 100, alpha: float = 0.1, beta: float = 0.1):
|
||
self.n = n_seeds
|
||
self.alpha = alpha
|
||
self.beta = beta
|
||
self.seeds: List[EMSeed] = []
|
||
self.history_E: List[List[float]] = []
|
||
self.history_B: List[List[float]] = []
|
||
self.history_energy: List[float] = []
|
||
|
||
# Initialize linear topology (1D tape)
|
||
for i in range(n_seeds):
|
||
self.seeds.append(EMSeed(
|
||
x=float(i),
|
||
E=0.0,
|
||
B=0.0,
|
||
tau=0,
|
||
pi=0,
|
||
chi=0,
|
||
omega=0.0
|
||
))
|
||
|
||
def initialize_pulse(self, center: int, width: int, amplitude: float = 1.0,
|
||
freq: float = 1.0):
|
||
"""Initialize a Gaussian pulse with given frequency."""
|
||
for i in range(self.n):
|
||
dist = abs(i - center)
|
||
if dist < width:
|
||
# Gaussian envelope
|
||
envelope = amplitude * np.exp(-(dist**2) / (2 * (width/3)**2))
|
||
self.seeds[i].E = envelope * np.cos(2 * np.pi * freq * i / self.n)
|
||
self.seeds[i].B = envelope * np.sin(2 * np.pi * freq * i / self.n)
|
||
self.seeds[i].omega = freq
|
||
|
||
def coupling_weight(self, i: int, j: int) -> float:
|
||
"""
|
||
Deterministic coupling weight.
|
||
|
||
For 1D linear topology: only nearest neighbors couple.
|
||
"""
|
||
if abs(i - j) == 1: # Nearest neighbor
|
||
return 1.0
|
||
elif abs(i - j) == 2: # Next-nearest (weaker)
|
||
return 0.3
|
||
return 0.0
|
||
|
||
def step(self):
|
||
"""One deterministic evolution step."""
|
||
new_E = np.zeros(self.n)
|
||
new_B = np.zeros(self.n)
|
||
new_tau = np.zeros(self.n, dtype=int)
|
||
|
||
for i in range(self.n):
|
||
# Coupling sum
|
||
coupling_E = 0.0
|
||
coupling_B = 0.0
|
||
|
||
for j in range(max(0, i-2), min(self.n, i+3)):
|
||
if i == j:
|
||
continue
|
||
w = self.coupling_weight(i, j)
|
||
coupling_E += w * (self.seeds[j].B - self.seeds[i].B)
|
||
coupling_B += w * (self.seeds[j].E - self.seeds[i].E)
|
||
|
||
# Deterministic update (NO RANDOMNESS)
|
||
new_E[i] = self.seeds[i].E + self.alpha * coupling_E
|
||
new_B[i] = self.seeds[i].B - self.beta * coupling_B
|
||
|
||
# Temporal phase evolution
|
||
new_tau[i] = (self.seeds[i].tau + int(self.seeds[i].omega * 16)) % 16
|
||
|
||
# Apply updates
|
||
for i in range(self.n):
|
||
self.seeds[i].E = new_E[i]
|
||
self.seeds[i].B = new_B[i]
|
||
self.seeds[i].tau = new_tau[i]
|
||
|
||
# Record history
|
||
self.history_E.append([s.E for s in self.seeds])
|
||
self.history_B.append([s.B for s in self.seeds])
|
||
|
||
# Energy
|
||
energy = sum(s.E**2 + s.B**2 for s in self.seeds)
|
||
self.history_energy.append(energy)
|
||
|
||
def run(self, steps: int = 200):
|
||
"""Run deterministic evolution."""
|
||
for _ in range(steps):
|
||
self.step()
|
||
|
||
def test_1_stable_propagation(self) -> Tuple[bool, str]:
|
||
"""
|
||
Test 1: Stable wave propagation.
|
||
|
||
Energy should remain bounded, pulse should propagate.
|
||
"""
|
||
self.initialize_pulse(center=25, width=10, amplitude=1.0, freq=2.0)
|
||
initial_energy = sum(s.E**2 + s.B**2 for s in self.seeds)
|
||
|
||
self.run(steps=100)
|
||
|
||
final_energy = sum(s.E**2 + s.B**2 for s in self.seeds)
|
||
energy_ratio = final_energy / initial_energy if initial_energy > 0 else 0
|
||
|
||
# Check if pulse moved from initial position
|
||
max_E_initial = max(abs(self.history_E[0][i]) for i in range(20, 30))
|
||
max_E_final = max(abs(self.history_E[-1][i]) for i in range(40, 80))
|
||
|
||
passed = (0.5 < energy_ratio < 2.0) and (max_E_final > 0.1 * max_E_initial)
|
||
|
||
msg = f"Energy ratio: {energy_ratio:.3f}, Pulse propagated: {max_E_final > 0.1 * max_E_initial}"
|
||
return passed, msg
|
||
|
||
def test_2_frequency_separability(self) -> Tuple[bool, str]:
|
||
"""
|
||
Test 2: Frequency separability.
|
||
|
||
Two different frequencies should remain distinguishable.
|
||
"""
|
||
# Initialize two pulses at different frequencies
|
||
self.__init__(self.n, self.alpha, self.beta) # Reset
|
||
|
||
# Low frequency pulse
|
||
for i in range(20, 40):
|
||
dist = abs(i - 30)
|
||
self.seeds[i].E = np.exp(-dist**2/20) * np.cos(2 * np.pi * 1.0 * i / 20)
|
||
self.seeds[i].omega = 1.0
|
||
|
||
# High frequency pulse
|
||
for i in range(60, 80):
|
||
dist = abs(i - 70)
|
||
self.seeds[i].E = np.exp(-dist**2/20) * np.cos(2 * np.pi * 4.0 * i / 10)
|
||
self.seeds[i].omega = 4.0
|
||
|
||
self.run(steps=50)
|
||
|
||
# FFT analysis
|
||
signal_low = [self.history_E[t][30] for t in range(len(self.history_E))]
|
||
signal_high = [self.history_E[t][70] for t in range(len(self.history_E))]
|
||
|
||
if len(signal_low) > 10:
|
||
fft_low = np.abs(np.fft.fft(signal_low))
|
||
fft_high = np.abs(np.fft.fft(signal_high))
|
||
|
||
peak_low = np.argmax(fft_low[:len(fft_low)//2])
|
||
peak_high = np.argmax(fft_high[:len(fft_high)//2])
|
||
|
||
separable = abs(peak_high - peak_low) > 2
|
||
msg = f"Low freq peak: {peak_low}, High freq peak: {peak_high}"
|
||
return separable, msg
|
||
|
||
return False, "Insufficient data"
|
||
|
||
def test_3_determinism(self) -> Tuple[bool, str]:
|
||
"""
|
||
Test 3: Determinism.
|
||
|
||
Same initial conditions → same evolution.
|
||
"""
|
||
# Run 1
|
||
self.__init__(self.n, self.alpha, self.beta)
|
||
self.initialize_pulse(center=30, width=8, amplitude=1.0, freq=2.0)
|
||
self.run(steps=50)
|
||
final_E_1 = [s.E for s in self.seeds]
|
||
|
||
# Run 2 (identical)
|
||
self.__init__(self.n, self.alpha, self.beta)
|
||
self.initialize_pulse(center=30, width=8, amplitude=1.0, freq=2.0)
|
||
self.run(steps=50)
|
||
final_E_2 = [s.E for s in self.seeds]
|
||
|
||
# Compare
|
||
diff = max(abs(a - b) for a, b in zip(final_E_1, final_E_2))
|
||
passed = diff < 1e-10
|
||
|
||
return passed, f"Max difference between runs: {diff:.2e}"
|
||
|
||
def test_4_energy_conservation(self) -> Tuple[bool, str]:
|
||
"""
|
||
Test 4: Energy conservation (bounded).
|
||
|
||
Total field energy should not explode or vanish.
|
||
"""
|
||
self.__init__(self.n, self.alpha, self.beta)
|
||
self.initialize_pulse(center=50, width=15, amplitude=1.0, freq=1.5)
|
||
|
||
initial_energy = self.history_energy[0] if self.history_energy else 1.0
|
||
|
||
self.run(steps=100)
|
||
|
||
energy_values = self.history_energy
|
||
max_energy = max(energy_values)
|
||
min_energy = min(energy_values)
|
||
|
||
# Energy should stay within reasonable bounds
|
||
ratio = max_energy / min_energy if min_energy > 0 else float('inf')
|
||
passed = ratio < 10.0 # Less than 10x variation
|
||
|
||
return passed, f"Energy variation ratio: {ratio:.3f}"
|
||
|
||
def plot_evolution(self, title: str = "EM Field Evolution"):
|
||
"""Plot field evolution over time."""
|
||
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
|
||
|
||
# Plot E field heatmap
|
||
E_array = np.array(self.history_E)
|
||
im1 = axes[0].imshow(E_array.T, aspect='auto', cmap='RdBu',
|
||
extent=[0, len(self.history_E), 0, self.n])
|
||
axes[0].set_ylabel('Position')
|
||
axes[0].set_title(f'{title} - E Field')
|
||
plt.colorbar(im1, ax=axes[0])
|
||
|
||
# Plot B field heatmap
|
||
B_array = np.array(self.history_B)
|
||
im2 = axes[1].imshow(B_array.T, aspect='auto', cmap='RdBu',
|
||
extent=[0, len(self.history_B), 0, self.n])
|
||
axes[1].set_ylabel('Position')
|
||
axes[1].set_title('B Field')
|
||
plt.colorbar(im2, ax=axes[1])
|
||
|
||
# Plot energy
|
||
axes[2].plot(self.history_energy)
|
||
axes[2].set_xlabel('Time Step')
|
||
axes[2].set_ylabel('Total Energy')
|
||
axes[2].set_title('Energy Conservation')
|
||
axes[2].grid(True)
|
||
|
||
plt.tight_layout()
|
||
return fig
|
||
|
||
|
||
def run_all_tests():
|
||
"""Run all four deterministic EM tests."""
|
||
|
||
print("=" * 70)
|
||
print("GWL DETERMINISTIC EM FIELD TESTS")
|
||
print("=" * 70)
|
||
|
||
field = DeterministicEMField(n_seeds=100, alpha=0.15, beta=0.15)
|
||
|
||
# Test 1: Stable propagation
|
||
print("\n[Test 1] Stable Wave Propagation")
|
||
print("-" * 50)
|
||
passed, msg = field.test_1_stable_propagation()
|
||
status = "PASS" if passed else "FAIL"
|
||
print(f"Status: {status}")
|
||
print(f"Details: {msg}")
|
||
|
||
# Test 2: Frequency separability
|
||
print("\n[Test 2] Frequency Separability")
|
||
print("-" * 50)
|
||
passed, msg = field.test_2_frequency_separability()
|
||
status = "PASS" if passed else "FAIL"
|
||
print(f"Status: {status}")
|
||
print(f"Details: {msg}")
|
||
|
||
# Test 3: Determinism
|
||
print("\n[Test 3] Determinism")
|
||
print("-" * 50)
|
||
passed, msg = field.test_3_determinism()
|
||
status = "PASS" if passed else "FAIL"
|
||
print(f"Status: {status}")
|
||
print(f"Details: {msg}")
|
||
|
||
# Test 4: Energy conservation
|
||
print("\n[Test 4] Energy Conservation")
|
||
print("-" * 50)
|
||
passed, msg = field.test_4_energy_conservation()
|
||
status = "PASS" if passed else "FAIL"
|
||
print(f"Status: {status}")
|
||
print(f"Details: {msg}")
|
||
|
||
print("\n" + "=" * 70)
|
||
print("SUMMARY")
|
||
print("=" * 70)
|
||
print("""
|
||
These tests validate the deterministic backbone of GWL/TSM.
|
||
|
||
If all tests pass:
|
||
- The system supports deterministic field propagation
|
||
- Stochastic extensions can be safely added as perturbations
|
||
- The EM spectrum is expressible
|
||
|
||
If any test fails:
|
||
- The local update law needs refinement
|
||
- Conservation constraints must be enforced
|
||
- Do NOT add stochasticity to mask deterministic failures
|
||
""")
|
||
|
||
# Generate visualization
|
||
field.__init__(n_seeds=100, alpha=0.15, beta=0.15)
|
||
field.initialize_pulse(center=30, width=10, amplitude=1.0, freq=2.0)
|
||
field.run(steps=150)
|
||
|
||
try:
|
||
fig = field.plot_evolution()
|
||
plt.savefig('gwl_deterministic_em_evolution.png', dpi=150)
|
||
print("\nVisualization saved to: gwl_deterministic_em_evolution.png")
|
||
except Exception as e:
|
||
print(f"\nPlotting skipped: {e}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
run_all_tests()
|