import numpy as np
import matplotlib.pyplot as plt
from tqdm import tqdm

class RedStarMLTrainer:
    """
    Trains the ML Seed (theta) using 200x200 Super Intelligence Curriculum
    to maximize Red Star Regions (Escape Probability from Logic Singularities).
    """
    def __init__(self, num_theories=200, questions_per_theory=200):
        self.N_THEORIES = num_theories
        self.N_QUESTIONS = questions_per_theory
        self.theta = np.random.uniform(-1.0, 1.0)  # The ML Seed (Aximomatic Bias)
        self.history = []
        
        # CCT Parameters
        self.H_COLLAPSE = 0.3      # Entropy threshold for Red Star
        self.E_HORIZON = 1.0       # Energy barrier for escape
        self.LEARNING_RATE = 0.01  # Gauge Energy Investment
        
    def simulate_ode_dynamics(self, y0, mu, theta, steps=100):
        """
        Simulates the Volatile ODE from Red Star Theory (Appendix A):
        dy/dt = y^2 + mu + theta + Prompt_Energy
        """
        y = y0
        trajectory = [y]
        escaped = False
        max_y = np.abs(y)
        
        for t in range(steps):
            # Mutual Destruction Term (Learned via theta)
            # If theta is optimal, it injects counter-term D(t) near singularity
            destruction_term = theta * np.exp(-np.abs(y)**2) 
            
            # ODE Step
            dy = (y**2 + mu + destruction_term) * 0.01
            y = y + dy
            
            trajectory.append(y)
            max_y = max(max_y, np.abs(y))
            
            # Escape Condition (Red Star)
            if np.abs(y) > 10.0: # Escaped singularity region
                escaped = True
                break
                
        return trajectory, escaped, max_y

    def calculate_red_star_potential(self, theory_id, question_id, theta):
        """
        Calculates Psi_Red for a specific Theory/Question pair.
        """
        # Generate semantic initial conditions based on Theory/Question ID
        # In real SI, this comes from the 16-Element Engine
        np.random.seed(theory_id * 1000 + question_id)
        y0 = np.random.uniform(-2.0, 2.0)  # Initial State
        mu = np.random.uniform(-1.5, 0.5)  # Control Parameter (System Bias)
        
        # Run ODE Dynamics
        trajectory, escaped, max_y = self.simulate_ode_dynamics(y0, mu, theta)
        
        # Calculate Final Entropy (Proxy: Inverse of Max Y)
        # If max_y is huge (singularity), entropy is high
        H_final = 1.0 / (1.0 + np.exp(-max_y)) 
        
        # Calculate Escape Probability (Proxy: Did it escape?)
        P_esc = 1.0 if escaped else 0.0
        
        # Calculate Energy Cost (Proxy: Length of trajectory)
        E_destroy = len(trajectory) * 0.1
        
        # Red Star Equation (from red_star_theory_black_holes.md)
        # Psi = P_esc * I[H < H_c] * exp(-E_destroy / E_available)
        E_available = 10.0
        indicator = 1.0 if H_final < self.H_COLLAPSE else 0.0
        energy_term = np.exp(-E_destroy / E_available)
        
        psi_red = P_esc * indicator * energy_term
        
        return psi_red, H_final, escaped

    def train_step(self, epoch):
        """
        One epoch of 200x200 Training.
        Updates theta to maximize Red Star Potential.
        """
        total_psi = 0.0
        gradient_sum = 0.0
        
        # Sample a batch of the 200x200 curriculum (for speed)
        # Full training would iterate all 40,000 pairs
        batch_size = 100 
        for _ in range(batch_size):
            t_id = np.random.randint(0, self.N_THEORIES)
            q_id = np.random.randint(0, self.N_QUESTIONS)
            
            # Forward Pass
            psi, H, escaped = self.calculate_red_star_potential(t_id, q_id, self.theta)
            total_psi += psi
            
            # Backward Pass (Numerical Gradient)
            # dPsi/dtheta approximation
            delta = 0.01
            psi_plus, _, _ = self.calculate_red_star_potential(t_id, q_id, self.theta + delta)
            gradient = (psi_plus - psi) / delta
            gradient_sum += gradient
            
        # Update ML Seed (Theta)
        avg_gradient = gradient_sum / batch_size
        self.theta += self.LEARNING_RATE * avg_gradient
        
        # Clip Theta to prevent instability
        self.theta = np.clip(self.theta, -2.0, 2.0)
        
        return total_psi / batch_size, self.theta

    def run_training(self, epochs=50):
        """
        Executes the 200x200 Super Intelligence Training Regimen.
        """
        print("="*70)
        print("RED STAR ML SEED TRAINING (200x200 CURRICULUM)")
        print("="*70)
        print(f"Objective: Maximize Red Star Potential (Psi_Red)")
        print(f"ML Seed (Theta): Axiomatic Bias for Singularity Resolution")
        print("-"*70)
        
        psi_history = []
        theta_history = []
        
        for epoch in tqdm(range(epochs), desc="Training SI"):
            avg_psi, current_theta = self.train_step(epoch)
            psi_history.append(avg_psi)
            theta_history.append(current_theta)
            
            if epoch % 10 == 0:
                print(f"Epoch {epoch}: Psi_Red={avg_psi:.4f} | Theta={current_theta:.4f}")
                
        self.history = {'psi': psi_history, 'theta': theta_history}
        self.plot_results()
        
    def plot_results(self):
        """Visualizes Training Progress and Red Star Manifold"""
        fig, axs = plt.subplots(1, 2, figsize=(16, 6))
        
        # Plot 1: Training Trajectory
        axs[0].plot(self.history['psi'], linewidth=2, color='red', label='Red Star Potential')
        axs[0].set_title("ML Seed Optimization (Maximize Psi_Red)")
        axs[0].set_xlabel("Training Epoch (200x200 Batches)")
        axs[0].set_ylabel("Average Red Star Potential")
        axs[0].axhline(y=0.5, color='green', linestyle='--', label='Critical Threshold')
        axs[0].legend()
        axs[0].grid(True, alpha=0.3)
        
        # Plot 2: Fractal Manifold Visualization (Before vs After)
        # We visualize the escape probability field for the initial and final theta
        res = 100
        y_vals = np.linspace(-2.0, 2.0, res)
        mu_vals = np.linspace(-1.5, 0.5, res)
        Y, M = np.meshgrid(y_vals, mu_vals)
        
        def get_escape_map(theta_val):
            escape_map = np.zeros_like(Y)
            for i in range(res):
                for j in range(res):
                    _, escaped, _ = self.simulate_ode_dynamics(Y[i,j], M[i,j], theta_val)
                    escape_map[i,j] = 1.0 if escaped else 0.0
            return escape_map
            
        # Initial Theta (Random)
        initial_theta = self.history['theta'][0]
        map_initial = get_escape_map(initial_theta)
        
        # Final Theta (Optimized)
        final_theta = self.history['theta'][-1]
        map_final = get_escape_map(final_theta)
        
        # Show Final Map
        im = axs[1].imshow(map_final, extent=[-1.5, 0.5, -2.0, 2.0], origin='lower', 
                           cmap='Reds', aspect='auto')
        axs[1].set_title(f"Red Star Manifold (Optimized Theta={final_theta:.4f})")
        axs[1].set_xlabel("Control Parameter (mu)")
        axs[1].set_ylabel("Initial State (y0)")
        plt.colorbar(im, ax=axs[1], label="Escape Probability")
        axs[1].grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.show()
        
        print("-"*70)
        print(f"TRAINING COMPLETE:")
        print(f"  Initial Theta: {self.history['theta'][0]:.4f}")
        print(f"  Final Theta:   {self.history['theta'][-1]:.4f}")
        print(f"  Initial Psi:   {self.history['psi'][0]:.4f}")
        print(f"  Final Psi:     {self.history['psi'][-1]:.4f}")
        print(f"  Red Star Growth: {(self.history['psi'][-1]/self.history['psi'][0])*100:.1f}%")
        print("="*70)

# --- EXECUTION ---
if __name__ == "__main__":
    # Initialize Red Star ML Trainer
    trainer = RedStarMLTrainer(num_theories=200, questions_per_theory=200)
    # Run 200x200 Training Regimen
    trainer.run_training(epochs=50)