#!/usr/bin/env python3 """ Flask Web GUI for the Self-Evolving Neural Network. Provides real-time monitoring of evolutionary training with: - Live fitness charts (Chart.js) - Start/Stop/Reset controls - Genome architecture visualization - Evolution history browser """ import os import sys import json import threading import time from pathlib import Path import numpy as np from flask import Flask, render_template_string, jsonify, request # Import the evolution engine from our main module from self_evolving_model import ( EvolutionEngine, DataHandler, Genome, CHECKPOINT_DIR, log ) # ═════════════════════════════════════════════════════════════════════ # Global Application State # ═════════════════════════════════════════════════════════════════════ app = Flask(__name__) state = { "running": False, "generation": 0, "total_generations": 0, "history": [], "best_genome": None, "population_summary": [], "best_fitness": 0.0, "improvement": 0.0, "status_message": "Idle. Click Start to begin evolution.", "dataset_loaded": False, "dataset_info": None, } engine: EvolutionEngine = None engine_thread: threading.Thread = None X_global: np.ndarray = None Y_global: np.ndarray = None # ═════════════════════════════════════════════════════════════════════ # Engine Callback # ═════════════════════════════════════════════════════════════════════ def on_generation_complete(stats, best_genome): """Called by EvolutionEngine after each generation.""" state["generation"] = stats["generation"] + 1 state["history"].append(stats) state["best_fitness"] = stats["best_fitness"] if best_genome: state["best_genome"] = best_genome.to_dict() state["population_summary"] = [ g.to_dict() for g in (engine.population if engine else []) ] if len(state["history"]) > 1 and state["history"][0]["best_fitness"] > 0: state["improvement"] = ( (state["history"][-1]["best_fitness"] - state["history"][0]["best_fitness"]) / state["history"][0]["best_fitness"] * 100 ) def run_evolution_with_data(pop_size, generations, mutation_rate, train_epochs, n_samples): """Background thread: loads dataset (if needed) then runs evolution.""" global engine, X_global, Y_global try: state["running"] = True state["total_generations"] = generations # Load dataset in background if not pre-loaded if X_global is None: state["status_message"] = "Loading FABLE.5 dataset..." try: X_global, Y_global = DataHandler.load_fable_dataset(n_samples=n_samples) state["dataset_loaded"] = True state["dataset_info"] = f"{X_global.shape[0]} samples, {X_global.shape[1]} features" except Exception as e: state["status_message"] = f"Dataset load failed: {e}. Using synthetic data." log(f"Dataset load failed: {e}. Falling back to synthetic.", "warn") X_global, Y_global = None, None state["status_message"] = f"Evolution in progress: 0/{generations} generations" engine = EvolutionEngine( pop_size=pop_size, generations=generations, mutation_rate=mutation_rate, train_epochs=train_epochs, ) engine.on_generation_complete = on_generation_complete engine.run(X=X_global, Y=Y_global, reset=True) state["status_message"] = ( f"Evolution complete! Best fitness: {state['best_fitness']:.2f} " f"(+{state['improvement']:.1f}%)" ) except Exception as e: state["status_message"] = f"Error: {str(e)}" log(f"Evolution error: {e}", "error") finally: state["running"] = False # ═════════════════════════════════════════════════════════════════════ # HTML Template (Single-page app with embedded CSS/JS) # ═════════════════════════════════════════════════════════════════════ HTML_TEMPLATE = r""" 🧬 Self-Evolving Neural Network

🧬 Self-Evolving Neural Network

Architecture evolves through mutation, crossover & natural selection — trained on FABLE.5 traces

⚡ Controls

Idle

📊 Statistics

Generation0 / 0
Best Fitness
Avg Fitness
Improvement
Population
Best Genome ID
DatasetNot loaded

📈 Fitness Over Generations

🏆 Best Genome

No evolution started yet

🧬 Population

Population will appear here during evolution...

📋 Evolution Log

Waiting for evolution to start...
""" # ═════════════════════════════════════════════════════════════════════ # Flask Routes # ═════════════════════════════════════════════════════════════════════ @app.route("/") def index(): return render_template_string(HTML_TEMPLATE) @app.route("/api/status") def api_status(): return jsonify(state) @app.route("/api/start", methods=["POST"]) def api_start(): global engine_thread, X_global, Y_global if state["running"]: return jsonify({"error": "Evolution already running"}), 400 params = request.get_json() or {} pop_size = params.get("pop_size", 6) generations = params.get("generations", 15) train_epochs = params.get("train_epochs", 10) mutation_rate = params.get("mutation_rate", 0.3) n_samples = params.get("n_samples", 5000) # Reset state state["history"] = [] state["generation"] = 0 state["best_genome"] = None state["population_summary"] = [] state["best_fitness"] = 0.0 state["improvement"] = 0.0 # Start evolution in background thread # Dataset loading happens inside the thread to avoid blocking Flask engine_thread = threading.Thread( target=run_evolution_with_data, args=(pop_size, generations, mutation_rate, train_epochs, n_samples), daemon=True, ) engine_thread.start() return jsonify({"status": "started", "generations": generations}) @app.route("/api/stop", methods=["POST"]) def api_stop(): global engine if engine and state["running"]: engine.stop_event.set() state["status_message"] = "Stopping... (finishing current genome)" return jsonify({"status": "stopping"}) return jsonify({"status": "not_running"}) # ═════════════════════════════════════════════════════════════════════ # Launch # ═════════════════════════════════════════════════════════════════════ def launch_gui( pop_size: int = 6, generations: int = 15, mutation_rate: float = 0.3, train_epochs: int = 10, X: np.ndarray = None, Y: np.ndarray = None, port: int = 5000, ): """Launch the Flask GUI server.""" global X_global, Y_global X_global = X Y_global = Y if X_global is not None: state["dataset_loaded"] = True state["dataset_info"] = f"{X_global.shape[0]} samples, {X_global.shape[1]} features" log(f"Dataset pre-loaded: {state['dataset_info']}", "success") banner = f""" ╔══════════════════════════════════════════════════════════════╗ ║ 🧬 Self-Evolving Neural Network — Web GUI ║ ║ Open http://localhost:{port} in your browser ║ ╚══════════════════════════════════════════════════════════════╝""" print(banner) app.run(host="0.0.0.0", port=port, debug=False, use_reloader=False) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("--port", type=int, default=5000) parser.add_argument("--n-samples", type=int, default=5000) args = parser.parse_args() X_data, Y_data = None, None try: X_data, Y_data = DataHandler.load_fable_dataset(n_samples=args.n_samples) except Exception as e: log(f"Could not load dataset: {e}. Will use synthetic data.", "warn") launch_gui(X=X_data, Y=Y_data, port=args.port)