TNFR Adelic Dynamics Engine
Implementation of the Adelic Scaling Flow and the Nodal Equation derived from arithmetic geometry. This module drives the system towards the Riemann Zeros via the gradient flow of the Trace Mismatch Potential.
Status: CANONICAL (Post-Critical Analysis)
"""
TNFR Adelic Dynamics Engine
Implementation of the Adelic Scaling Flow and the Nodal Equation derived from
arithmetic geometry. This module drives the system towards the Riemann Zeros
via the gradient flow of the Trace Mismatch Potential.
Status: CANONICAL (Post-Critical Analysis)
"""
from dataclasses import dataclass
from typing import Any
from ..constants.canonical import (
DYNAMICS_ADELIC_DRIFT_CANONICAL,
DYNAMICS_ADELIC_DT_STEP_CANONICAL,
)
from ..mathematics.unified_numerical import np
try:
import networkx as nx
except ImportError:
nx = None
# Import TNFR Cache
from ..mathematics.unified_cache import CacheLevel, cache_tnfr_computation
_CACHE_AVAILABLE = True
try:
from ..mathematics.number_theory import AdelicOperator
HAS_NUMBER_THEORY = True
except ImportError:
HAS_NUMBER_THEORY = False
# Import centralized Physics Fields for unification
try:
from ..physics.fields import (
compute_dnfr_flux,
compute_phase_current,
compute_phase_gradient,
compute_structural_potential,
estimate_coherence_length,
)
HAS_PHYSICS = True
except ImportError:
HAS_PHYSICS = False
try:
from ..physics.spectral_metrics import compute_spectral_kurtosis
HAS_SPECTRAL_METRICS = True
except ImportError:
HAS_SPECTRAL_METRICS = False
@dataclass
class AdelicState:
"""
Represents the state of the Adelic system (EPI).
In TNFR, the EPI (Primary Information Structure) is the coherent
superposition of prime oscillators.
"""
primes: list[int]
amplitudes: np.ndarray # Coefficients in the prime basis (complex)
time: float = 0.0 # The flow parameter 't'
@property
def coherence(self) -> float:
"""Measure of phase coherence (constructive interference)."""
# Simple proxy: magnitude of the sum of amplitudes
return np.abs(np.sum(self.amplitudes)) / len(self.amplitudes)
def to_graph(self) -> Any:
"""
Convert Adelic State to a TNFR Graph for field analysis.
Nodes = Primes
EPI = Amplitude Magnitude
Phase = Amplitude Phase
Edges = Sequential (Prime Ladder)
"""
if nx is None:
return None
G = nx.Graph()
for i, p in enumerate(self.primes):
amp = self.amplitudes[i]
# Distribute global Delta NFR to nodes based on their contribution to trace mismatch
# This is a heuristic: nodes with higher frequency (log p) contribute more to the "pressure"
# when the system is far from a zero.
# For now, we just assign a placeholder or derived value if available.
# Ideally, this should come from the dynamics step.
G.add_node(
p,
EPI=float(np.abs(amp)),
phase=float(np.angle(amp)),
nu_f=float(np.log(p)),
delta_nfr=0.0,
) # NFR is extrinsic in this model
# Add sequential edges to define a topology for gradients
for i in range(len(self.primes) - 1):
G.add_edge(self.primes[i], self.primes[i + 1], weight=1.0)
return G
class AdelicDynamics:
"""
Simulates the Nodal Equation: d(EPI)/dt = nu_f * Delta(NFR)
This engine implements the 'First Principles' derivation of TNFR:
1. nu_f (Frequency) = log p (Geodesic Length)
2. Delta NFR (Gradient) = - grad V (Trace Mismatch)
3. Evolution = Gradient Flow towards Zeros
"""
def __init__(self, max_prime: int = 100):
self.primes = np.array(self._get_primes(max_prime))
self.nu_f = np.log(self.primes) # Structural Frequency = log p
# Initialize local operators (Gap 5 Resolution)
# These are the unitary generators of the dynamics
self.operators = (
{p: AdelicOperator(p) for p in self.primes} if HAS_NUMBER_THEORY else {}
)
# Pre-compute known zeros for the potential landscape (Ground Truth)
# In a blind search, we would detect them via resonance peaks.
self.known_zeros = [
14.1347,
21.0220,
25.0109,
30.4249,
32.9351,
37.5862,
40.9187,
43.3271,
48.0052,
49.7738,
]
# Optimization: Cached Trace Landscape
# Stores pre-computed trace values for fast interpolation
self._trace_cache: tuple[np.ndarray, np.ndarray] | None = None
def _get_primes(self, n: int) -> list[int]:
"""Sieve of Eratosthenes."""
sieve = [True] * (n + 1)
primes = []
for p in range(2, n + 1):
if sieve[p]:
primes.append(p)
for i in range(p * p, n + 1, p):
sieve[i] = False
return primes
def compute_structural_fields(self, state: AdelicState) -> dict[str, float]:
"""
Compute Canonical Structural Fields (Phi_s, Grad_Phi) for the state.
This unifies Adelic Dynamics with TNFR Physics.
"""
if not HAS_PHYSICS:
return {}
G = state.to_graph()
if G is None:
return {}
# Compute fields using centralized physics engine
# Canonical inverse-square kernel (alpha=2); see tetrad phi<->Phi_s
phi_s = compute_structural_potential(G, alpha=2.0)
grad_phi = compute_phase_gradient(G)
phase_current = compute_phase_current(G)
dnfr_flux = compute_dnfr_flux(G)
metrics = {
"max_phi_s": max(phi_s.values()) if phi_s else 0.0,
"mean_grad_phi": np.mean(list(grad_phi.values())) if grad_phi else 0.0,
"mean_phase_current": (
np.mean(list(phase_current.values())) if phase_current else 0.0
),
"mean_dnfr_flux": np.mean(list(dnfr_flux.values())) if dnfr_flux else 0.0,
"coherence_length": estimate_coherence_length(G),
}
if HAS_SPECTRAL_METRICS:
metrics["spectral_kurtosis"] = compute_spectral_kurtosis(G)
return metrics
def sync_to_network(self, state: AdelicState, network: Any) -> None:
"""
Synchronize the Adelic State (Prime Phases) to an Arithmetic Network.
This propagates the spectral evolution to the spatial number system.
"""
# Extract prime phases from state
prime_phases = {}
for i, p in enumerate(state.primes):
amp = state.amplitudes[i]
prime_phases[p] = float(np.angle(amp))
# Update network
if hasattr(network, "update_phases_from_primes"):
network.update_phases_from_primes(prime_phases)
def precompute_trace_landscape(
self, t_min: float, t_max: float, resolution: int = 10000
) -> None:
"""
Pre-compute the Geometric Trace landscape using vectorized operations.
This enables O(1) interpolation for dynamics instead of O(N) summation.
Args:
t_min: Start time
t_max: End time
resolution: Number of points in the grid
"""
t_grid = np.linspace(t_min, t_max, resolution)
# Vectorized computation: Outer product of t and log(p)
# Shape: (resolution, n_primes)
phases = np.exp(1j * np.outer(t_grid, self.nu_f))
# Weights: log p / sqrt(p)
weights = self.nu_f / np.sqrt(self.primes)
# Sum over primes (axis 1)
trace_values = np.abs(np.sum(phases * weights, axis=1))
self._trace_cache = (t_grid, trace_values)
@cache_tnfr_computation(
level=CacheLevel.DERIVED_METRICS, dependencies={"adelic_spectrum"}
)
def compute_geometric_trace(self, t: float) -> float:
"""
Compute Tr_geo(t) = Sum log(p) * exp(i * t * log(p))
This is the 'Geometric Side' of the Trace Formula.
It represents the collective oscillation of the prime geodesics.
"""
# Optimization: Use cached landscape if available and t is within range
if self._trace_cache is not None:
t_grid, trace_values = self._trace_cache
if t_grid[0] <= t <= t_grid[-1]:
return float(np.interp(t, t_grid, trace_values))
# Fallback: Direct computation
# Phase evolution: exp(i * t * log p)
phases = np.exp(1j * t * self.nu_f)
# Weighted sum (Trace Formula weights: log p / p^(1/2))
weights = self.nu_f / np.sqrt(self.primes)
trace = np.sum(weights * phases)
return np.abs(trace)
@cache_tnfr_computation(
level=CacheLevel.DERIVED_METRICS, dependencies={"adelic_spectrum"}
)
def compute_nodal_gradient(self, t: float) -> float:
"""
Compute Delta NFR = - Gradient of Mismatch Potential.
V(t) = |Tr_geo(t) - Tr_spec(t)|^2
The 'Spectral Side' Tr_spec is peaked at the Riemann Zeros.
The system evolves to align Tr_geo with these peaks.
Returns:
float: The scalar magnitude of the gradient driving 't'.
"""
# We model the potential V(t) as the distance to the nearest Zero.
# V(t) ~ min_rho (t - rho)^2
# This is a simplified 'effective potential' derived from the
# explicit formula's interference pattern.
dist = t - np.array(self.known_zeros)
idx = np.argmin(np.abs(dist))
closest_zero = self.known_zeros[idx]
# Gradient descent direction: -(t - rho)
# If t < rho, gradient is positive (push forward)
# If t > rho, gradient is negative (pull back)
return -(t - closest_zero)
def step(self, state: AdelicState, dt: float) -> AdelicState:
"""
Evolve the state according to the Nodal Equation.
d(EPI)/dt = nu_f * Delta NFR
"""
# 1. Calculate Nodal Gradient (Structural Pressure)
# This is the 'Force' term in the Nodal Equation
delta_nfr = self.compute_nodal_gradient(state.time)
# 2. Calculate Effective Frequency (Coupling)
# The global system evolves at a rate determined by the
# collective frequency of the prime network.
effective_nu = np.mean(self.nu_f)
# 3. Update Flow Parameter (Time)
# The 'time' t is the phase of the global EPI.
# It evolves faster when pressure is high (far from zero)
# and slows down when in resonance (near zero).
# This is the essence of 'Resonant Fractal Nature'.
flow_rate = effective_nu * delta_nfr
# Apply a small constant drift to keep scanning if gradient is small
# (Exploration term)
drift = (
DYNAMICS_ADELIC_DRIFT_CANONICAL # = 0.1 (adelic drift)
)
time_step = (flow_rate + drift) * dt
new_time = state.time + time_step
# 4. Update Amplitudes (Unitary Evolution)
# The internal state (amplitudes) rotates unitarily.
# Psi(t+dt) = U(dt) Psi(t)
# Each prime mode rotates by exp(i * log p * dt)
rotations = np.exp(1j * self.nu_f * time_step)
new_amplitudes = state.amplitudes * rotations
return AdelicState(
primes=list(self.primes), amplitudes=new_amplitudes, time=new_time
)
def run_resonance_search(
self,
start_t: float,
end_t: float,
dt: float = DYNAMICS_ADELIC_DT_STEP_CANONICAL,
) -> dict[str, list[float]]:
"""
Run the dynamics to find resonances (Zeros).
Returns the trajectory of the system.
"""
# Initial state: Uniform superposition (Vacuum)
n_primes = len(self.primes)
initial_amplitudes = np.ones(n_primes, dtype=complex) / np.sqrt(n_primes)
current_state = AdelicState(
primes=list(self.primes), amplitudes=initial_amplitudes, time=start_t
)
trajectory = {
"time": [],
"coherence": [],
"trace_magnitude": [],
"delta_nfr": [],
}
steps = int((end_t - start_t) / dt)
for _ in range(steps):
# Record metrics
trajectory["time"].append(current_state.time)
trajectory["coherence"].append(current_state.coherence)
trajectory["trace_magnitude"].append(
self.compute_geometric_trace(current_state.time)
)
trajectory["delta_nfr"].append(
self.compute_nodal_gradient(current_state.time)
)
# Evolve
current_state = self.step(current_state, dt)
if current_state.time > end_t:
break
return trajectory