Spaces:
Sleeping
Sleeping
| """Shared helpers for 6-String Optimizer HF Space (mystery shell + hf_space core).""" | |
| from __future__ import annotations | |
| import os | |
| import sys | |
| import tempfile | |
| from dataclasses import dataclass, field | |
| from pathlib import Path | |
| from typing import Any, Generator | |
| import numpy as np | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| import pandas as pd | |
| import torch | |
| _BUNDLE = Path(__file__).resolve().parent | |
| if str(_BUNDLE) not in sys.path: | |
| sys.path.insert(0, str(_BUNDLE)) | |
| if str(_BUNDLE / "src") not in sys.path: | |
| sys.path.insert(0, str(_BUNDLE / "src")) | |
| from optimizer import GeooptBurstOptimizer, HierarchicalGeooptBurstOptimizer | |
| from optimizer.models import SphereRosenbrockModel | |
| from optimizer.utils import stereographic_projection | |
| from optimizer.losses import rosenbrock_3d | |
| GITHUB_URL = "https://github.com/kinaar8340/6-string-optimizer" | |
| HF_SPACE_URL = "https://huggingface.co/spaces/kinaar111/6-string-optimizer" | |
| QVPIC_URL = "https://huggingface.co/spaces/kinaar111/qvpic" | |
| PHYSICS_AUDIO_URL = f"{GITHUB_URL}/tree/main/physics_audio" | |
| BOOT_QUOTE_STRING = "HARMONY OF THE SPHERES · SIX-STRING BURST · S³" | |
| STARTUP_STRING = "HARMONY OF THE SPHERES · SIX STRING BURST · S THREE" | |
| SAMPLE_GUITAR_FILENAME = "demo_guitar_g3.wav" | |
| SAMPLE_GUITAR_NOTE = "G3 open string (196 Hz) · 2.5s" | |
| STRING_NAMES = ["High E", "B", "G", "D", "A", "Low E"] | |
| STRING_COLORS = ["#FF6B6B", "#FF9F43", "#FFD93D", "#6BCB77", "#4D96FF", "#9B59B6"] | |
| WINDOWS = [400, 580, 780, 1100, 1600, 2400] | |
| BURST_BOOSTS = [1.02, 1.04, 1.06, 1.08, 1.10, 1.15] | |
| THETA_BOOSTS = [1.01, 1.02, 1.03, 1.04, 1.05, 1.06] | |
| WALLPAPER_URL = f"{GITHUB_URL}/raw/main/physics_audio/overview_composite.png" | |
| SIMULATION_BANNER_MD = """ | |
| > **Research showcase** on Hugging Face **cpu-basic** (free tier). Default **Quick Demo** mode (~20s optimize). | |
| > For full 8k–12k step runs, clone [GitHub](https://github.com/kinaar8340/6-string-optimizer) locally. | |
| """ | |
| KNOWN_LIMITATIONS_BODY = """ | |
| - **CPU only** — optimization + Plotly streaming can feel slow; use **Quick Demo** first. | |
| - **WebGL required for 3D** — embedded views, privacy browsers, or older GPUs may block WebGL; use **2D (WebGL-safe)** trajectory view. | |
| - **Cold starts** — first load after sleep may take 30–60s to build. | |
| - **ETA is approximate** — actual time varies with server load and concurrent users. | |
| - **Compare vs RiemannianAdam** doubles wall time; capped at 3000 steps on HF. | |
| - **Research mode (12k steps)** — best run [locally](https://github.com/kinaar8340/6-string-optimizer) or upgrade Space hardware. | |
| - **Best experience:** desktop Chrome/Firefox · let Quick Demo finish before switching tabs. | |
| """.strip() | |
| WEBGL_PROBE_JS = """() => { | |
| let ok = false; | |
| try { | |
| if (localStorage.getItem('6string-webgl-failed') === '1') ok = false; | |
| else { | |
| const c = document.createElement('canvas'); | |
| ok = !!(window.WebGLRenderingContext && (c.getContext('webgl') || c.getContext('experimental-webgl'))); | |
| } | |
| } catch (e) { ok = false; } | |
| return ok ? "1" : "0"; | |
| }""" | |
| TRAJECTORY_VIEW_CHOICES: list[tuple[str, str]] = [ | |
| ("Auto (3D if WebGL available)", "auto"), | |
| ("3D interactive", "3d"), | |
| ("2D projection (WebGL-safe)", "2d"), | |
| ] | |
| KNOWN_LIMITATIONS_MD = f"### Known limitations (HF free tier)\n{KNOWN_LIMITATIONS_BODY}" | |
| LOCAL_RUN_MD = """ | |
| **Run locally (recommended for 12k+ steps):** | |
| ```bash | |
| git clone https://github.com/kinaar8340/6-string-optimizer.git | |
| cd 6-string-optimizer/hf_staging && pip install -r requirements.txt && python app.py | |
| ``` | |
| [View on GitHub](https://github.com/kinaar8340/6-string-optimizer) · [Upgrade Space hardware](https://huggingface.co/spaces/kinaar111/6-string-optimizer/settings) | |
| """ | |
| INTRO_WALKTHROUGH_HTML = """ | |
| <div class="intro-walkthrough" aria-label="60-second demo flow animation"> | |
| <div class="intro-step intro-step-1"><span>①</span> Open Space · Quick Demo selected</div> | |
| <div class="intro-step intro-step-2"><span>②</span> ▶ DEMO GUITAR — hear G3 partials</div> | |
| <div class="intro-step intro-step-3"><span>③</span> ▶ RUN OPTIMIZATION — loss drops · VU bursts</div> | |
| <div class="intro-step intro-step-4"><span>④</span> Theory tab — S³ + burst mechanism</div> | |
| <div class="intro-progress" aria-hidden="true"></div> | |
| </div> | |
| """ | |
| WHAT_IS_THIS_MD = """ | |
| ### What is this project? | |
| An **artistic research demo** for [GeooptBurstOptimizer](https://github.com/kinaar8340/6-string-optimizer) — | |
| a novel optimizer on the **3-sphere (S³)** that uses **punctuated-equilibrium bursts** (slow tension buildup → | |
| productive escape) inspired by Pythagorean harmonics, musical consonance, and natural avalanche dynamics. | |
| **You are not** tuning a real guitar here. **You are** watching how six virtual "strings" monitor stagnation | |
| and fire macro-bursts to escape Rosenbrock plateaus where Riemannian Adam often stalls. | |
| | Try this first | Action | | |
| |----------------|--------| | |
| | Hear it | **SPECTRUM** → ▶ DEMO GUITAR (G3) | | |
| | See it | **OPTIMIZE** → ▶ RUN OPTIMIZATION | | |
| | Understand it | Open **Theory** tab (nav bar) | | |
| | Full tour | Keypad **11** — skippable anytime | | |
| [GitHub repo](https://github.com/kinaar8340/6-string-optimizer) · [Physics audio](https://github.com/kinaar8340/6-string-optimizer/tree/main/physics_audio) | |
| """ | |
| ONBOARDING_MD = """ | |
| ### 60-second guided tour *(skippable — dismiss welcome card or press CLEAR)* | |
| **1 — The metaphor** | |
| Six virtual guitar strings wrap the base optimizer. Each watches a **stagnation window** | |
| (400 → 2400 steps, Pythagorean ratios). Tension rises → **burst** fires → plateau escape. | |
| **2 — Optimize (~30s on HF)** | |
| **OPTIMIZE** tab → choose a preset (Standard / Fisher-Rao / Aggressive) → ▶ RUN OPTIMIZATION. | |
| Watch S³→R³ trajectory, loss envelope, per-string VU meters (High E → Low E), twist accumulation. | |
| **3 — Listen** | |
| **SPECTRUM** → ▶ DEMO GUITAR (G3) — bundled 2.5s open G string. Waveform · STFT · partial tracks · string energy. | |
| **4 — Physics connection** | |
| **PHYSICS** tab — Smith chart (Γ reflection / mismatch analogy) + reconstruction pyramid overview. | |
| Full Stiefel-manifold training runs locally via `physics_audio/run_real_audio.py`. | |
| **5 — Keypad** | |
| PROG 1–6 = string layers · 7 = optimize · 8 = demo guitar · 11 = replay this tour. | |
| Press **Theory** in the nav bar for equations and burst mechanism diagrams. | |
| """ | |
| THEORY_MD = """ | |
| ## Theory — How it works | |
| ### S³ optimization in plain language | |
| Parameters live on the **unit 3-sphere S³** (quaternion q, |q| = 1). We optimize a loss on **stereographic | |
| projection** u = π(q) ∈ ℝ³ — a compact view of ℝ³ plus a point at infinity. The demo target is Rosenbrock | |
| in 3D; the global minimum sits at u = (1, 1, 1). | |
| ### Burst mechanism — tension → escape | |
| Standard Riemannian SGD/Adam can **stall on plateaus**. GeooptBurstOptimizer tracks stagnation: | |
| when loss improvement drops below threshold over a sliding window, it applies a **macro-burst** — | |
| a controlled geodesic jump with damped momentum — to escape the plateau. | |
| ``` | |
| loss │ ╭── plateau ──╮ | |
| │ ╱ ╲___ burst lands lower | |
| │───╱ tension ↑ ╲ | |
| └──────────────────────── step | |
| └── window fills → BURST | |
| ``` | |
| Six **hierarchical layers** (virtual strings) use staggered windows and boost factors — like | |
| coupled oscillators at Pythagorean ratios (400 : 580 : 780 : …). | |
| ### Key equations | |
| - **Stereographic map:** u = (q₁/(1+q₀), q₂/(1+q₀), q₃/(1+q₀)) | |
| - **Stagnation trigger:** |Δloss| < ε over window Wᵢ → burst on layer i | |
| - **Burst factor:** scales geodesic step magnitude (cap = burst_factor_max) | |
| - **Twist accumulation:** tracks cumulative rotation from burst jumps on S³ | |
| - **Fisher-Rao modulation (optional):** uses information geometry to shape burst severity | |
| ### Why guitar / harmonics? | |
| - **Harmony of the Spheres** — optimization layers echo consonant frequency ratios. | |
| - **VU meters** — each bar is a real string name (High E … Low E), showing stagnation *tension*. | |
| - **Spectrum tab** — real audio partials foreshadow the **physics-audio** inverse problem: | |
| recover damped, inharmonic modal parameters on the **Stiefel manifold** from a recording. | |
| ### Smith chart (PHYSICS tab) | |
| In the physics-audio trainer, **Γ (reflection coefficient)** measures how far candidate modal | |
| parameters are from matching the observed spectrum. The Smith chart plots Γ in the complex plane — | |
| like impedance matching in RF engineering, but here "matching" means **reconstructing string vibration modes**. | |
| ### vs. Riemannian Adam | |
| | | Burst optimizer | Riemannian Adam | | |
| |--|-----------------|-----------------| | |
| | Plateau escape | Macro-bursts on stagnation | Smooth small steps only | | |
| | Hierarchy | Six staggered windows | Single optimizer | | |
| | Geometry | S³ + optional Fisher-Rao | Manifold-aware steps | | |
| Full benchmarks and derivations: [GitHub](https://github.com/kinaar8340/6-string-optimizer). | |
| """ | |
| KATEX_CDN = """ | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" crossorigin="anonymous"> | |
| <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js" crossorigin="anonymous"></script> | |
| <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js" crossorigin="anonymous" | |
| onload="document.querySelectorAll('.katex-block').forEach(function(el){renderMathInElement(el,{delimiters:[{left:'$$',right:'$$',display:true},{left:'\\\\(',right:'\\\\)',display:false},{left:'\\\\[',right:'\\\\]',display:true}],throwOnError:false});});"> | |
| </script> | |
| """ | |
| THEORY_KATEX_HTML = """ | |
| <div class="katex-block" style="margin-top:1rem;padding:0.75rem;background:rgba(10,10,10,0.5);border-radius:8px;"> | |
| <p><strong>Key equations (KaTeX)</strong></p> | |
| <p>Stereographic projection from unit quaternion $q=(q_0,q_1,q_2,q_3)$:</p> | |
| $$u_i = \\frac{q_i}{1+q_0}, \\quad i=1,2,3$$ | |
| <p>Stagnation burst trigger when loss improvement falls below $\\varepsilon$ over window $W_i$:</p> | |
| $$|\\mathcal{L}_t - \\mathcal{L}_{t-W_i}| < \\varepsilon \\;\\Rightarrow\\; \\text{BURST}_i$$ | |
| <p>Ideal string tension (frequency–length relation):</p> | |
| $$f = \\frac{1}{2L}\\sqrt{\\frac{T}{\\mu}} \\quad\\Rightarrow\\quad T = 4\\mu L^2 f^2$$ | |
| <p>Inharmonic partial (physics-audio modal model):</p> | |
| $$f_k = k\\,f_0\\sqrt{1 + B k^2}$$ | |
| <p>Smith chart reflection coefficient (modal mismatch):</p> | |
| $$\\Gamma = \\frac{Z - Z_0}{Z + Z_0}$$ | |
| </div> | |
| """ | |
| CLAIMS_MD = """ | |
| | What you see | What it demonstrates | | |
| |--------------|----------------------| | |
| | **Six-string VU meters** | High E, B, G, D, A, Low E — real-time stagnation tension per layer | | |
| | **S³ trajectory** | Stereographic path toward Rosenbrock minimum [1,1,1] | | |
| | **Burst presets** | Standard · Fisher-Rao enhanced · Aggressive plateau escape | | |
| | **Demo guitar G3** | Waveform, STFT, partial tracks, per-string spectral energy | | |
| | **Smith chart** | Γ-mismatch visualization from physics-audio training | | |
| | **Terminal UI** | Research-console aesthetic — keypad, phosphor scan, streaming readouts | | |
| *Not included in-browser:* full modal synthesis, multi-string upload batch, tension/gauge calculator. | |
| Those live in the [physics_audio](https://github.com/kinaar8340/6-string-optimizer/tree/main/physics_audio) repo. | |
| """ | |
| RUN_MODES: dict[str, dict[str, Any]] = { | |
| "quick": { | |
| "label": "Quick Demo", | |
| "total_steps": 2500, | |
| "steps_per_update": 125, | |
| "learning_rate": 0.03, | |
| "burst_factor_max": 8.0, | |
| "use_fisher_modulation": False, | |
| "compare_adam": False, | |
| "audio_duration": 1.5, | |
| "eta_hint": "~15–25s optimize on HF cpu-basic", | |
| "description": "Recommended first visit — fewer Plotly updates, faster finish.", | |
| }, | |
| "standard": { | |
| "label": "Standard HF", | |
| "total_steps": 4000, | |
| "steps_per_update": 100, | |
| "learning_rate": 0.03, | |
| "burst_factor_max": 8.0, | |
| "use_fisher_modulation": False, | |
| "compare_adam": False, | |
| "audio_duration": 2.0, | |
| "eta_hint": "~35–50s optimize on HF cpu-basic", | |
| "description": "Balanced public demo — still tuned for free-tier CPU.", | |
| }, | |
| "research": { | |
| "label": "Full Research Run", | |
| "total_steps": 12000, | |
| "steps_per_update": 80, | |
| "learning_rate": 0.03, | |
| "burst_factor_max": 8.0, | |
| "use_fisher_modulation": False, | |
| "compare_adam": False, | |
| "audio_duration": 3.0, | |
| "eta_hint": "~2–4 min on HF — may timeout; prefer local clone", | |
| "description": "Long run for plateau study — heavy on free tier.", | |
| }, | |
| } | |
| OPTIMIZATION_PRESETS: dict[str, dict[str, Any]] = { | |
| "standard": { | |
| "label": "Standard Burst", | |
| "total_steps": 4000, | |
| "steps_per_update": 100, | |
| "learning_rate": 0.03, | |
| "burst_factor_max": 8.0, | |
| "use_fisher_modulation": False, | |
| "description": "Balanced — matches Standard HF run mode.", | |
| }, | |
| "fisher_rao": { | |
| "label": "Fisher-Rao Enhanced", | |
| "total_steps": 4000, | |
| "steps_per_update": 100, | |
| "learning_rate": 0.025, | |
| "burst_factor_max": 8.0, | |
| "use_fisher_modulation": True, | |
| "description": "Information-geometric burst shaping — smoother escapes.", | |
| }, | |
| "aggressive": { | |
| "label": "Aggressive Escape", | |
| "total_steps": 6000, | |
| "steps_per_update": 80, | |
| "learning_rate": 0.05, | |
| "burst_factor_max": 12.0, | |
| "use_fisher_modulation": False, | |
| "description": "Larger bursts — use Research mode locally for 12k steps.", | |
| }, | |
| } | |
| SLIDER_TOOLTIPS = { | |
| "total_steps": "Optimizer steps. Quick Demo uses 2500 · Standard HF 4000 · Research 12000.", | |
| "steps_per_update": "Steps between UI refreshes — higher = fewer Plotly redraws (faster on HF).", | |
| "learning_rate": "Base Riemannian step size before burst scaling.", | |
| "burst_factor_max": "Maximum burst jump magnitude — higher = more aggressive plateau escape.", | |
| "use_fisher": "Enable Fisher-Rao information geometry to modulate burst severity.", | |
| "audio_duration": "Seconds of audio to analyze (trimmed from start).", | |
| } | |
| FIGURES_INTRO_MD = ( | |
| "Physics-audio reference figures — bundled overview composite and HF-safe Smith chart preview. " | |
| "Regenerate full pyramid plots locally with `run_real_audio.py`." | |
| ) | |
| FIGURE_URLS = ( | |
| str(_BUNDLE / "assets" / "overview_composite.png"), | |
| "", # filled at runtime by render_smith_chart_preview | |
| f"{GITHUB_URL}/raw/main/physics_audio/overview_composite.png", | |
| f"{GITHUB_URL}/raw/main/physics_audio/training_evaluation/viz.py", | |
| ) | |
| PROG_ACTIONS: dict[int, tuple[str, str]] = { | |
| 1: ("high_e", "High E string — window 400 · burst 1.02×"), | |
| 2: ("b_string", "B string — window 580 · burst 1.04×"), | |
| 3: ("g_string", "G string — window 780 · burst 1.06×"), | |
| 4: ("d_string", "D string — window 1100 · burst 1.08×"), | |
| 5: ("a_string", "A string — window 1600 · burst 1.10×"), | |
| 6: ("low_e", "Low E string — window 2400 · burst 1.15×"), | |
| 7: ("run_optimize", "Run Rosenbrock S³ optimization"), | |
| 8: ("demo_guitar", "Analyze bundled demo guitar (G3)"), | |
| 9: ("guided_tour", "60-second guided onboarding tour"), | |
| 10: ("string_table", "Six-string hierarchy reference"), | |
| 11: ("settings", "Open tuning & parameter dials"), | |
| 12: ("about", "About / credits / build stamp"), | |
| } | |
| TERM_KEY_ACTIONS: dict[int, tuple[str, str]] = { | |
| 1: ("home", "Return to selection menu"), | |
| 2: ("status", "Live optimizer & environment status"), | |
| 3: ("scope", "What this Space runs vs local pipeline"), | |
| 4: ("directory", "Repo layout & paths"), | |
| 5: ("results", "Six-string hierarchy snapshot"), | |
| 6: ("build", "Build stamp & deploy info"), | |
| 7: ("help", "D-pad / keypad navigation"), | |
| 8: ("scan", "Phosphor signal scan — CSS only, any key exits"), | |
| 9: ("strings", "Six-string layer catalog"), | |
| 10: ("physics", "Physics-audio pipeline overview"), | |
| 11: ("tour", "Guided onboarding tour"), | |
| 12: ("figures", "Smith chart + pyramid figure index"), | |
| } | |
| def is_hf_space() -> bool: | |
| return bool(os.environ.get("SPACE_ID")) | |
| def get_build_label() -> str: | |
| try: | |
| from build_info import BUILD_COMMIT, BUILD_UPDATED_UTC # noqa: WPS433 | |
| return f"Build {BUILD_UPDATED_UTC} UTC · `{BUILD_COMMIT}`" | |
| except ImportError: | |
| return "Local dev build" | |
| def get_sample_guitar_path() -> Path: | |
| candidates = ( | |
| _BUNDLE / "assets" / SAMPLE_GUITAR_FILENAME, | |
| Path(__file__).resolve().parent / "assets" / SAMPLE_GUITAR_FILENAME, | |
| ) | |
| for path in candidates: | |
| if path.is_file(): | |
| return path | |
| raise FileNotFoundError( | |
| f"Demo guitar not found — expected assets/{SAMPLE_GUITAR_FILENAME} in Space bundle." | |
| ) | |
| def get_overview_composite_path() -> Path: | |
| path = _BUNDLE / "assets" / "overview_composite.png" | |
| if path.is_file(): | |
| return path | |
| return Path(FIGURE_URLS[2]) | |
| def default_run_params() -> dict[str, Any]: | |
| on_hf = is_hf_space() | |
| if on_hf: | |
| return dict(RUN_MODES["quick"]) | |
| research = dict(RUN_MODES["research"]) | |
| research["total_steps"] = 20000 | |
| research["audio_duration"] = 4.0 | |
| return research | |
| def get_run_mode_params(mode: str) -> dict[str, Any]: | |
| """Return slider values for quick / standard / research run mode.""" | |
| base = default_run_params() | |
| spec = RUN_MODES.get(mode, RUN_MODES["quick"]) | |
| base.update({k: v for k, v in spec.items() if k not in ("label", "description", "eta_hint")}) | |
| return base | |
| def estimate_runtime_sec(total_steps: int, steps_per_update: int, *, compare: bool = False) -> float: | |
| """Heuristic seconds for HF cpu-basic (empirical ~0.008s per step + plot overhead).""" | |
| updates = max(1, total_steps // max(1, steps_per_update)) | |
| base = total_steps * 0.009 + updates * 0.35 | |
| return base * (1.9 if compare else 1.0) | |
| def format_eta_status(step: int, total: int, elapsed_s: float, *, compare: bool = False) -> str: | |
| approx = " (approx., server load varies)" | |
| if step <= 0 or elapsed_s <= 0: | |
| est = estimate_runtime_sec(total, max(1, total // max(1, 20)), compare=compare) | |
| return f"Estimated runtime: ~{est:.0f}s on HF cpu-basic{approx}" | |
| rate = step / elapsed_s | |
| remaining = max(0, total - step) | |
| eta = remaining / rate if rate > 0 else 0 | |
| return f"Elapsed {elapsed_s:.0f}s · ETA ~{eta:.0f}s{approx} · {100 * step / total:.0f}%" | |
| class PlotStreamConfig: | |
| trajectory_every: int = 1 | |
| trajectory_max_points: int = 300 | |
| loss_max_points: int = 800 | |
| live_3d: bool = True | |
| def for_run_mode(cls, mode: str) -> "PlotStreamConfig": | |
| if mode == "quick": | |
| return cls(trajectory_every=3, trajectory_max_points=100, loss_max_points=350, live_3d=False) | |
| if mode == "standard": | |
| return cls(trajectory_every=2, trajectory_max_points=180, loss_max_points=500, live_3d=True) | |
| return cls(trajectory_every=1, trajectory_max_points=400, loss_max_points=1500, live_3d=True) | |
| def resolve_trajectory_mode(view_pref: str, webgl_ok: bool) -> str: | |
| """Map UI preference + client WebGL probe to '3d' or '2d'.""" | |
| pref = (view_pref or "auto").strip().lower() | |
| if pref == "2d": | |
| return "2d" | |
| if pref == "3d": | |
| return "3d" if webgl_ok else "2d" | |
| return "2d" if not webgl_ok else "3d" | |
| def default_trajectory_view() -> str: | |
| """HF free tier defaults to 2D for maximum browser compatibility.""" | |
| return "2d" if is_hf_space() else "auto" | |
| def webgl_status_markdown(webgl_ok: bool, view_pref: str) -> str: | |
| if is_hf_space() and view_pref == "2d": | |
| lead = ( | |
| "**HF free tier:** **2D (WebGL-safe)** trajectory is the default for maximum compatibility. " | |
| "Switch to **3D interactive** if your browser supports WebGL." | |
| ) | |
| if not webgl_ok: | |
| return f"⚠ {lead} (WebGL probe failed — staying in 2D.)" | |
| return lead | |
| if not webgl_ok: | |
| return ( | |
| "⚠ **WebGL unavailable** — trajectory uses **2D stereographic projections**. " | |
| "Select **2D (WebGL-safe)** below, or try desktop Chrome/Firefox." | |
| ) | |
| if view_pref == "2d": | |
| return "Showing **2D projections** (WebGL-safe mode)." | |
| if view_pref == "3d": | |
| return "✓ WebGL detected — **interactive 3D** trajectory." | |
| return "✓ WebGL detected — **Auto** will use 3D trajectory." | |
| def _subsample_df(df: pd.DataFrame, max_points: int) -> pd.DataFrame: | |
| if len(df) <= max_points: | |
| return df | |
| idx = np.linspace(0, len(df) - 1, max_points, dtype=int) | |
| return df.iloc[idx].reset_index(drop=True) | |
| def friendly_error(context: str, exc: Exception) -> str: | |
| """User-facing error text with actionable hints.""" | |
| msg = str(exc).strip() or type(exc).__name__ | |
| hints: dict[str, str] = { | |
| "optimize": ( | |
| "Optimization hit an error. Try **Quick Demo** mode, fewer steps, or press ■ CANCEL and retry. " | |
| "If this persists, run locally from GitHub." | |
| ), | |
| "spectrum": ( | |
| "Audio analysis failed. Try **▶ DEMO GUITAR (G3)** (bundled WAV) instead of upload, " | |
| "or shorten **Audio duration** to 1.5s." | |
| ), | |
| "synthesis": ( | |
| "Modal synthesis failed. Analyze demo guitar first, or reduce duration / inharmonicity B." | |
| ), | |
| "physics": "Physics preview failed. Smith chart will regenerate on retry — overview image is bundled.", | |
| "asset": ( | |
| f"Missing bundled asset ({msg}). Re-deploy the Space or use the GitHub repo copy." | |
| ), | |
| "webgl": ( | |
| "3D trajectory needs WebGL. Switch **Trajectory view** to **2D (WebGL-safe)** " | |
| "in the OPTIMIZE tab, or open in desktop Chrome/Firefox." | |
| ), | |
| } | |
| lead = hints.get(context, "Something went wrong.") | |
| return f"⚠ {lead}\n\nDetails: {msg[:300]}" | |
| def _configure_cpu_threads() -> None: | |
| threads = 4 if is_hf_space() else min(8, (os.cpu_count() or 8) // 2) | |
| torch.set_num_threads(threads) | |
| torch.set_num_interop_threads(2) | |
| class OptimizerMonitor: | |
| layer_names: list[str] | |
| history: dict[str, list] = field(default_factory=dict) | |
| layer_tension: dict[str, float] = field(default_factory=dict) | |
| burst_events: list[tuple[int, str, str]] = field(default_factory=list) | |
| def __post_init__(self) -> None: | |
| self.history = { | |
| "step": [], | |
| "loss": [], | |
| "u_x": [], | |
| "u_y": [], | |
| "u_z": [], | |
| "twist": [], | |
| } | |
| self.layer_tension = {name: 0.0 for name in self.layer_names} | |
| def record_step(self, step: int, loss_val: float, u: np.ndarray, twist: float = 0.0) -> None: | |
| self.history["step"].append(step) | |
| self.history["loss"].append(loss_val) | |
| self.history["u_x"].append(float(u[0])) | |
| self.history["u_y"].append(float(u[1])) | |
| self.history["u_z"].append(float(u[2])) | |
| self.history["twist"].append(twist) | |
| def record_burst(self, step: int, layer_name: str, severity: float = 0.0) -> None: | |
| self.burst_events.append((step, layer_name, "BURST")) | |
| self.layer_tension[layer_name] = severity | |
| for name in self.layer_names: | |
| if name != layer_name: | |
| self.layer_tension[name] *= 0.85 | |
| def _build_hierarchical_optimizer( | |
| model: SphereRosenbrockModel, | |
| *, | |
| lr: float, | |
| burst_factor_max: float, | |
| use_fisher_modulation: bool, | |
| ) -> HierarchicalGeooptBurstOptimizer: | |
| base_opt = GeooptBurstOptimizer( | |
| model.parameters(), | |
| lr=lr, | |
| burst_factor_max=burst_factor_max, | |
| damping_min=0.98, | |
| damping=0.995, | |
| max_theta=torch.pi / 20, | |
| verbose=False, | |
| use_fisher_modulation=use_fisher_modulation, | |
| ) | |
| opt: Any = base_opt | |
| for name, win, bb, tb in zip(STRING_NAMES, WINDOWS, BURST_BOOSTS, THETA_BOOSTS): | |
| opt = HierarchicalGeooptBurstOptimizer( | |
| opt, | |
| stagnation_window=win, | |
| stagnation_thresh=5e-5, | |
| burst_boost=bb, | |
| theta_boost=tb, | |
| name=name, | |
| verbose=False, | |
| ) | |
| return opt | |
| def _make_trajectory_2d_figure( | |
| df: pd.DataFrame, | |
| *, | |
| max_points: int | None = None, | |
| lite: bool = False, | |
| webgl_fallback: bool = False, | |
| ) -> go.Figure: | |
| """Canvas/SVG 2D projections — no WebGL required.""" | |
| if df.empty or not {"u_x", "u_y", "u_z"}.issubset(df.columns): | |
| return _trajectory_placeholder("Waiting for trajectory data…") | |
| plot_df = _subsample_df(df, max_points) if max_points else df | |
| marker_size = 3 if lite else 4 | |
| fallback_note = " · 2D fallback (WebGL unavailable)" if webgl_fallback else "" | |
| fig = make_subplots( | |
| rows=1, | |
| cols=2, | |
| subplot_titles=("u_x vs u_y", "u_y vs u_z"), | |
| horizontal_spacing=0.1, | |
| ) | |
| marker_kw = dict( | |
| size=marker_size, | |
| color=plot_df["loss"], | |
| colorscale="Viridis", | |
| showscale=not lite, | |
| colorbar=dict(title="loss", len=0.55, y=0.5) if not lite else None, | |
| ) | |
| line_kw = dict(width=1 if lite else 2, color="rgba(255,170,0,0.65)") | |
| for col, xcol, ycol in ((1, "u_x", "u_y"), (2, "u_y", "u_z")): | |
| fig.add_trace( | |
| go.Scatter( | |
| x=plot_df[xcol], | |
| y=plot_df[ycol], | |
| mode="lines+markers", | |
| marker=marker_kw, | |
| line=line_kw, | |
| name=f"{xcol}/{ycol}", | |
| showlegend=False, | |
| ), | |
| row=1, | |
| col=col, | |
| ) | |
| fig.add_trace( | |
| go.Scatter( | |
| x=[1], | |
| y=[1], | |
| mode="markers", | |
| marker=dict(size=9, color="#FF4444", symbol="diamond"), | |
| name="min [1,1,1]", | |
| showlegend=False, | |
| ), | |
| row=1, | |
| col=col, | |
| ) | |
| fig.update_layout( | |
| title=f"S³ → R³ Stereographic — 2D projections{fallback_note}", | |
| paper_bgcolor="#0a0a0a", | |
| font=dict(color="#FFB000"), | |
| height=380, | |
| margin=dict(l=40, r=20, t=50, b=30), | |
| ) | |
| for col, xlab, ylab in ((1, "u_x", "u_y"), (2, "u_y", "u_z")): | |
| fig.update_xaxes(title_text=xlab, gridcolor="#333", row=1, col=col) | |
| fig.update_yaxes(title_text=ylab, gridcolor="#333", row=1, col=col) | |
| return fig | |
| def _make_trajectory_figure( | |
| df: pd.DataFrame, | |
| *, | |
| max_points: int | None = None, | |
| lite: bool = False, | |
| ) -> go.Figure: | |
| plot_df = _subsample_df(df, max_points) if max_points else df | |
| marker_size = 1 if lite else 2 | |
| fig = go.Figure() | |
| fig.add_trace( | |
| go.Scatter3d( | |
| x=plot_df["u_x"], | |
| y=plot_df["u_y"], | |
| z=plot_df["u_z"], | |
| mode="lines+markers", | |
| marker=dict( | |
| size=marker_size, | |
| color=plot_df["loss"], | |
| colorscale="Viridis", | |
| showscale=not lite, | |
| ), | |
| line=dict(width=1 if lite else 2, color="rgba(255,170,0,0.6)"), | |
| name="Trajectory", | |
| ) | |
| ) | |
| fig.add_trace( | |
| go.Scatter3d( | |
| x=[1], y=[1], z=[1], | |
| mode="markers", | |
| marker=dict(size=10, color="#FF4444", symbol="diamond"), | |
| name="Global min [1,1,1]", | |
| ) | |
| ) | |
| fig.update_layout( | |
| title="S³ → R³ Stereographic Trajectory (drag to rotate)", | |
| scene=dict( | |
| xaxis_title="u_x", | |
| yaxis_title="u_y", | |
| zaxis_title="u_z", | |
| aspectmode="cube", | |
| bgcolor="rgba(10,10,10,0.9)", | |
| camera=dict(eye=dict(x=1.6, y=1.4, z=1.1)), | |
| ), | |
| paper_bgcolor="#0a0a0a", | |
| font=dict(color="#FFB000"), | |
| margin=dict(l=0, r=0, t=40, b=0), | |
| height=380, | |
| ) | |
| return fig | |
| def make_trajectory_figure( | |
| df: pd.DataFrame, | |
| *, | |
| mode: str = "3d", | |
| max_points: int | None = None, | |
| lite: bool = False, | |
| webgl_fallback: bool = False, | |
| ) -> go.Figure: | |
| """Dispatch 3D WebGL or 2D canvas trajectory.""" | |
| if mode == "2d": | |
| return _make_trajectory_2d_figure( | |
| df, | |
| max_points=max_points, | |
| lite=lite, | |
| webgl_fallback=webgl_fallback, | |
| ) | |
| if df.empty: | |
| return _trajectory_placeholder("Waiting for trajectory data…") | |
| return _make_trajectory_figure(df, max_points=max_points, lite=lite) | |
| def _make_loss_figure( | |
| df: pd.DataFrame, | |
| *, | |
| max_points: int | None = None, | |
| monitor: OptimizerMonitor | None = None, | |
| step: int | None = None, | |
| best_loss: float | None = None, | |
| ) -> go.Figure: | |
| if df.empty or "loss" not in df.columns: | |
| fig = go.Figure() | |
| fig.add_annotation( | |
| text="Loss curve will appear after the first optimizer update", | |
| xref="paper", yref="paper", x=0.5, y=0.5, | |
| showarrow=False, font=dict(size=13, color="#FFB000"), | |
| ) | |
| fig.update_layout( | |
| title="Loss Envelope (log scale)", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=280, | |
| xaxis=dict(visible=False), | |
| yaxis=dict(visible=False), | |
| ) | |
| return fig | |
| plot_df = _subsample_df(df, max_points) if max_points else df | |
| current_loss = float(plot_df["loss"].iloc[-1]) | |
| best = best_loss if best_loss is not None else float(plot_df["loss"].min()) | |
| cur_step = step if step is not None else int(plot_df["step"].iloc[-1]) | |
| burst_n = len(monitor.burst_events) if monitor else 0 | |
| title = ( | |
| f"Loss Envelope — step {cur_step:,} · current {current_loss:.4g} · " | |
| f"best {best:.4g} · {burst_n} burst{'s' if burst_n != 1 else ''}" | |
| ) | |
| fig = px.line(plot_df, x="step", y="loss", title=title, log_y=True) | |
| fig.update_traces(line=dict(color="#FFB000", width=2)) | |
| if monitor: | |
| color_cycle = STRING_COLORS | |
| for i, (burst_step, layer, _) in enumerate(monitor.burst_events[-12:]): | |
| color = color_cycle[i % len(color_cycle)] | |
| fig.add_vline( | |
| x=burst_step, | |
| line_width=1, | |
| line_dash="dot", | |
| line_color=color, | |
| ) | |
| if i == len(monitor.burst_events[-12:]) - 1: | |
| fig.add_annotation( | |
| x=burst_step, | |
| y=current_loss, | |
| text=f"{layer} burst", | |
| showarrow=False, | |
| font=dict(size=9, color=color), | |
| yshift=12, | |
| ) | |
| losses = plot_df["loss"].astype(float) | |
| y_min = max(losses.min() * 0.5, 1e-12) | |
| y_max = max(losses.max() * 2.0, y_min * 10) | |
| fig.update_layout( | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| xaxis=dict(gridcolor="#333", title="Step"), | |
| yaxis=dict(gridcolor="#333", title="Loss", type="log", range=[np.log10(y_min), np.log10(y_max)]), | |
| height=280, | |
| margin=dict(l=40, r=20, t=50, b=30), | |
| ) | |
| return fig | |
| def _make_string_vu_figure(monitor: OptimizerMonitor) -> go.Figure: | |
| tensions = [monitor.layer_tension[name] for name in monitor.layer_names] | |
| fig = go.Figure() | |
| for name, tension, color in zip(monitor.layer_names, tensions, STRING_COLORS): | |
| fig.add_trace( | |
| go.Bar( | |
| y=[name], | |
| x=[tension], | |
| orientation="h", | |
| marker=dict(color=color, line=dict(color="#222", width=1)), | |
| showlegend=False, | |
| ) | |
| ) | |
| max_t = max(tensions) if tensions else 1.0 | |
| fig.update_layout( | |
| title="Six-String VU — High E · B · G · D · A · Low E (stagnation tension)", | |
| barmode="overlay", | |
| xaxis=dict(range=[0, max(max_t * 1.3, 0.001)], title="Tension → burst trigger"), | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000", family="monospace"), | |
| height=320, | |
| margin=dict(l=60, r=20, t=40, b=30), | |
| ) | |
| return fig | |
| def _make_twist_figure(df: pd.DataFrame, *, max_points: int | None = None) -> go.Figure: | |
| plot_df = _subsample_df(df, max_points) if max_points else df | |
| fig = px.area(plot_df, x="step", y="twist", title="Twist Accumulation") | |
| fig.update_traces(fillcolor="rgba(255,170,0,0.3)", line=dict(color="#FF8800")) | |
| fig.update_layout( | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=220, | |
| margin=dict(l=40, r=20, t=40, b=30), | |
| ) | |
| return fig | |
| def _trajectory_placeholder(message: str = "3D trajectory paused in Quick mode — see loss & VU") -> go.Figure: | |
| fig = go.Figure() | |
| fig.add_annotation( | |
| text=message, | |
| xref="paper", yref="paper", x=0.5, y=0.5, | |
| showarrow=False, font=dict(size=14, color="#FFB000"), | |
| ) | |
| fig.update_layout( | |
| title="S³ Trajectory (lite stream)", | |
| paper_bgcolor="#0a0a0a", | |
| font=dict(color="#FFB000"), | |
| height=280, | |
| xaxis=dict(visible=False), | |
| yaxis=dict(visible=False), | |
| ) | |
| return fig | |
| def _build_live_figures( | |
| df: pd.DataFrame, | |
| monitor: OptimizerMonitor, | |
| *, | |
| plot_cfg: PlotStreamConfig, | |
| update_idx: int, | |
| step: int, | |
| total_steps: int, | |
| last_traj: go.Figure | None, | |
| loss_builder, | |
| trajectory_mode: str = "3d", | |
| trajectory_webgl_fallback: bool = False, | |
| best_loss: float | None = None, | |
| ) -> tuple[go.Figure, go.Figure, go.Figure, go.Figure]: | |
| """Throttle expensive 3D redraws; subsample points for 2D plots.""" | |
| final = step >= total_steps | |
| lite = not final | |
| loss_fig = _make_loss_figure( | |
| df, | |
| max_points=None if final else plot_cfg.loss_max_points, | |
| monitor=monitor, | |
| step=step, | |
| best_loss=best_loss, | |
| ) | |
| vu_fig = _make_string_vu_figure(monitor) | |
| twist_fig = _make_twist_figure(df, max_points=None if final else plot_cfg.loss_max_points) | |
| use_2d = trajectory_mode == "2d" | |
| refresh_traj = final or (update_idx % plot_cfg.trajectory_every == 0) or use_2d | |
| if not plot_cfg.live_3d and not final and not use_2d: | |
| traj_fig = last_traj or _trajectory_placeholder() | |
| elif refresh_traj: | |
| traj_fig = make_trajectory_figure( | |
| df, | |
| mode=trajectory_mode, | |
| max_points=None if final else plot_cfg.trajectory_max_points, | |
| lite=lite and not final, | |
| webgl_fallback=trajectory_webgl_fallback, | |
| ) | |
| else: | |
| traj_fig = last_traj or make_trajectory_figure( | |
| df, | |
| mode=trajectory_mode, | |
| max_points=plot_cfg.trajectory_max_points, | |
| lite=True, | |
| webgl_fallback=trajectory_webgl_fallback, | |
| ) | |
| if loss_builder is not None: | |
| loss_fig = loss_builder(df if final else _subsample_df(df, plot_cfg.loss_max_points)) | |
| return traj_fig, loss_fig, vu_fig, twist_fig | |
| def run_optimization_stream( | |
| *, | |
| total_steps: int, | |
| steps_per_update: int, | |
| learning_rate: float, | |
| burst_factor_max: float, | |
| use_fisher_modulation: bool, | |
| plot_cfg: PlotStreamConfig | None = None, | |
| trajectory_mode: str = "3d", | |
| trajectory_webgl_fallback: bool = False, | |
| progress_cb=None, | |
| ) -> Generator[tuple, None, None]: | |
| import time | |
| _configure_cpu_threads() | |
| np.random.seed(42) | |
| torch.manual_seed(42) | |
| t0 = time.monotonic() | |
| model = SphereRosenbrockModel(num_instances=1) | |
| monitor = OptimizerMonitor(STRING_NAMES) | |
| def closure(): | |
| model.zero_grad() | |
| q = model() | |
| u = stereographic_projection(q) | |
| loss = rosenbrock_3d(u).mean() | |
| loss.backward() | |
| return loss | |
| opt = _build_hierarchical_optimizer( | |
| model, | |
| lr=learning_rate, | |
| burst_factor_max=burst_factor_max, | |
| use_fisher_modulation=use_fisher_modulation, | |
| ) | |
| plot_cfg = plot_cfg or PlotStreamConfig.for_run_mode("standard") | |
| step = 0 | |
| best_loss = float("inf") | |
| prev_loss = float("inf") | |
| loss_val = float("inf") | |
| update_idx = 0 | |
| last_traj: go.Figure | None = None | |
| while step < total_steps: | |
| for _ in range(steps_per_update): | |
| loss = opt.step(closure) | |
| loss_val = loss.item() | |
| if loss_val < best_loss: | |
| best_loss = loss_val | |
| if abs(prev_loss - loss_val) < 5e-5 and step > 100: | |
| layer_idx = min(step // 500, len(STRING_NAMES) - 1) | |
| monitor.record_burst( | |
| step, STRING_NAMES[layer_idx], severity=abs(prev_loss - loss_val) * 1e4 | |
| ) | |
| prev_loss = loss_val | |
| with torch.no_grad(): | |
| u = stereographic_projection(model.q).squeeze(0).cpu().numpy() | |
| twist = float(getattr(opt, "twist_accum", 0.0)) if hasattr(opt, "twist_accum") else 0.0 | |
| monitor.record_step(step, loss_val, u, twist=twist) | |
| step += 1 | |
| if step >= total_steps: | |
| break | |
| if progress_cb is not None: | |
| progress_cb(step / total_steps, desc=f"Optimizing step {step}/{total_steps}") | |
| df = pd.DataFrame(monitor.history) | |
| elapsed = time.monotonic() - t0 | |
| eta = format_eta_status(step, total_steps, elapsed) | |
| status = ( | |
| f"STEP {step:,} / {total_steps:,} │ LOSS {loss_val:.6f} │ " | |
| f"BEST {best_loss:.6f} │ BURSTS {len(monitor.burst_events)} │ {eta}" | |
| ) | |
| update_idx += 1 | |
| traj_fig, loss_fig, vu_fig, twist_fig = _build_live_figures( | |
| df, | |
| monitor, | |
| plot_cfg=plot_cfg, | |
| update_idx=update_idx, | |
| step=step, | |
| total_steps=total_steps, | |
| last_traj=last_traj, | |
| loss_builder=None, | |
| trajectory_mode=trajectory_mode, | |
| trajectory_webgl_fallback=trajectory_webgl_fallback, | |
| best_loss=best_loss, | |
| ) | |
| last_traj = traj_fig | |
| yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history)) | |
| if progress_cb is not None: | |
| progress_cb(1.0, desc="Optimization complete") | |
| def _subsample_series(steps: list, values: list, max_points: int) -> tuple[list, list]: | |
| if len(steps) <= max_points: | |
| return steps, values | |
| idx = np.linspace(0, len(steps) - 1, max_points, dtype=int) | |
| return [steps[i] for i in idx], [values[i] for i in idx] | |
| def _make_dual_loss_figure( | |
| hist_burst: dict, | |
| hist_adam: dict, | |
| *, | |
| max_points: int | None = None, | |
| ) -> go.Figure: | |
| hb, ha = hist_burst, hist_adam | |
| if max_points and len(hb["step"]) > max_points: | |
| s, l = _subsample_series(hb["step"], hb["loss"], max_points) | |
| hb = {"step": s, "loss": l} | |
| s2, l2 = _subsample_series(ha["step"], ha["loss"], max_points) | |
| ha = {"step": s2, "loss": l2} | |
| fig = go.Figure() | |
| fig.add_trace( | |
| go.Scatter( | |
| x=hb["step"], y=hb["loss"], | |
| mode="lines", name="6-String Burst", | |
| line=dict(color="#FFB000", width=2), | |
| ) | |
| ) | |
| fig.add_trace( | |
| go.Scatter( | |
| x=ha["step"], y=ha["loss"], | |
| mode="lines", name="RiemannianAdam", | |
| line=dict(color="#4D96FF", width=2, dash="dot"), | |
| ) | |
| ) | |
| fig.update_layout( | |
| title="Burst vs RiemannianAdam (same init, log scale)", | |
| yaxis_type="log", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| xaxis=dict(gridcolor="#333", title="Step"), | |
| yaxis=dict(gridcolor="#333", title="Loss"), | |
| height=300, | |
| legend=dict(orientation="h", y=1.12), | |
| margin=dict(l=50, r=20, t=50, b=40), | |
| ) | |
| return fig | |
| def run_comparison_stream( | |
| *, | |
| total_steps: int, | |
| steps_per_update: int, | |
| learning_rate: float, | |
| burst_factor_max: float, | |
| use_fisher_modulation: bool, | |
| plot_cfg: PlotStreamConfig | None = None, | |
| trajectory_mode: str = "3d", | |
| trajectory_webgl_fallback: bool = False, | |
| progress_cb=None, | |
| ) -> Generator[tuple, None, None]: | |
| """Side-by-side burst optimizer vs geoopt RiemannianAdam on identical Rosenbrock S³ init.""" | |
| import time | |
| from geoopt.optim import RiemannianAdam | |
| _configure_cpu_threads() | |
| np.random.seed(42) | |
| torch.manual_seed(42) | |
| t0 = time.monotonic() | |
| cap = min(int(total_steps), 3000 if is_hf_space() else int(total_steps)) | |
| steps_per_update = int(steps_per_update) | |
| model_burst = SphereRosenbrockModel(num_instances=1) | |
| init_q = model_burst.q.data.clone() | |
| model_adam = SphereRosenbrockModel(num_instances=1) | |
| model_adam.q.data.copy_(init_q) | |
| monitor = OptimizerMonitor(STRING_NAMES) | |
| hist_burst: dict[str, list] = {"step": [], "loss": []} | |
| hist_adam: dict[str, list] = {"step": [], "loss": []} | |
| def closure_burst(): | |
| model_burst.zero_grad() | |
| u = stereographic_projection(model_burst()) | |
| loss = rosenbrock_3d(u).mean() | |
| loss.backward() | |
| return loss | |
| def closure_adam(): | |
| model_adam.zero_grad() | |
| u = stereographic_projection(model_adam()) | |
| loss = rosenbrock_3d(u).mean() | |
| loss.backward() | |
| return loss | |
| opt_burst = _build_hierarchical_optimizer( | |
| model_burst, | |
| lr=learning_rate, | |
| burst_factor_max=burst_factor_max, | |
| use_fisher_modulation=use_fisher_modulation, | |
| ) | |
| opt_adam = RiemannianAdam([model_adam.q], lr=learning_rate) | |
| plot_cfg = plot_cfg or PlotStreamConfig.for_run_mode("standard") | |
| step = 0 | |
| best_b = best_a = float("inf") | |
| loss_b = loss_a = float("inf") | |
| update_idx = 0 | |
| last_traj: go.Figure | None = None | |
| while step < cap: | |
| for _ in range(steps_per_update): | |
| loss_b = opt_burst.step(closure_burst).item() | |
| loss_a = opt_adam.step(closure_adam).item() | |
| best_b = min(best_b, loss_b) | |
| best_a = min(best_a, loss_a) | |
| hist_burst["step"].append(step) | |
| hist_burst["loss"].append(loss_b) | |
| hist_adam["step"].append(step) | |
| hist_adam["loss"].append(loss_a) | |
| with torch.no_grad(): | |
| u = stereographic_projection(model_burst.q).squeeze(0).cpu().numpy() | |
| twist = float(getattr(opt_burst, "twist_accum", 0.0)) if hasattr(opt_burst, "twist_accum") else 0.0 | |
| monitor.record_step(step, loss_b, u, twist=twist) | |
| step += 1 | |
| if step >= cap: | |
| break | |
| if progress_cb is not None: | |
| progress_cb(step / cap, desc=f"Compare step {step}/{cap}") | |
| df = pd.DataFrame(monitor.history) | |
| elapsed = time.monotonic() - t0 | |
| eta = format_eta_status(step, cap, elapsed, compare=True) | |
| status = ( | |
| f"COMPARE {step:,}/{cap:,} │ BURST {loss_b:.6f} (best {best_b:.6f}) │ " | |
| f"ADAM {loss_a:.6f} (best {best_a:.6f}) │ Δ {loss_a - loss_b:+.4f} │ {eta}" | |
| ) | |
| update_idx += 1 | |
| traj_fig, loss_fig, vu_fig, twist_fig = _build_live_figures( | |
| df, | |
| monitor, | |
| plot_cfg=plot_cfg, | |
| update_idx=update_idx, | |
| step=step, | |
| total_steps=cap, | |
| last_traj=last_traj, | |
| loss_builder=lambda d: _make_dual_loss_figure( | |
| hist_burst, | |
| hist_adam, | |
| max_points=None if step >= cap else plot_cfg.loss_max_points, | |
| ), | |
| trajectory_mode=trajectory_mode, | |
| trajectory_webgl_fallback=trajectory_webgl_fallback, | |
| best_loss=best_b, | |
| ) | |
| last_traj = traj_fig | |
| yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history)) | |
| if progress_cb is not None: | |
| progress_cb(1.0, desc="Comparison complete") | |
| def save_plotly_png(fig: go.Figure, filename: str) -> str | None: | |
| """Export plotly figure to PNG (kaleido) or HTML fallback.""" | |
| out_dir = Path(tempfile.gettempdir()) / "string_opt_exports" | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| png_path = out_dir / filename | |
| try: | |
| fig.write_image(str(png_path), width=1100, height=700, scale=2) | |
| return str(png_path) | |
| except Exception: | |
| html_path = png_path.with_suffix(".html") | |
| fig.write_html(str(html_path)) | |
| return str(html_path) | |
| def export_optimization_plots(history: dict | None) -> tuple[str | None, str | None, str | None]: | |
| """Rebuild figures from stored history and export trajectory, loss, VU PNGs.""" | |
| if not history or not history.get("step"): | |
| return None, None, None | |
| df = pd.DataFrame(history) | |
| monitor = OptimizerMonitor(STRING_NAMES) | |
| for i, step in enumerate(history["step"]): | |
| u = np.array([history["u_x"][i], history["u_y"][i], history["u_z"][i]]) | |
| monitor.record_step(int(step), history["loss"][i], u, twist=history.get("twist", [0])[i]) | |
| return ( | |
| save_plotly_png(make_trajectory_figure(df, mode="2d"), "trajectory.png"), | |
| save_plotly_png(_make_loss_figure(df), "loss.png"), | |
| save_plotly_png(_make_string_vu_figure(monitor), "vu.png"), | |
| ) | |
| # --- Guitar string tension calculator (complementary simple tool) --- | |
| STANDARD_TUNING: dict[str, dict[str, float]] = { | |
| "High E": {"freq_hz": 329.63, "gauge_in": 0.010}, | |
| "B": {"freq_hz": 246.94, "gauge_in": 0.013}, | |
| "G": {"freq_hz": 196.00, "gauge_in": 0.017}, | |
| "D": {"freq_hz": 146.83, "gauge_in": 0.026}, | |
| "A": {"freq_hz": 110.00, "gauge_in": 0.036}, | |
| "Low E": {"freq_hz": 82.41, "gauge_in": 0.046}, | |
| } | |
| DEFAULT_SCALE_LENGTH_IN = 25.5 | |
| def calculate_tension_lbs(freq_hz: float, scale_length_in: float, gauge_in: float) -> float: | |
| """D'Addario-style unit-weight approximation: tension in pounds.""" | |
| unit_weight = 0.000121547 * (gauge_in ** 2) | |
| return float((unit_weight * (2.0 * scale_length_in * freq_hz) ** 2) / 386.088) | |
| def tension_calculator_table(scale_length_in: float = DEFAULT_SCALE_LENGTH_IN) -> str: | |
| lines = [ | |
| f"### String tension reference (scale {scale_length_in:.1f}\")", | |
| "", | |
| "| String | Freq (Hz) | Gauge (in) | Tension (lb) |", | |
| "|--------|-----------|------------|--------------|", | |
| ] | |
| for name, spec in STANDARD_TUNING.items(): | |
| t = calculate_tension_lbs(spec["freq_hz"], scale_length_in, spec["gauge_in"]) | |
| lines.append( | |
| f"| **{name}** | {spec['freq_hz']:.2f} | {spec['gauge_in']:.3f} | **{t:.1f}** |" | |
| ) | |
| lines.extend([ | |
| "", | |
| "Formula: $T = UW \\cdot (2 L f)^2 / 386.088$ with unit weight $UW \\propto d^2$.", | |
| "Adjust scale length below for baritone/short-scale guitars.", | |
| ]) | |
| return "\n".join(lines) | |
| def tension_single_string( | |
| string_name: str, | |
| scale_length_in: float, | |
| gauge_in: float | None = None, | |
| freq_hz: float | None = None, | |
| ) -> str: | |
| spec = STANDARD_TUNING.get(string_name, STANDARD_TUNING["G"]) | |
| g = gauge_in if gauge_in and gauge_in > 0 else spec["gauge_in"] | |
| f = freq_hz if freq_hz and freq_hz > 0 else spec["freq_hz"] | |
| t = calculate_tension_lbs(f, scale_length_in, g) | |
| return "\n".join([ | |
| f"String: {string_name}", | |
| f"Frequency: {f:.2f} Hz", | |
| f"Gauge: {g:.3f} in", | |
| f"Scale length: {scale_length_in:.2f} in", | |
| f"Estimated tension: {t:.2f} lb ({t * 4.448:.1f} N)", | |
| "", | |
| "Approximation for steel acoustic strings — not a setup prescription.", | |
| ]) | |
| def _lite_modal_synthesis( | |
| freqs: np.ndarray, | |
| damping: np.ndarray, | |
| sr: int, | |
| duration: float, | |
| amps: np.ndarray | None = None, | |
| ) -> np.ndarray: | |
| """HF-safe damped harmonic modal sum (no torch / Stiefel deps).""" | |
| n_samples = int(sr * duration) | |
| t = np.arange(n_samples, dtype=np.float64) / sr | |
| if amps is None: | |
| amps = 1.0 / np.arange(1, len(freqs) + 1) | |
| y = np.zeros(n_samples, dtype=np.float64) | |
| for k, (freq, damp, amp) in enumerate(zip(freqs, damping, amps)): | |
| y += amp * np.exp(-damp * t) * np.sin(2.0 * np.pi * freq * t) | |
| peak = np.max(np.abs(y)) | |
| return (y / peak if peak > 1e-8 else y).astype(np.float32) | |
| def synthesize_modal_comparison( | |
| audio_path: str | None, | |
| *, | |
| duration: float = 2.0, | |
| use_sample: bool = False, | |
| inharmonicity_b: float = 0.0005, | |
| progress_cb=None, | |
| ) -> tuple[str | None, str | None, go.Figure, str]: | |
| """Before/after: original clip vs lite modal resynthesis from estimated partials.""" | |
| import librosa | |
| import soundfile as sf | |
| if progress_cb is not None: | |
| progress_cb(0.15, desc="Loading audio…") | |
| if use_sample: | |
| sample = get_sample_guitar_path() | |
| y, sr = _load_audio_array(str(sample), duration=duration) | |
| source = sample.name | |
| elif audio_path and Path(audio_path).is_file(): | |
| y, sr = _load_audio_array(audio_path, duration=duration) | |
| source = Path(audio_path).name | |
| else: | |
| y = _synthetic_guitar_tone(duration=duration) | |
| sr = 22050 | |
| source = "synthetic" | |
| hop = 512 | |
| f0, voiced, _ = librosa.pyin(y, fmin=80, fmax=600, sr=sr, hop_length=hop) | |
| f0_mean = float(np.nanmean(f0)) if f0 is not None and np.any(np.isfinite(f0)) else 196.0 | |
| if progress_cb is not None: | |
| progress_cb(0.45, desc="Fitting modal parameters…") | |
| n_modes = 6 | |
| freqs = np.array([ | |
| (k + 1) * f0_mean * np.sqrt(1.0 + inharmonicity_b * (k + 1) ** 2) | |
| for k in range(n_modes) | |
| ]) | |
| damping = np.linspace(2.0, 8.0, n_modes) | |
| rms_env = librosa.feature.rms(y=y, hop_length=hop)[0] | |
| decay_rate = float(np.median(np.diff(rms_env)[rms_env[:-1] > 0.01])) if len(rms_env) > 2 else -0.05 | |
| damping = np.clip(damping * (1.0 + abs(decay_rate) * 10), 1.5, 12.0) | |
| amps = 1.0 / np.arange(1, n_modes + 1) | |
| y_synth = _lite_modal_synthesis(freqs, damping, sr, len(y) / sr, amps) | |
| if progress_cb is not None: | |
| progress_cb(0.75, desc="Writing audio files…") | |
| out_dir = Path(tempfile.mkdtemp(prefix="string_modal_")) | |
| orig_path = out_dir / "original.wav" | |
| synth_path = out_dir / "modal_synthesis.wav" | |
| sf.write(str(orig_path), y, sr) | |
| sf.write(str(synth_path), y_synth, sr) | |
| t = np.arange(len(y)) / sr | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter(x=t, y=y, mode="lines", name="Original", line=dict(color="#4D96FF", width=1))) | |
| fig.add_trace(go.Scatter(x=t, y=y_synth, mode="lines", name="Modal synth", line=dict(color="#FFB000", width=1))) | |
| fig.update_layout( | |
| title=f"Before / After — {source}", | |
| xaxis_title="Time (s)", | |
| yaxis_title="Amplitude", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=260, | |
| legend=dict(orientation="h"), | |
| ) | |
| mse = float(np.mean((y - y_synth[: len(y)]) ** 2)) | |
| metrics = "\n".join([ | |
| "=== Modal Synthesis (lite preview) ===", | |
| "", | |
| f"source : {source}", | |
| f"f0 estimate : {f0_mean:.2f} Hz", | |
| f"inharmonicity B : {inharmonicity_b:.6f}", | |
| f"modes : {n_modes}", | |
| "damping (1/s) : " + ", ".join(f"{d:.2f}" for d in damping), | |
| f"recon MSE : {mse:.6f}", | |
| "", | |
| "Full physics-audio inversion (Stiefel + coupling) runs locally.", | |
| f"Pipeline: {PHYSICS_AUDIO_URL}", | |
| ]) | |
| if progress_cb is not None: | |
| progress_cb(1.0, desc="Synthesis complete") | |
| return str(orig_path), str(synth_path), fig, metrics | |
| def _load_audio_array(audio_path: str, duration: float = 3.0) -> tuple[np.ndarray, int]: | |
| import librosa | |
| path = Path(audio_path) | |
| if not path.is_file(): | |
| raise FileNotFoundError(f"Audio file not found: {audio_path}") | |
| y, sr = librosa.load(str(path), sr=22050, duration=duration, mono=True) | |
| y, _ = librosa.effects.trim(y, top_db=25) | |
| y = librosa.util.normalize(y) | |
| return y, sr | |
| def get_preset_params(preset_key: str) -> dict[str, Any]: | |
| """Return slider values for a named optimization preset.""" | |
| base = default_run_params() | |
| preset = OPTIMIZATION_PRESETS.get(preset_key, OPTIMIZATION_PRESETS["standard"]) | |
| return { | |
| "total_steps": preset.get("total_steps", base["total_steps"]), | |
| "steps_per_update": preset.get("steps_per_update", base["steps_per_update"]), | |
| "learning_rate": preset.get("learning_rate", base["learning_rate"]), | |
| "burst_factor_max": preset.get("burst_factor_max", base["burst_factor_max"]), | |
| "use_fisher_modulation": preset.get("use_fisher_modulation", base["use_fisher_modulation"]), | |
| } | |
| def _make_string_energy_figure(y: np.ndarray, sr: int, f0_hz: float | None) -> go.Figure: | |
| """Per-string spectral energy from harmonic partials 1–6.""" | |
| import librosa | |
| n_fft, hop = 2048, 512 | |
| S = np.abs(librosa.stft(y, n_fft=n_fft, hop_length=hop)) ** 2 | |
| freqs = librosa.fft_frequencies(sr=sr, n_fft=n_fft) | |
| f0 = f0_hz if f0_hz and f0_hz > 0 else 196.0 | |
| energies = [] | |
| for k in range(6): | |
| target = (k + 1) * f0 * np.sqrt(1 + 0.0005 * (k + 1) ** 2) | |
| band = (freqs >= target * 0.92) & (freqs <= target * 1.08) | |
| energies.append(float(S[band, :].sum()) if band.any() else 0.0) | |
| total = sum(energies) or 1.0 | |
| pct = [100 * e / total for e in energies] | |
| fig = go.Figure() | |
| fig.add_trace( | |
| go.Bar( | |
| x=STRING_NAMES, | |
| y=pct, | |
| marker=dict(color=STRING_COLORS, line=dict(color="#222", width=1)), | |
| text=[f"{p:.1f}%" for p in pct], | |
| textposition="outside", | |
| ) | |
| ) | |
| fig.update_layout( | |
| title=f"Partial Energy by String (f0 ≈ {f0:.1f} Hz)", | |
| yaxis_title="% of harmonic energy", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=280, | |
| margin=dict(l=40, r=20, t=40, b=60), | |
| ) | |
| return fig | |
| def _synthetic_guitar_tone(duration: float = 2.0, sr: int = 22050) -> np.ndarray: | |
| t = np.linspace(0, duration, int(sr * duration), endpoint=False) | |
| f0 = 196.0 | |
| y = np.zeros_like(t) | |
| for k in range(1, 7): | |
| freq = f0 * k * (1 + 0.0003 * k ** 2) | |
| amp = 1.0 / k | |
| decay = np.exp(-2.5 * k * t) | |
| y += amp * decay * np.sin(2 * np.pi * freq * t) | |
| y += 0.02 * np.random.randn(len(t)) | |
| return y / (np.max(np.abs(y)) + 1e-8) | |
| def analyze_audio_signal( | |
| audio_path: str | None, | |
| *, | |
| duration: float = 2.0, | |
| use_sample: bool = False, | |
| progress_cb=None, | |
| ) -> tuple[go.Figure, go.Figure, go.Figure, go.Figure, str]: | |
| import librosa | |
| _configure_cpu_threads() | |
| if progress_cb is not None: | |
| progress_cb(0.1, desc="Loading audio…") | |
| if use_sample: | |
| sample = get_sample_guitar_path() | |
| y, sr = _load_audio_array(str(sample), duration=duration) | |
| source = f"{sample.name} ({SAMPLE_GUITAR_NOTE})" | |
| elif audio_path and Path(audio_path).is_file(): | |
| y, sr = _load_audio_array(audio_path, duration=duration) | |
| source = Path(audio_path).name | |
| else: | |
| y = _synthetic_guitar_tone(duration=duration) | |
| sr = 22050 | |
| source = "synthetic G3 harmonic (fallback)" | |
| if progress_cb is not None: | |
| progress_cb(0.35, desc="Computing spectrogram…") | |
| t = np.arange(len(y)) / sr | |
| fig_wave = go.Figure() | |
| fig_wave.add_trace(go.Scatter(x=t, y=y, mode="lines", line=dict(color="#4D96FF", width=1))) | |
| fig_wave.update_layout( | |
| title=f"Waveform — {source}", | |
| xaxis_title="Time (s)", | |
| yaxis_title="Amplitude", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=220, | |
| margin=dict(l=40, r=20, t=40, b=30), | |
| ) | |
| n_fft, hop = 2048, 512 | |
| S = np.abs(librosa.stft(y, n_fft=n_fft, hop_length=hop)) | |
| S_db = librosa.amplitude_to_db(S, ref=np.max) | |
| times = librosa.frames_to_time(np.arange(S_db.shape[1]), sr=sr, hop_length=hop) | |
| freqs = librosa.fft_frequencies(sr=sr, n_fft=n_fft) | |
| fig_spec = go.Figure( | |
| data=go.Heatmap(z=S_db, x=times, y=freqs, colorscale="Viridis", colorbar=dict(title="dB")) | |
| ) | |
| fig_spec.update_layout( | |
| title="STFT Spectrogram", | |
| xaxis_title="Time (s)", | |
| yaxis_title="Frequency (Hz)", | |
| yaxis=dict(range=[0, min(4000, sr // 2)]), | |
| paper_bgcolor="#0a0a0a", | |
| font=dict(color="#FFB000"), | |
| height=320, | |
| margin=dict(l=50, r=20, t=40, b=30), | |
| ) | |
| if progress_cb is not None: | |
| progress_cb(0.65, desc="Tracking partials…") | |
| f0, voiced, _ = librosa.pyin(y, fmin=80, fmax=600, sr=sr, hop_length=hop) | |
| fig_partials = go.Figure() | |
| for k in range(6): | |
| if k == 0 and f0 is not None and np.any(np.isfinite(f0)): | |
| fig_partials.add_trace( | |
| go.Scatter( | |
| y=f0, mode="lines", name="f0 (pYIN)", | |
| line=dict(color=STRING_COLORS[k], width=1.5), | |
| ) | |
| ) | |
| elif k > 0 and f0 is not None: | |
| harmonic = np.where( | |
| (f0 > 0) & np.isfinite(f0), | |
| (k + 1) * f0 * np.sqrt(1 + 0.0005 * (k + 1) ** 2), | |
| np.nan, | |
| ) | |
| fig_partials.add_trace( | |
| go.Scatter( | |
| y=harmonic, mode="lines", name=f"Partial {k + 1}", | |
| line=dict(color=STRING_COLORS[k], width=1, dash="dot"), | |
| ) | |
| ) | |
| fig_partials.update_layout( | |
| title="Harmonic Partial Tracks (6 strings)", | |
| xaxis_title="Frame", | |
| yaxis_title="Frequency (Hz)", | |
| paper_bgcolor="#0a0a0a", | |
| plot_bgcolor="#111318", | |
| font=dict(color="#FFB000"), | |
| height=280, | |
| margin=dict(l=50, r=20, t=40, b=30), | |
| legend=dict(orientation="h", y=-0.25), | |
| ) | |
| f0_mean = float(np.nanmean(f0)) if f0 is not None and np.any(np.isfinite(f0)) else 196.0 | |
| fig_energy = _make_string_energy_figure(y, sr, f0_mean) | |
| rms = float(np.sqrt(np.mean(y ** 2))) | |
| peak = float(np.max(np.abs(y))) | |
| centroid = float(librosa.feature.spectral_centroid(y=y, sr=sr).mean()) | |
| decay = float(librosa.feature.rms(y=y, hop_length=hop).mean()) | |
| metrics = "\n".join([ | |
| "=== 6-String Spectrum Analysis ===", | |
| "", | |
| f"source : {source}", | |
| f"sample_rate : {sr} Hz", | |
| f"duration : {len(y) / sr:.2f} s", | |
| f"f0 (mean) : {f0_mean:.1f} Hz", | |
| f"rms : {rms:.4f}", | |
| f"peak : {peak:.4f}", | |
| f"spectral_centroid: {centroid:.1f} Hz", | |
| f"mean_frame_rms : {decay:.4f}", | |
| f"voiced_frames : {int(np.sum(voiced)) if voiced is not None else 0}", | |
| "", | |
| "Per-string bars = energy in partial bands 1–6 (harmonic model).", | |
| "Full physics-audio: damped inharmonic modes on Stiefel manifold.", | |
| f"Pipeline: {PHYSICS_AUDIO_URL}", | |
| ]) | |
| if progress_cb is not None: | |
| progress_cb(1.0, desc="Analysis complete") | |
| return fig_wave, fig_spec, fig_partials, fig_energy, metrics | |
| def _draw_classic_smith_background(ax) -> None: | |
| import matplotlib.pyplot as plt | |
| from matplotlib.patches import Arc, Circle | |
| ax.add_patch(Circle((0, 0), 1.0, fill=False, color="black", linewidth=1.5)) | |
| ax.plot([-1.1, 1.1], [0, 0], color="black", linewidth=1) | |
| for swr in (1.5, 2.0, 3.0, 5.0, 10.0): | |
| rho = (swr - 1.0) / (swr + 1.0) | |
| ax.add_patch(Circle((0, 0), rho, fill=False, color="gray", linewidth=0.8)) | |
| ax.text(rho + 0.02, 0.02, f"{swr}", fontsize=9, color="gray", ha="left", va="bottom") | |
| ax.text(0, 0.02, "SWR=1.0\nPerfect\nMatch", ha="center", va="bottom", fontsize=10, color="black") | |
| for r in (0.2, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0, 20.0): | |
| center_x = r / (r + 1.0) | |
| radius = 1.0 / (r + 1.0) | |
| ax.add_patch(Circle((center_x, 0), radius, fill=False, color="blue", linewidth=0.8, ls="--")) | |
| for base_x in (0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 20.0): | |
| for sign in (1.0, -1.0): | |
| x = sign * base_x | |
| center = (1.0, 1.0 / x) | |
| radius = abs(1.0 / x) | |
| theta1 = 0 if x > 0 else 180 | |
| theta2 = 180 if x > 0 else 360 | |
| ax.add_patch( | |
| Arc(center, 2 * radius, 2 * radius, angle=0.0, | |
| theta1=theta1, theta2=theta2, color="blue", linewidth=0.8, ls="--") | |
| ) | |
| ax.set_xlim(-1.1, 1.1) | |
| ax.set_ylim(-1.1, 1.1) | |
| ax.set_aspect("equal") | |
| ax.axis("off") | |
| def render_smith_chart_preview(seed: int = 42) -> str: | |
| """HF-safe Smith chart preview using synthetic Γ candidates (physics_audio viz style).""" | |
| import matplotlib | |
| matplotlib.use("Agg") | |
| import matplotlib.pyplot as plt | |
| rng = np.random.default_rng(seed) | |
| n = 24 | |
| angles = rng.uniform(0, 2 * np.pi, n) | |
| mags = rng.uniform(0.05, 0.85, n) | |
| mags = np.sort(mags) | |
| gamma = mags * np.exp(1j * angles) | |
| re, im = np.real(gamma), np.imag(gamma) | |
| mag_norm = mags / (mags.max() + 1e-8) | |
| colors = plt.cm.viridis_r(1.0 - mag_norm) | |
| pre_idx, sel_idx = 8, 3 | |
| fig = plt.figure(figsize=(8, 8)) | |
| ax = fig.add_subplot(111) | |
| _draw_classic_smith_background(ax) | |
| ax.scatter(re, im, c=colors, s=90, marker="o", edgecolors="black", linewidth=1.0, alpha=0.92, zorder=5) | |
| ax.scatter(re[pre_idx], im[pre_idx], s=120, marker="o", facecolors="black", edgecolors="white", zorder=10) | |
| ax.scatter(re[sel_idx], im[sel_idx], s=120, marker="o", facecolors="lime", edgecolors="black", zorder=11) | |
| ax.arrow(re[pre_idx], im[pre_idx], re[sel_idx] - re[pre_idx], im[sel_idx] - im[pre_idx], | |
| head_width=0.06, head_length=0.08, fc="black", ec="black", lw=1, length_includes_head=True) | |
| ax.set_title(f"Smith Chart Preview — Seed {seed} (synthetic Γ jump)", fontsize=14, pad=20) | |
| sm = plt.cm.ScalarMappable(cmap="viridis_r", norm=plt.Normalize(mags.min(), mags.max())) | |
| sm.set_array([]) | |
| fig.colorbar(sm, ax=ax, shrink=0.65, pad=0.02).set_label("|Γ| mismatch — lower = better") | |
| out_dir = Path(tempfile.mkdtemp(prefix="string_smith_")) | |
| path = out_dir / f"smith_preview_seed_{seed}.png" | |
| fig.savefig(path, dpi=150, bbox_inches="tight") | |
| plt.close(fig) | |
| return str(path) | |
| def run_analysis(_kappa: float = 0.85) -> tuple[str, str | None, str | None]: | |
| """Mystery-shell compatibility: demo guitar spectrum + Smith chart + overview.""" | |
| _fig_wave, _fig_spec, _fig_partials, _fig_energy, metrics = analyze_audio_signal( | |
| None, duration=default_run_params()["audio_duration"], use_sample=True | |
| ) | |
| smith_path = render_smith_chart_preview() | |
| overview = get_overview_composite_path() | |
| overview_path = str(overview) if overview.is_file() else smith_path | |
| return metrics, smith_path, overview_path | |
| def terminal_results_snapshot() -> str: | |
| lines = ["Six-string hierarchy (Pythagorean stagger):", ""] | |
| for name, win, boost in zip(STRING_NAMES, WINDOWS, BURST_BOOSTS): | |
| lines.append(f" {name:8s} window {win:4d} burst {boost:.2f}×") | |
| lines.extend([ | |
| "", | |
| f"Default HF steps: {default_run_params()['total_steps']:,}", | |
| f"Demo guitar: {SAMPLE_GUITAR_FILENAME}", | |
| f"Physics audio: {PHYSICS_AUDIO_URL}", | |
| ]) | |
| return "\n".join(lines) | |
| def terminal_directory_help() -> str: | |
| return "\n".join([ | |
| "6-string-optimizer/ (github.com/kinaar8340/6-string-optimizer)", | |
| "├── src/optimizer/ burst optimizer + manifolds", | |
| "├── physics_audio/ real-audio Stiefel inverse problem", | |
| "│ ├── run_real_audio.py", | |
| "│ └── training_evaluation/viz.py (pyramid + Smith chart)", | |
| "├── hf_staging/ Space bundle source (mystery shell)", | |
| "│ ├── app.py terminal + keypad + optimize/spectrum", | |
| "│ ├── demo_core.py optimizer + audio + Smith preview", | |
| "│ └── assets/ demo_guitar_g3.wav · overview_composite.png", | |
| "", | |
| "Local: python physics_audio/run_real_audio.py", | |
| "HF: OPTIMIZE · SPECTRUM → DEMO GUITAR", | |
| ]) | |
| def terminal_probe_scope() -> str: | |
| return "\n".join([ | |
| "THIS SPACE — production demo (browser):", | |
| " · Rosenbrock S³ optimization with six-string burst hierarchy", | |
| " · Real-audio spectrum: waveform · STFT · harmonic partials", | |
| " · Demo guitar G3 one-click analysis", | |
| " · Smith chart + pyramid overview figures", | |
| " · CLI terminal + 24-key prog keypad", | |
| "", | |
| "GITHUB REPO — full depth:", | |
| " · physics_audio/run_real_audio.py — Stiefel training on real WAV", | |
| " · training_evaluation/viz.py — full pyramid + Smith chart jumps", | |
| " · Fisher-Rao burst modulation extensions", | |
| ]) | |
| def terminal_string_catalog() -> str: | |
| lines = ["Six virtual strings (outermost → innermost):", ""] | |
| for idx, (name, win, boost) in enumerate(zip(STRING_NAMES, WINDOWS, BURST_BOOSTS), start=1): | |
| lines.append(f" PROG {idx:02d} {name}") | |
| lines.append(f" stagnation window {win} · burst boost {boost:.2f}×") | |
| lines.extend(["", "PROG 07 → run optimization · PROG 08 → demo guitar · PROG 09 → tour"]) | |
| return "\n".join(lines) | |
| def terminal_physics_overview() -> str: | |
| return "\n".join([ | |
| "Physics-audio pipeline (local training):", | |
| "", | |
| " 1. Load real guitar WAV → streaming STFT features", | |
| " 2. Stiefel manifold parameterization of coupled string modes", | |
| " 3. Fisher-Rao / invariant losses + burst jumps on plateau", | |
| " 4. Viz: reconstruction pyramid + Smith chart (Γ mismatch)", | |
| "", | |
| f"Entry: physics_audio/run_real_audio.py", | |
| f"Viz: physics_audio/training_evaluation/viz.py", | |
| "", | |
| "This Space renders an HF-safe Smith chart preview + bundled overview composite.", | |
| f"Repo: {PHYSICS_AUDIO_URL}", | |
| ]) | |
| def terminal_figures_index() -> str: | |
| overview = get_overview_composite_path() | |
| smith = render_smith_chart_preview() | |
| lines = ["Bundled / generated figures:", ""] | |
| lines.append(f" 1. overview_composite.png") | |
| lines.append(f" {overview}") | |
| lines.append(f" 2. smith_chart_preview.png") | |
| lines.append(f" {smith}") | |
| lines.append("") | |
| lines.append("Open Figures tab in the UI for full grid.") | |
| lines.append(f"Regenerate locally: python physics_audio/run_real_audio.py") | |
| return "\n".join(lines) | |
| def terminal_keypad_map() -> str: | |
| lines = ["Assigned prog keys (01–12):", ""] | |
| for index in sorted(TERM_KEY_ACTIONS): | |
| _action, desc = TERM_KEY_ACTIONS[index] | |
| tag = "01 Home" if index == 1 else f"{index:02d}" | |
| lines.append(f" [{tag}] {desc}") | |
| lines.extend([ | |
| "", | |
| "D-pad: ▲▼◀▶ move menu · enter confirm · clear blank", | |
| "Keys 13–24: reserved (latch only)", | |
| "PROG 1–6 mirror string layers · 7=optimize · 8=demo guitar", | |
| ]) | |
| return "\n".join(lines) | |
| def terminal_optimizer_help() -> str: | |
| return "\n".join([ | |
| "6-STRING SIGNAL ANALYZER — keypad / terminal help", | |
| "", | |
| "WORKSPACE TABS:", | |
| " OPTIMIZE Rosenbrock S³ + VU meters + twist", | |
| " SPECTRUM Upload WAV or ▶ DEMO GUITAR (G3)", | |
| " SETTINGS Steps · LR · burst factor · Fisher-Rao", | |
| "", | |
| "KEYPAD:", | |
| " 01 Home 02 Status 03 Scope 04 Directory", | |
| " 05 Results 06 Build 07 Help 08 Scan", | |
| " 09 Strings 10 Physics 11 Tour 12 Figures", | |
| "", | |
| "Commands: optimize · spectrum · demo · tour · help · about", | |
| f"Repo: {GITHUB_URL}", | |
| ]) | |
| def terminal_guided_onboarding() -> str: | |
| return "\n".join([ | |
| "╔══════════════════════════════════════════════════════╗", | |
| "║ 6-STRING SIGNAL ANALYZER — GUIDED TOUR (SKIPPABLE) ║", | |
| "╚══════════════════════════════════════════════════════╝", | |
| "", | |
| "Press CLEAR or dismiss welcome card to skip anytime.", | |
| "", | |
| "STEP 1 — Theory tab: S³, bursts, guitar metaphor explained", | |
| "STEP 2 — OPTIMIZE: pick preset → ▶ RUN (~30s HF)", | |
| "STEP 3 — SPECTRUM: ▶ DEMO GUITAR (G3) + string energy bars", | |
| "STEP 4 — STRINGS tab: High E→Low E hierarchy table", | |
| "STEP 5 — PHYSICS: Smith chart Γ + pyramid overview", | |
| "", | |
| "Keypad 1–6 = string layers · 7 = optimize · 8 = demo · 11 = this tour", | |
| "", | |
| f"Repo: {GITHUB_URL}", | |
| f"Physics: {PHYSICS_AUDIO_URL}", | |
| ]) | |
| def terminal_about() -> str: | |
| return "\n".join([ | |
| "6-STRING OPTIMIZER — production", | |
| "Riemannian burst optimizer on S³ · six virtual guitar strings", | |
| "Real-audio spectrum analysis · physics-audio Smith chart viz", | |
| f"GitHub: {GITHUB_URL}", | |
| f"Space: {HF_SPACE_URL}", | |
| f"Shell: mystery terminal + qvpic signal-analyzer style", | |
| get_build_label(), | |
| ]) |