""" FlowNet Model — Full Architecture Integration. This is the complete model that integrates all novel components: - Particle encoding (not embeddings) - Flow field dynamics (not attention) - Wave interference (not softmax) - Bond system (not fixed weights) - Phase transitions (not fixed depth) - Consistency field (not just cross-entropy) - Topological memory (not vector DB) """ import torch import torch.nn as nn import torch.nn.functional as F import numpy as np from typing import Dict, Optional, Tuple from ..core.particle import ParticleEncoder, ParticleState, ParticleCombiner, ParticleUpdater from ..core.flow_field import FlowField, FlowFieldEfficient from ..core.wave_processor import WaveProcessor, WaveAttention from ..core.bond_system import BondSystem, BondState from ..core.phase_engine import PhaseEngine from ..core.consistency_field import ConsistencyField from ..core.topological_memory import TopologicalMemory class FlowNetBlock(nn.Module): """A single FlowNet processing block. Equivalent to a Transformer block, but with completely different internal computation: Transformer block: x → LayerNorm → MultiHeadAttention → Residual → LayerNorm → FFN → Residual FlowNet block: x → ParticleUpdate → WaveProcess → BondUpdate → PhaseTransition → Residual """ def __init__( self, d_semantic: int = 256, d_charge: int = 32, d_spin: int = 32, d_memory: int = 128, n_wave_heads: int = 8, d_wave: int = 32, use_efficient_flow: bool = False, local_radius: int = 64, ): super().__init__() self.d_semantic = d_semantic # Particle updater (replaces attention) self.particle_updater = ParticleUpdater(d_semantic, d_charge, d_spin, d_memory) # Wave processor (novel — no Transformer equivalent) self.wave_processor = WaveProcessor(d_semantic, n_wave_heads, d_wave) # Bond system (novel — no Transformer equivalent) self.bond_system = BondSystem(d_semantic, d_charge, d_spin) # Flow field (replaces FFN) if use_efficient_flow: self.flow_field = FlowFieldEfficient(d_semantic, local_radius=local_radius) else: self.flow_field = FlowField(d_semantic) # Normalization (topology-preserving, not LayerNorm) self.norm1 = nn.LayerNorm(d_semantic) self.norm2 = nn.LayerNorm(d_semantic) # Gating — controls information flow self.gate = nn.Sequential( nn.Linear(d_semantic * 2, d_semantic), nn.Sigmoid(), ) def forward( self, particles: ParticleState, bonds: Optional[BondState] = None, ) -> Tuple[ParticleState, BondState, Dict]: """Process one FlowNet block. Args: particles: Input particle state bonds: Previous bond state Returns: Updated particles, bonds, and diagnostics """ residual = particles.semantic # === Step 1: Particle interaction update === particles = self.particle_updater(particles) # === Step 2: Wave processing === wave_output, wave_diag = self.wave_processor(particles) # Gate wave influence gate = self.gate(torch.cat([particles.semantic, wave_output], dim=-1)) particles_semantic = particles.semantic + wave_output * gate # Residual + norm particles_semantic = self.norm1(particles_semantic + residual) # === Step 3: Bond system === particles_for_bonds = ParticleState( semantic=particles_semantic, position=particles.position, charge=particles.charge, mass=particles.mass, spin=particles.spin, amplitude=particles.amplitude, phase=particles.phase, memory_trace=particles.memory_trace, ) bonds, bond_output, bond_diag = self.bond_system(particles_for_bonds, bonds) particles_semantic = particles_semantic + bond_output * 0.1 # === Step 4: Flow field === residual2 = particles_semantic particles_for_flow = ParticleState( semantic=particles_semantic, position=particles.position, charge=particles.charge, mass=particles.mass, spin=particles.spin, amplitude=particles.amplitude, phase=particles.phase, memory_trace=particles.memory_trace, ) particles_for_flow, flow_diag = self.flow_field(particles_for_flow) particles_semantic = self.norm2(particles_for_flow.semantic + residual2) # Reconstruct particle state final_particles = ParticleState( semantic=particles_semantic, position=particles.position, charge=particles.charge, mass=particles.mass, spin=particles.spin, amplitude=particles_for_flow.amplitude, phase=particles_for_flow.phase, memory_trace=particles_for_flow.memory_trace, ) diagnostics = { 'wave': wave_diag, 'bond': bond_diag, 'flow': flow_diag, } return final_particles, bonds, diagnostics class FlowNetModel(nn.Module): """Complete FlowNet model for language modeling. Architecture: 1. Token → Particle encoding 2. N × FlowNet blocks (wave + bond + flow) 3. Phase transition engine (adaptive depth) 4. Consistency field (truth verification) 5. Topological memory (persistent storage) 6. Particle → output decoding This is NOT a Transformer. It processes language through physical dynamics rather than matrix multiplication. """ def __init__( self, vocab_size: int = 32000, d_semantic: int = 256, d_position: int = 64, d_charge: int = 32, d_spin: int = 32, d_memory: int = 128, n_blocks: int = 6, n_wave_heads: int = 8, d_wave: int = 32, use_phase_engine: bool = True, use_consistency: bool = True, use_topological_memory: bool = True, max_seq_len: int = 8192, use_efficient_flow: bool = False, local_radius: int = 64, ): super().__init__() self.vocab_size = vocab_size self.d_semantic = d_semantic self.n_blocks = n_blocks self.use_phase_engine = use_phase_engine self.use_consistency = use_consistency self.use_topological_memory = use_topological_memory # === Particle Encoder === self.encoder = ParticleEncoder( vocab_size=vocab_size, d_semantic=d_semantic, d_position=d_position, d_charge=d_charge, d_spin=d_spin, d_memory=d_memory, max_seq_len=max_seq_len, ) # === FlowNet Blocks === self.blocks = nn.ModuleList([ FlowNetBlock( d_semantic=d_semantic, d_charge=d_charge, d_spin=d_spin, d_memory=d_memory, n_wave_heads=n_wave_heads, d_wave=d_wave, use_efficient_flow=use_efficient_flow, local_radius=local_radius, ) for _ in range(n_blocks) ]) # === Phase Transition Engine === if use_phase_engine: self.phase_engine = PhaseEngine( d_semantic=d_semantic, min_steps=2, max_steps=16, ) # === Consistency Field === if use_consistency: self.consistency_field = ConsistencyField( d_semantic=d_semantic, ) # === Topological Memory === if use_topological_memory: self.topological_memory = TopologicalMemory( d_semantic=d_semantic, ) # === Output Head === # Converts final particle state to token logits self.output_head = nn.Sequential( nn.Linear(d_semantic + d_memory, d_semantic), nn.GELU(), nn.LayerNorm(d_semantic), nn.Linear(d_semantic, vocab_size), ) # Particle combiner for sequence-level tasks self.combiner = ParticleCombiner(d_semantic, d_memory, vocab_size) def forward( self, token_ids: torch.Tensor, # [batch, seq_len] labels: Optional[torch.Tensor] = None, store_memory: bool = False, use_stored_memory: bool = True, ) -> Dict: """Forward pass through the complete FlowNet architecture. Args: token_ids: Input token indices [batch, seq_len] labels: Target tokens for loss computation [batch, seq_len] store_memory: Whether to store processed patterns in memory use_stored_memory: Whether to retrieve from topological memory Returns: Dictionary with logits, loss, diagnostics """ batch, seq_len = token_ids.shape device = token_ids.device # === Step 1: Encode tokens into particles === particles = self.encoder(token_ids) # === Step 2: Retrieve from topological memory === memory_diag = {} if self.use_topological_memory and use_stored_memory and self.training is False: # Query memory with current context particles.semantic, memory_diag = self.topological_memory( particles.semantic, particles.semantic ) # === Step 3: Process through FlowNet blocks === bonds = None block_diagnostics = [] for block in self.blocks: particles, bonds, block_diag = block(particles, bonds) block_diagnostics.append(block_diag) # === Step 4: Phase transition (adaptive refinement) === phase_diag = {} if self.use_phase_engine: # Use first block as the refinement function def flow_fn(p): updated, _, diag = self.blocks[0](p, bonds) return updated, diag particles, phase_diag = self.phase_engine( particles, flow_fn=flow_fn ) # === Step 5: Consistency field === consistency_diag = {} if self.use_consistency: particles.semantic, consistency_diag = self.consistency_field( particles.semantic ) # === Step 6: Store in topological memory === if self.use_topological_memory and store_memory: # Store the processed pattern for b in range(batch): self.topological_memory.store(particles.semantic[b]) # === Step 7: Decode to token logits === # Combine semantic and memory for output combined = torch.cat([particles.semantic, particles.memory_trace], dim=-1) logits = self.output_head(combined) # [batch, seq_len, vocab_size] # === Compute loss === loss = None if labels is not None: # Standard language modeling loss shift_logits = logits[..., :-1, :].contiguous() shift_labels = labels[..., 1:].contiguous() loss = F.cross_entropy( shift_logits.reshape(-1, self.vocab_size), shift_labels.reshape(-1), ignore_index=-100, ) # Add consistency field loss (penalize high energy) if self.use_consistency: energy_penalty = consistency_diag.get('final_energy', 0) if isinstance(energy_penalty, torch.Tensor): loss = loss + energy_penalty * 0.01 # Add phase transition regularization if self.use_phase_engine: # Encourage proper phase transitions pass # PhaseEngine has its own loss, applied externally # === Compile diagnostics === diagnostics = { 'blocks': block_diagnostics, 'phase': phase_diag, 'consistency': consistency_diag, 'memory': memory_diag, 'particles': { 'mean_amplitude': particles.amplitude.mean().item(), 'mean_phase': particles.phase.mean().item(), 'phase_coherence': torch.abs( torch.exp(1j * particles.phase).mean() ).item(), }, } return { 'logits': logits, 'loss': loss, 'diagnostics': diagnostics, 'particles': particles, } def generate( self, token_ids: torch.Tensor, max_new_tokens: int = 100, temperature: float = 1.0, top_k: int = 50, top_p: float = 0.9, consistency_check: bool = True, ) -> torch.Tensor: """Generate tokens autoregressively. Unlike standard generation, FlowNet can: 1. Use topological memory to recall relevant patterns 2. Check consistency of generated tokens 3. Adapt computation depth based on difficulty """ self.eval() generated = token_ids.clone() with torch.no_grad(): for _ in range(max_new_tokens): # Forward pass output = self.forward( generated, store_memory=False, use_stored_memory=True, ) logits = output['logits'][:, -1, :] # last token logits # Consistency check — penalize inconsistent continuations if consistency_check and self.use_consistency: # Check if top candidates are consistent top_logits, top_indices = logits.topk(top_k) # Quick energy check for top candidates # (In production, this would be more sophisticated) pass # Temperature scaling logits = logits / temperature # Top-k filtering if top_k > 0: indices_to_remove = logits < torch.topk(logits, top_k)[0][..., -1, None] logits[indices_to_remove] = float('-inf') # Top-p (nucleus) filtering if top_p < 1.0: sorted_logits, sorted_indices = torch.sort(logits, descending=True) cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1) sorted_indices_to_remove = cumulative_probs > top_p sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone() sorted_indices_to_remove[..., 0] = 0 indices_to_remove = sorted_indices_to_remove.scatter( 1, sorted_indices, sorted_indices_to_remove ) logits[indices_to_remove] = float('-inf') # Sample probs = F.softmax(logits, dim=-1) next_token = torch.multinomial(probs, num_samples=1) generated = torch.cat([generated, next_token], dim=1) return generated class FlowNetForClassification(nn.Module): """FlowNet variant for sequence classification tasks.""" def __init__( self, vocab_size: int = 32000, d_semantic: int = 256, n_classes: int = 2, **kwargs ): super().__init__() self.model = FlowNetModel(vocab_size=vocab_size, d_semantic=d_semantic, **kwargs) self.classifier = nn.Sequential( nn.Linear(d_semantic + 128, 256), # +128 for memory nn.GELU(), nn.Dropout(0.1), nn.Linear(256, n_classes), ) def forward(self, token_ids: torch.Tensor, labels: Optional[torch.Tensor] = None): output = self.model(token_ids, store_memory=False) particles = output['particles'] # Pool particles (phase-weighted) combined = torch.cat([particles.semantic, particles.memory_trace], dim=-1) phase_weights = F.softmax(torch.cos(particles.phase), dim=-1) pooled = (combined * phase_weights.unsqueeze(-1)).sum(dim=1) logits = self.classifier(pooled) loss = None if labels is not None: loss = F.cross_entropy(logits, labels) return {'logits': logits, 'loss': loss}