| """ |
| Mathematical utilities for FlowNet. |
| Wave mechanics, topology helpers, and novel computation primitives. |
| """ |
|
|
| import torch |
| import torch.nn.functional as F |
| import numpy as np |
| from typing import Tuple, List, Optional |
|
|
|
|
| |
| |
| |
|
|
| def complex_wave(amplitude: torch.Tensor, phase: torch.Tensor) -> torch.Tensor: |
| """Create a complex wave from amplitude and phase. |
| |
| wave = amplitude * exp(i * phase) |
| |
| This is the fundamental representation for wave-based processing. |
| Real and imaginary parts encode different aspects of meaning. |
| """ |
| return amplitude * torch.exp(1j * phase) |
|
|
|
|
| def wave_interference(wave1: torch.Tensor, wave2: torch.Tensor) -> torch.Tensor: |
| """Compute interference between two waves. |
| |
| Constructive interference (in-phase) → amplified signal |
| Destructive interference (out-of-phase) → cancelled signal |
| |
| Returns the interference pattern as a real tensor. |
| """ |
| interference = wave1 + wave2 |
| return torch.abs(interference) |
|
|
|
|
| def phase_coherence(phases: torch.Tensor, dim: int = -1) -> torch.Tensor: |
| """Measure how synchronized a set of phases are. |
| |
| Returns 1.0 if all phases are identical, 0.0 if uniformly distributed. |
| This measures "semantic alignment" between particles. |
| |
| Uses the order parameter from Kuramoto model: |
| R = |1/N * sum(exp(i * theta_j))| |
| """ |
| complex_phases = torch.exp(1j * phases) |
| order_param = torch.abs(torch.mean(complex_phases, dim=dim)) |
| return order_param |
|
|
|
|
| def resonance_detect( |
| wave_field: torch.Tensor, |
| min_amplitude: float = 0.1, |
| coherence_threshold: float = 0.7 |
| ) -> List[torch.Tensor]: |
| """Detect standing wave patterns (resonances) in a wave field. |
| |
| Resonances represent stable meaning patterns — when multiple particles |
| oscillate in sync, their shared meaning is amplified. |
| |
| Returns list of resonance regions (tensors of particle indices). |
| """ |
| amplitudes = torch.abs(wave_field) |
| phases = torch.angle(wave_field) |
| |
| |
| active_mask = amplitudes > min_amplitude |
| |
| |
| n = phases.shape[0] |
| visited = torch.zeros(n, dtype=torch.bool) |
| resonances = [] |
| |
| phase_diff_matrix = torch.abs( |
| phases.unsqueeze(0) - phases.unsqueeze(1) |
| ) |
| |
| phase_diff_matrix = torch.min(phase_diff_matrix, 2 * np.pi - phase_diff_matrix) |
| |
| for i in range(n): |
| if visited[i] or not active_mask[i]: |
| continue |
| |
| in_phase = (phase_diff_matrix[i] < coherence_threshold) & active_mask |
| resonances.append(torch.where(in_phase)[0]) |
| visited[in_phase] = True |
| |
| return resonances |
|
|
|
|
| def wave_propagate( |
| source_amplitude: torch.Tensor, |
| source_phase: torch.Tensor, |
| distance: torch.Tensor, |
| decay_rate: float = 0.1, |
| wavelength: float = 1.0 |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| """Propagate a wave through space with distance-based decay. |
| |
| Waves carry information across the particle field without needing |
| direct pairwise attention. Information travels at wave speed. |
| |
| Returns (amplitude_at_distance, phase_at_distance). |
| """ |
| |
| received_amplitude = source_amplitude / (1.0 + decay_rate * distance) |
| |
| |
| received_phase = source_phase + 2 * np.pi * distance / wavelength |
| |
| return received_amplitude, received_phase |
|
|
|
|
| |
| |
| |
|
|
| def pairwise_distances(positions: torch.Tensor) -> torch.Tensor: |
| """Compute pairwise Euclidean distances between particle positions.""" |
| diff = positions.unsqueeze(0) - positions.unsqueeze(1) |
| return torch.norm(diff, dim=-1) |
|
|
|
|
| def build_adjacency( |
| positions: torch.Tensor, |
| threshold: float = 1.0, |
| use_bonds: Optional[torch.Tensor] = None |
| ) -> torch.Tensor: |
| """Build adjacency matrix from particle positions. |
| |
| If use_bonds is provided, uses bond strengths instead of distance. |
| """ |
| if use_bonds is not None: |
| return use_bonds |
| |
| distances = pairwise_distances(positions) |
| adjacency = (distances < threshold).float() |
| |
| adjacency.fill_diagonal_(0) |
| return adjacency |
|
|
|
|
| def compute_betti_numbers(adjacency: torch.Tensor, max_dim: int = 2) -> List[int]: |
| """Compute Betti numbers (topological features) from adjacency. |
| |
| β₀ = number of connected components |
| β₁ = number of loops/holes |
| β₂ = number of voids |
| |
| This is a simplified computation for the prototype. |
| Full persistent homology would use giotto-tda or similar. |
| """ |
| n = adjacency.shape[0] |
| |
| |
| visited = torch.zeros(n, dtype=torch.bool) |
| beta_0 = 0 |
| for i in range(n): |
| if visited[i]: |
| continue |
| beta_0 += 1 |
| queue = [i] |
| while queue: |
| node = queue.pop(0) |
| if visited[node]: |
| continue |
| visited[node] = True |
| neighbors = torch.where(adjacency[node] > 0)[0] |
| queue.extend(neighbors[~visited[neighbors]].tolist()) |
| |
| |
| edges = torch.sum(adjacency > 0).item() / 2 |
| beta_1 = max(0, int(edges - n + beta_0)) |
| |
| |
| beta_2 = 0 |
| |
| return [beta_0, beta_1, beta_2] |
|
|
|
|
| def topological_signature(adjacency: torch.Tensor) -> torch.Tensor: |
| """Compute a topological signature vector for memory comparison. |
| |
| This is a fixed-size representation of the topology that can be |
| used for similarity search in topological memory. |
| """ |
| betti = compute_betti_numbers(adjacency) |
| |
| |
| n = adjacency.shape[0] |
| degrees = torch.sum(adjacency > 0, dim=-1).float() |
| |
| features = torch.tensor([ |
| float(betti[0]), |
| float(betti[1]), |
| float(betti[2]), |
| n, |
| torch.mean(degrees), |
| torch.std(degrees), |
| torch.max(degrees), |
| float(torch.sum(adjacency > 0).item()) / 2, |
| ], dtype=torch.float32) |
| |
| return features |
|
|
|
|
| |
| |
| |
|
|
| def kuramoto_step( |
| phases: torch.Tensor, |
| frequencies: torch.Tensor, |
| coupling: torch.Tensor, |
| dt: float = 0.01 |
| ) -> torch.Tensor: |
| """One step of the Kuramoto model for phase synchronization. |
| |
| dθᵢ/dt = ωᵢ + Σⱼ Kᵢⱼ sin(θⱼ - θᵢ) |
| |
| This governs how particles synchronize their phases — |
| strongly coupled particles synchronize, weakly coupled drift apart. |
| Synchronization = semantic alignment. |
| """ |
| n = phases.shape[0] |
| phase_diff = phases.unsqueeze(0) - phases.unsqueeze(1) |
| |
| |
| sync_force = torch.sum(coupling * torch.sin(-phase_diff), dim=-1) |
| |
| |
| new_phases = phases + dt * (frequencies + sync_force) |
| |
| |
| new_phases = new_phases % (2 * np.pi) |
| |
| return new_phases |
|
|
|
|
| def lennard_jones_force( |
| positions: torch.Tensor, |
| epsilon: float = 1.0, |
| sigma: float = 1.0, |
| cutoff: float = 3.0 |
| ) -> torch.Tensor: |
| """Compute Lennard-Jones-like forces between particles. |
| |
| Attraction at medium range, repulsion at close range. |
| This creates natural "preferred distances" between particles — |
| semantically related particles settle at stable distances. |
| |
| F = 24ε * [(σ/r)⁶ * (2(σ/r)⁶ - 1)] / r |
| |
| Returns force vectors for each particle [n, dim]. |
| """ |
| n = positions.shape[0] |
| diff = positions.unsqueeze(0) - positions.unsqueeze(1) |
| distances = torch.norm(diff, dim=-1, keepdim=True) |
| distances = torch.clamp(distances, min=0.1) |
| |
| |
| mask = (distances.squeeze(-1) < cutoff).float() |
| mask.fill_diagonal_(0) |
| |
| |
| sr6 = (sigma / distances) ** 6 |
| force_mag = 24 * epsilon * sr6 * (2 * sr6 - 1) / distances |
| |
| |
| direction = diff / distances |
| forces = force_mag * direction * mask.unsqueeze(-1) |
| |
| return forces.sum(dim=1) |
|
|
|
|
| def simulated_annealing_step( |
| state: torch.Tensor, |
| energy_fn, |
| temperature: float, |
| perturbation_scale: float = 0.1 |
| ) -> Tuple[torch.Tensor, float]: |
| """One step of simulated annealing for energy minimization. |
| |
| Used by the Consistency Field to find low-energy (true) states. |
| High temperature → accept worse states (explore) |
| Low temperature → only accept better states (exploit) |
| """ |
| current_energy = energy_fn(state) |
| |
| |
| perturbation = perturbation_scale * torch.randn_like(state) |
| proposed_state = state + perturbation |
| proposed_energy = energy_fn(proposed_state) |
| |
| |
| delta_e = proposed_energy - current_energy |
| if delta_e < 0: |
| |
| return proposed_state, proposed_energy |
| else: |
| |
| acceptance_prob = torch.exp(-delta_e / max(temperature, 1e-8)) |
| if torch.rand(1).item() < acceptance_prob.item(): |
| return proposed_state, proposed_energy |
| else: |
| return state, current_energy |
|
|
|
|
| |
| |
| |
|
|
| def phase_activation(x: torch.Tensor) -> torch.Tensor: |
| """Activation function that maps to phase space [0, 2π]. |
| |
| Unlike ReLU/GELU, this preserves periodic structure. |
| """ |
| return torch.sigmoid(x) * 2 * np.pi |
|
|
|
|
| def amplitude_activation(x: torch.Tensor) -> torch.Tensor: |
| """Activation for amplitudes — always positive, bounded. |
| |
| Uses softplus to ensure positivity with smooth gradients. |
| """ |
| return F.softplus(x) |
|
|
|
|
| def topological_normalize(x: torch.Tensor, dim: int = -1) -> torch.Tensor: |
| """Normalize while preserving topological structure. |
| |
| Instead of LayerNorm (which destroys phase information), |
| normalize amplitudes independently of phases. |
| """ |
| |
| magnitude = torch.norm(x, dim=dim, keepdim=True) |
| direction = x / (magnitude + 1e-8) |
| |
| |
| normalized_magnitude = F.softmax(magnitude, dim=dim) |
| |
| return direction * normalized_magnitude |
|
|