Spaces:
Sleeping
Sleeping
WebGL-safe 2D trajectory fallback + enriched loss plot
Browse files- app.py +124 -26
- build_info.py +1 -1
- demo_core.py +245 -33
app.py
CHANGED
|
@@ -55,6 +55,10 @@ from demo_core import (
|
|
| 55 |
LOCAL_RUN_MD,
|
| 56 |
PlotStreamConfig,
|
| 57 |
RUN_MODES,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
get_overview_composite_path,
|
| 59 |
get_preset_params,
|
| 60 |
get_sample_guitar_path,
|
|
@@ -914,29 +918,46 @@ def _has_opt_history(history: dict | None) -> bool:
|
|
| 914 |
return bool(history and history.get("step"))
|
| 915 |
|
| 916 |
|
| 917 |
-
def
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 921 |
return gr.update(visible=True), gr.update(visible=False)
|
| 922 |
|
| 923 |
|
| 924 |
-
def
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 931 |
|
|
|
|
|
|
|
| 932 |
|
| 933 |
-
def _plot_3d_for_mode(mode: str, history: dict | None) -> tuple:
|
| 934 |
-
"""Idle visibility when switching run mode or resetting."""
|
| 935 |
-
return _plot_3d_on_run_finish(mode, history)
|
| 936 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 937 |
|
| 938 |
-
|
| 939 |
-
|
|
|
|
| 940 |
|
| 941 |
|
| 942 |
def _nav_to_page(page: str) -> tuple:
|
|
@@ -1130,6 +1151,32 @@ WALLPAPER_HEAD = f"""
|
|
| 1130 |
}}
|
| 1131 |
}})();
|
| 1132 |
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1133 |
{KATEX_CDN}
|
| 1134 |
"""
|
| 1135 |
|
|
@@ -1359,6 +1406,21 @@ footer {{
|
|
| 1359 |
.gradio-container .vqc-run-mode-radio label {{
|
| 1360 |
line-height: 1.35 !important;
|
| 1361 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1362 |
.gradio-container .vqc-panel-header-row {{
|
| 1363 |
display: flex !important;
|
| 1364 |
align-items: center !important;
|
|
@@ -2166,9 +2228,13 @@ def _run_optimize(
|
|
| 2166 |
use_fisher_modulation: bool,
|
| 2167 |
compare_adam: bool,
|
| 2168 |
run_mode: str,
|
|
|
|
|
|
|
| 2169 |
progress: gr.Progress = gr.Progress(track_tqdm=False),
|
| 2170 |
):
|
| 2171 |
plot_cfg = PlotStreamConfig.for_run_mode(run_mode or "quick")
|
|
|
|
|
|
|
| 2172 |
stream_fn = run_comparison_stream if compare_adam else run_optimization_stream
|
| 2173 |
try:
|
| 2174 |
yield from stream_fn(
|
|
@@ -2178,6 +2244,8 @@ def _run_optimize(
|
|
| 2178 |
burst_factor_max=float(burst_factor_max),
|
| 2179 |
use_fisher_modulation=bool(use_fisher_modulation),
|
| 2180 |
plot_cfg=plot_cfg,
|
|
|
|
|
|
|
| 2181 |
progress_cb=progress,
|
| 2182 |
)
|
| 2183 |
except Exception as exc:
|
|
@@ -2579,9 +2647,23 @@ def build_app() -> gr.Blocks:
|
|
| 2579 |
)
|
| 2580 |
|
| 2581 |
opt_history_state = gr.State(None)
|
|
|
|
| 2582 |
|
| 2583 |
with gr.Tabs():
|
| 2584 |
with gr.Tab("OPTIMIZE"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2585 |
with gr.Row():
|
| 2586 |
with gr.Column(
|
| 2587 |
visible=False,
|
|
@@ -2598,9 +2680,9 @@ def build_app() -> gr.Blocks:
|
|
| 2598 |
elem_classes=["vqc-plot3d-hint-col"],
|
| 2599 |
) as plot_3d_hint_col:
|
| 2600 |
plot_3d_hint = gr.Markdown(
|
| 2601 |
-
"**S³ trajectory (Quick Demo)** — hidden during the run to cut
|
| 2602 |
-
"
|
| 2603 |
-
"completes.",
|
| 2604 |
elem_classes=["vqc-plot3d-hint"],
|
| 2605 |
)
|
| 2606 |
plot_loss = gr.Plot(label="Loss envelope")
|
|
@@ -2717,10 +2799,10 @@ def build_app() -> gr.Blocks:
|
|
| 2717 |
logger.exception("Modal synthesis failed")
|
| 2718 |
return None, None, go.Figure(), friendly_error("synthesis", exc)
|
| 2719 |
|
| 2720 |
-
def _reset_workspace():
|
| 2721 |
p = get_run_mode_params("quick")
|
| 2722 |
spec = RUN_MODES["quick"]
|
| 2723 |
-
plot_col, hint_col = _plot_3d_for_mode("quick", None)
|
| 2724 |
return (
|
| 2725 |
p["total_steps"],
|
| 2726 |
p["steps_per_update"],
|
|
@@ -2750,14 +2832,14 @@ def build_app() -> gr.Blocks:
|
|
| 2750 |
opt_event = (
|
| 2751 |
run_opt_btn.click(
|
| 2752 |
_plot_3d_on_run_start,
|
| 2753 |
-
[run_mode],
|
| 2754 |
[plot_3d_col, plot_3d_hint_col],
|
| 2755 |
)
|
| 2756 |
.then(
|
| 2757 |
_run_optimize,
|
| 2758 |
[
|
| 2759 |
total_steps, steps_per_update, learning_rate, burst_factor_max,
|
| 2760 |
-
use_fisher, compare_adam, run_mode,
|
| 2761 |
],
|
| 2762 |
[plot_3d, plot_loss, plot_vu, plot_twist, opt_status, opt_history_state],
|
| 2763 |
)
|
|
@@ -2768,7 +2850,7 @@ def build_app() -> gr.Blocks:
|
|
| 2768 |
)
|
| 2769 |
.then(
|
| 2770 |
_plot_3d_on_run_finish,
|
| 2771 |
-
[run_mode, opt_history_state],
|
| 2772 |
[plot_3d_col, plot_3d_hint_col],
|
| 2773 |
)
|
| 2774 |
)
|
|
@@ -2800,10 +2882,16 @@ def build_app() -> gr.Blocks:
|
|
| 2800 |
]
|
| 2801 |
run_mode.change(
|
| 2802 |
_apply_run_mode_with_3d,
|
| 2803 |
-
[run_mode, opt_history_state],
|
| 2804 |
run_mode_outputs,
|
| 2805 |
)
|
| 2806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2807 |
def _hint_on_dial_change(ts, spu, cmp):
|
| 2808 |
return _runtime_hint(ts, spu, cmp)
|
| 2809 |
|
|
@@ -2816,7 +2904,11 @@ def build_app() -> gr.Blocks:
|
|
| 2816 |
use_fisher, compare_adam, audio_duration, run_mode, preset_note,
|
| 2817 |
runtime_hint, opt_status, opt_history_state, plot_3d_col, plot_3d_hint_col,
|
| 2818 |
]
|
| 2819 |
-
reset_btn.click(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2820 |
|
| 2821 |
demo_guitar_btn.click(
|
| 2822 |
_run_demo_guitar,
|
|
@@ -2918,6 +3010,12 @@ def build_app() -> gr.Blocks:
|
|
| 2918 |
newhere_minimize_btn.click(_minimize_newhere, outputs=newhere_outputs[:3])
|
| 2919 |
claims_minimize_btn.click(_minimize_claims, outputs=claims_outputs[:3])
|
| 2920 |
demo.load(_stream_term_boot, outputs=term_keypad_outputs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2921 |
|
| 2922 |
gr.Markdown(
|
| 2923 |
"6-String Optimizer — mystery shell + physics-audio · "
|
|
|
|
| 55 |
LOCAL_RUN_MD,
|
| 56 |
PlotStreamConfig,
|
| 57 |
RUN_MODES,
|
| 58 |
+
TRAJECTORY_VIEW_CHOICES,
|
| 59 |
+
WEBGL_PROBE_JS,
|
| 60 |
+
resolve_trajectory_mode,
|
| 61 |
+
webgl_status_markdown,
|
| 62 |
get_overview_composite_path,
|
| 63 |
get_preset_params,
|
| 64 |
get_sample_guitar_path,
|
|
|
|
| 918 |
return bool(history and history.get("step"))
|
| 919 |
|
| 920 |
|
| 921 |
+
def _trajectory_panel_visibility(
|
| 922 |
+
run_mode: str,
|
| 923 |
+
history: dict | None,
|
| 924 |
+
traj_view: str,
|
| 925 |
+
webgl_ok: bool,
|
| 926 |
+
*,
|
| 927 |
+
running: bool = False,
|
| 928 |
+
) -> tuple:
|
| 929 |
+
"""Hide WebGL 3D column during Quick runs; 2D stays visible live."""
|
| 930 |
+
resolved = resolve_trajectory_mode(traj_view, webgl_ok)
|
| 931 |
+
if run_mode == "quick" and resolved == "3d":
|
| 932 |
+
if running or not _has_opt_history(history):
|
| 933 |
+
return gr.update(visible=False), gr.update(visible=True)
|
| 934 |
return gr.update(visible=True), gr.update(visible=False)
|
| 935 |
|
| 936 |
|
| 937 |
+
def _plot_3d_on_run_start(run_mode: str, traj_view: str, webgl_ok: bool) -> tuple:
|
| 938 |
+
return _trajectory_panel_visibility(run_mode, None, traj_view, webgl_ok, running=True)
|
| 939 |
+
|
| 940 |
+
|
| 941 |
+
def _plot_3d_on_run_finish(run_mode: str, history: dict | None, traj_view: str, webgl_ok: bool) -> tuple:
|
| 942 |
+
return _trajectory_panel_visibility(run_mode, history, traj_view, webgl_ok, running=False)
|
| 943 |
+
|
| 944 |
+
|
| 945 |
+
def _plot_3d_for_mode(run_mode: str, history: dict | None, traj_view: str, webgl_ok: bool) -> tuple:
|
| 946 |
+
return _trajectory_panel_visibility(run_mode, history, traj_view, webgl_ok, running=False)
|
| 947 |
+
|
| 948 |
|
| 949 |
+
def _apply_run_mode_with_3d(mode: str, history: dict | None, traj_view: str, webgl_ok: bool) -> tuple:
|
| 950 |
+
return _apply_run_mode(mode) + _plot_3d_for_mode(mode, history, traj_view, webgl_ok)
|
| 951 |
|
|
|
|
|
|
|
|
|
|
| 952 |
|
| 953 |
+
def _on_webgl_probe(probe: str | None = None) -> tuple:
|
| 954 |
+
ok = str(probe if probe is not None else "1") != "0"
|
| 955 |
+
view_up = gr.update(value="2d") if not ok else gr.update()
|
| 956 |
+
return ok, view_up, webgl_status_markdown(ok, "2d" if not ok else "auto")
|
| 957 |
|
| 958 |
+
|
| 959 |
+
def _on_trajectory_view_change(view: str, webgl_ok: bool) -> str:
|
| 960 |
+
return webgl_status_markdown(webgl_ok, view)
|
| 961 |
|
| 962 |
|
| 963 |
def _nav_to_page(page: str) -> tuple:
|
|
|
|
| 1151 |
}}
|
| 1152 |
}})();
|
| 1153 |
</script>
|
| 1154 |
+
<script>
|
| 1155 |
+
(function() {{
|
| 1156 |
+
function markWebGLFailed() {{
|
| 1157 |
+
try {{ localStorage.setItem('6string-webgl-failed', '1'); }} catch (e) {{}}
|
| 1158 |
+
var status = document.querySelector('.vqc-webgl-status');
|
| 1159 |
+
if (status) {{
|
| 1160 |
+
status.innerHTML = '⚠ <strong>WebGL error detected</strong> — switch <strong>Trajectory view</strong> to <strong>2D (WebGL-safe)</strong>.';
|
| 1161 |
+
}}
|
| 1162 |
+
}}
|
| 1163 |
+
function scanPlotlyWebGLErrors() {{
|
| 1164 |
+
var plots = document.querySelectorAll('.vqc-plot3d-panel, .js-plotly-plot');
|
| 1165 |
+
plots.forEach(function(plot) {{
|
| 1166 |
+
var text = plot.innerText || '';
|
| 1167 |
+
if (text.indexOf('WebGL is not supported') >= 0) markWebGLFailed();
|
| 1168 |
+
}});
|
| 1169 |
+
}}
|
| 1170 |
+
setInterval(scanPlotlyWebGLErrors, 2500);
|
| 1171 |
+
var obs = new MutationObserver(scanPlotlyWebGLErrors);
|
| 1172 |
+
function start() {{
|
| 1173 |
+
scanPlotlyWebGLErrors();
|
| 1174 |
+
if (document.body) obs.observe(document.body, {{ childList: true, subtree: true }});
|
| 1175 |
+
}}
|
| 1176 |
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
|
| 1177 |
+
else start();
|
| 1178 |
+
}})();
|
| 1179 |
+
</script>
|
| 1180 |
{KATEX_CDN}
|
| 1181 |
"""
|
| 1182 |
|
|
|
|
| 1406 |
.gradio-container .vqc-run-mode-radio label {{
|
| 1407 |
line-height: 1.35 !important;
|
| 1408 |
}}
|
| 1409 |
+
.gradio-container .vqc-webgl-status {{
|
| 1410 |
+
margin: 0.2rem 0 0.45rem 0 !important;
|
| 1411 |
+
padding: 0.45rem 0.65rem !important;
|
| 1412 |
+
border-radius: 8px !important;
|
| 1413 |
+
border: 1px solid rgba(255, 176, 0, 0.25) !important;
|
| 1414 |
+
background: rgba(10, 10, 10, 0.4) !important;
|
| 1415 |
+
font-size: 0.84rem !important;
|
| 1416 |
+
color: #c9b8ff !important;
|
| 1417 |
+
}}
|
| 1418 |
+
.gradio-container .vqc-webgl-status:empty {{
|
| 1419 |
+
display: none !important;
|
| 1420 |
+
}}
|
| 1421 |
+
.gradio-container .vqc-trajectory-view-radio label {{
|
| 1422 |
+
line-height: 1.3 !important;
|
| 1423 |
+
}}
|
| 1424 |
.gradio-container .vqc-panel-header-row {{
|
| 1425 |
display: flex !important;
|
| 1426 |
align-items: center !important;
|
|
|
|
| 2228 |
use_fisher_modulation: bool,
|
| 2229 |
compare_adam: bool,
|
| 2230 |
run_mode: str,
|
| 2231 |
+
trajectory_view: str,
|
| 2232 |
+
webgl_ok: bool,
|
| 2233 |
progress: gr.Progress = gr.Progress(track_tqdm=False),
|
| 2234 |
):
|
| 2235 |
plot_cfg = PlotStreamConfig.for_run_mode(run_mode or "quick")
|
| 2236 |
+
traj_mode = resolve_trajectory_mode(trajectory_view, webgl_ok)
|
| 2237 |
+
webgl_fallback = trajectory_view == "3d" and not webgl_ok
|
| 2238 |
stream_fn = run_comparison_stream if compare_adam else run_optimization_stream
|
| 2239 |
try:
|
| 2240 |
yield from stream_fn(
|
|
|
|
| 2244 |
burst_factor_max=float(burst_factor_max),
|
| 2245 |
use_fisher_modulation=bool(use_fisher_modulation),
|
| 2246 |
plot_cfg=plot_cfg,
|
| 2247 |
+
trajectory_mode=traj_mode,
|
| 2248 |
+
trajectory_webgl_fallback=webgl_fallback,
|
| 2249 |
progress_cb=progress,
|
| 2250 |
)
|
| 2251 |
except Exception as exc:
|
|
|
|
| 2647 |
)
|
| 2648 |
|
| 2649 |
opt_history_state = gr.State(None)
|
| 2650 |
+
webgl_ok_state = gr.State(True)
|
| 2651 |
|
| 2652 |
with gr.Tabs():
|
| 2653 |
with gr.Tab("OPTIMIZE"):
|
| 2654 |
+
gr.Markdown(
|
| 2655 |
+
"Watch the **stereographic Rosenbrock path** toward [1,1,1] — "
|
| 2656 |
+
"colored by loss. **Burst events** (vertical ticks on loss plot) fire when "
|
| 2657 |
+
"a virtual string's stagnation tension triggers a plateau escape."
|
| 2658 |
+
)
|
| 2659 |
+
trajectory_view = gr.Radio(
|
| 2660 |
+
choices=TRAJECTORY_VIEW_CHOICES,
|
| 2661 |
+
value="auto",
|
| 2662 |
+
label="Trajectory view",
|
| 2663 |
+
info="Choose 2D if you see a WebGL error (common in embedded or privacy browsers).",
|
| 2664 |
+
elem_classes=["vqc-trajectory-view-radio"],
|
| 2665 |
+
)
|
| 2666 |
+
webgl_status = gr.Markdown("", elem_classes=["vqc-webgl-status"])
|
| 2667 |
with gr.Row():
|
| 2668 |
with gr.Column(
|
| 2669 |
visible=False,
|
|
|
|
| 2680 |
elem_classes=["vqc-plot3d-hint-col"],
|
| 2681 |
) as plot_3d_hint_col:
|
| 2682 |
plot_3d_hint = gr.Markdown(
|
| 2683 |
+
"**S³ trajectory (Quick Demo + 3D)** — hidden during the run to cut "
|
| 2684 |
+
"Plotly payload. **2D (WebGL-safe)** mode streams live projections here. "
|
| 2685 |
+
"Full 3D path unlocks when optimization completes.",
|
| 2686 |
elem_classes=["vqc-plot3d-hint"],
|
| 2687 |
)
|
| 2688 |
plot_loss = gr.Plot(label="Loss envelope")
|
|
|
|
| 2799 |
logger.exception("Modal synthesis failed")
|
| 2800 |
return None, None, go.Figure(), friendly_error("synthesis", exc)
|
| 2801 |
|
| 2802 |
+
def _reset_workspace(traj_view, webgl_ok):
|
| 2803 |
p = get_run_mode_params("quick")
|
| 2804 |
spec = RUN_MODES["quick"]
|
| 2805 |
+
plot_col, hint_col = _plot_3d_for_mode("quick", None, traj_view, webgl_ok)
|
| 2806 |
return (
|
| 2807 |
p["total_steps"],
|
| 2808 |
p["steps_per_update"],
|
|
|
|
| 2832 |
opt_event = (
|
| 2833 |
run_opt_btn.click(
|
| 2834 |
_plot_3d_on_run_start,
|
| 2835 |
+
[run_mode, trajectory_view, webgl_ok_state],
|
| 2836 |
[plot_3d_col, plot_3d_hint_col],
|
| 2837 |
)
|
| 2838 |
.then(
|
| 2839 |
_run_optimize,
|
| 2840 |
[
|
| 2841 |
total_steps, steps_per_update, learning_rate, burst_factor_max,
|
| 2842 |
+
use_fisher, compare_adam, run_mode, trajectory_view, webgl_ok_state,
|
| 2843 |
],
|
| 2844 |
[plot_3d, plot_loss, plot_vu, plot_twist, opt_status, opt_history_state],
|
| 2845 |
)
|
|
|
|
| 2850 |
)
|
| 2851 |
.then(
|
| 2852 |
_plot_3d_on_run_finish,
|
| 2853 |
+
[run_mode, opt_history_state, trajectory_view, webgl_ok_state],
|
| 2854 |
[plot_3d_col, plot_3d_hint_col],
|
| 2855 |
)
|
| 2856 |
)
|
|
|
|
| 2882 |
]
|
| 2883 |
run_mode.change(
|
| 2884 |
_apply_run_mode_with_3d,
|
| 2885 |
+
[run_mode, opt_history_state, trajectory_view, webgl_ok_state],
|
| 2886 |
run_mode_outputs,
|
| 2887 |
)
|
| 2888 |
|
| 2889 |
+
trajectory_view.change(
|
| 2890 |
+
_on_trajectory_view_change,
|
| 2891 |
+
[trajectory_view, webgl_ok_state],
|
| 2892 |
+
[webgl_status],
|
| 2893 |
+
)
|
| 2894 |
+
|
| 2895 |
def _hint_on_dial_change(ts, spu, cmp):
|
| 2896 |
return _runtime_hint(ts, spu, cmp)
|
| 2897 |
|
|
|
|
| 2904 |
use_fisher, compare_adam, audio_duration, run_mode, preset_note,
|
| 2905 |
runtime_hint, opt_status, opt_history_state, plot_3d_col, plot_3d_hint_col,
|
| 2906 |
]
|
| 2907 |
+
reset_btn.click(
|
| 2908 |
+
_reset_workspace,
|
| 2909 |
+
[trajectory_view, webgl_ok_state],
|
| 2910 |
+
outputs=reset_outputs,
|
| 2911 |
+
)
|
| 2912 |
|
| 2913 |
demo_guitar_btn.click(
|
| 2914 |
_run_demo_guitar,
|
|
|
|
| 3010 |
newhere_minimize_btn.click(_minimize_newhere, outputs=newhere_outputs[:3])
|
| 3011 |
claims_minimize_btn.click(_minimize_claims, outputs=claims_outputs[:3])
|
| 3012 |
demo.load(_stream_term_boot, outputs=term_keypad_outputs)
|
| 3013 |
+
demo.load(
|
| 3014 |
+
_on_webgl_probe,
|
| 3015 |
+
inputs=None,
|
| 3016 |
+
outputs=[webgl_ok_state, trajectory_view, webgl_status],
|
| 3017 |
+
js=WEBGL_PROBE_JS,
|
| 3018 |
+
)
|
| 3019 |
|
| 3020 |
gr.Markdown(
|
| 3021 |
"6-String Optimizer — mystery shell + physics-audio · "
|
build_info.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
"""Auto-generated by scripts/deploy_hf_space.sh — do not edit by hand."""
|
| 2 |
|
| 3 |
BUILD_COMMIT = "97b4a81"
|
| 4 |
-
BUILD_UPDATED_UTC = "2026-06-28T17:
|
|
|
|
| 1 |
"""Auto-generated by scripts/deploy_hf_space.sh — do not edit by hand."""
|
| 2 |
|
| 3 |
BUILD_COMMIT = "97b4a81"
|
| 4 |
+
BUILD_UPDATED_UTC = "2026-06-28T17:15:51Z"
|
demo_core.py
CHANGED
|
@@ -12,6 +12,7 @@ from typing import Any, Generator
|
|
| 12 |
import numpy as np
|
| 13 |
import plotly.express as px
|
| 14 |
import plotly.graph_objects as go
|
|
|
|
| 15 |
import pandas as pd
|
| 16 |
import torch
|
| 17 |
|
|
@@ -51,6 +52,7 @@ SIMULATION_BANNER_MD = """
|
|
| 51 |
|
| 52 |
KNOWN_LIMITATIONS_BODY = """
|
| 53 |
- **CPU only** — optimization + Plotly streaming can feel slow; use **Quick Demo** first.
|
|
|
|
| 54 |
- **Cold starts** — first load after sleep may take 30–60s to build.
|
| 55 |
- **ETA is approximate** — actual time varies with server load and concurrent users.
|
| 56 |
- **Compare vs RiemannianAdam** doubles wall time; capped at 3000 steps on HF.
|
|
@@ -58,6 +60,24 @@ KNOWN_LIMITATIONS_BODY = """
|
|
| 58 |
- **Best experience:** desktop Chrome/Firefox · let Quick Demo finish before switching tabs.
|
| 59 |
""".strip()
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
KNOWN_LIMITATIONS_MD = f"### Known limitations (HF free tier)\n{KNOWN_LIMITATIONS_BODY}"
|
| 62 |
|
| 63 |
LOCAL_RUN_MD = """
|
|
@@ -419,6 +439,30 @@ class PlotStreamConfig:
|
|
| 419 |
return cls(trajectory_every=1, trajectory_max_points=400, loss_max_points=1500, live_3d=True)
|
| 420 |
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
def _subsample_df(df: pd.DataFrame, max_points: int) -> pd.DataFrame:
|
| 423 |
if len(df) <= max_points:
|
| 424 |
return df
|
|
@@ -445,6 +489,10 @@ def friendly_error(context: str, exc: Exception) -> str:
|
|
| 445 |
"asset": (
|
| 446 |
f"Missing bundled asset ({msg}). Re-deploy the Space or use the GitHub repo copy."
|
| 447 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
}
|
| 449 |
lead = hints.get(context, "Something went wrong.")
|
| 450 |
return f"⚠ {lead}\n\nDetails: {msg[:300]}"
|
|
@@ -521,6 +569,73 @@ def _build_hierarchical_optimizer(
|
|
| 521 |
return opt
|
| 522 |
|
| 523 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
def _make_trajectory_figure(
|
| 525 |
df: pd.DataFrame,
|
| 526 |
*,
|
|
@@ -572,18 +687,95 @@ def _make_trajectory_figure(
|
|
| 572 |
return fig
|
| 573 |
|
| 574 |
|
| 575 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
plot_df = _subsample_df(df, max_points) if max_points else df
|
| 577 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
fig.update_traces(line=dict(color="#FFB000", width=2))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
fig.update_layout(
|
| 580 |
paper_bgcolor="#0a0a0a",
|
| 581 |
plot_bgcolor="#111318",
|
| 582 |
font=dict(color="#FFB000"),
|
| 583 |
-
xaxis=dict(gridcolor="#333"),
|
| 584 |
-
yaxis=dict(gridcolor="#333"),
|
| 585 |
height=280,
|
| 586 |
-
margin=dict(l=40, r=20, t=
|
| 587 |
)
|
| 588 |
return fig
|
| 589 |
|
|
@@ -657,26 +849,42 @@ def _build_live_figures(
|
|
| 657 |
total_steps: int,
|
| 658 |
last_traj: go.Figure | None,
|
| 659 |
loss_builder,
|
|
|
|
|
|
|
|
|
|
| 660 |
) -> tuple[go.Figure, go.Figure, go.Figure, go.Figure]:
|
| 661 |
"""Throttle expensive 3D redraws; subsample points for 2D plots."""
|
| 662 |
final = step >= total_steps
|
| 663 |
lite = not final
|
| 664 |
-
loss_fig = _make_loss_figure(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
vu_fig = _make_string_vu_figure(monitor)
|
| 666 |
twist_fig = _make_twist_figure(df, max_points=None if final else plot_cfg.loss_max_points)
|
| 667 |
|
| 668 |
-
|
| 669 |
-
|
|
|
|
| 670 |
traj_fig = last_traj or _trajectory_placeholder()
|
| 671 |
-
elif
|
| 672 |
-
traj_fig =
|
| 673 |
df,
|
|
|
|
| 674 |
max_points=None if final else plot_cfg.trajectory_max_points,
|
| 675 |
lite=lite and not final,
|
|
|
|
| 676 |
)
|
| 677 |
else:
|
| 678 |
-
traj_fig = last_traj or
|
| 679 |
-
df,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
)
|
| 681 |
|
| 682 |
if loss_builder is not None:
|
|
@@ -693,6 +901,8 @@ def run_optimization_stream(
|
|
| 693 |
burst_factor_max: float,
|
| 694 |
use_fisher_modulation: bool,
|
| 695 |
plot_cfg: PlotStreamConfig | None = None,
|
|
|
|
|
|
|
| 696 |
progress_cb=None,
|
| 697 |
) -> Generator[tuple, None, None]:
|
| 698 |
import time
|
|
@@ -768,6 +978,9 @@ def run_optimization_stream(
|
|
| 768 |
total_steps=total_steps,
|
| 769 |
last_traj=last_traj,
|
| 770 |
loss_builder=None,
|
|
|
|
|
|
|
|
|
|
| 771 |
)
|
| 772 |
last_traj = traj_fig
|
| 773 |
yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history))
|
|
@@ -833,6 +1046,8 @@ def run_comparison_stream(
|
|
| 833 |
burst_factor_max: float,
|
| 834 |
use_fisher_modulation: bool,
|
| 835 |
plot_cfg: PlotStreamConfig | None = None,
|
|
|
|
|
|
|
| 836 |
progress_cb=None,
|
| 837 |
) -> Generator[tuple, None, None]:
|
| 838 |
"""Side-by-side burst optimizer vs geoopt RiemannianAdam on identical Rosenbrock S³ init."""
|
|
@@ -914,26 +1129,23 @@ def run_comparison_stream(
|
|
| 914 |
f"ADAM {loss_a:.6f} (best {best_a:.6f}) │ Δ {loss_a - loss_b:+.4f} │ {eta}"
|
| 915 |
)
|
| 916 |
update_idx += 1
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
|
| 929 |
-
|
| 930 |
-
|
| 931 |
-
|
| 932 |
-
|
| 933 |
-
|
| 934 |
-
traj_fig = last_traj or _make_trajectory_figure(
|
| 935 |
-
df, max_points=plot_cfg.trajectory_max_points, lite=True,
|
| 936 |
-
)
|
| 937 |
last_traj = traj_fig
|
| 938 |
yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history))
|
| 939 |
|
|
@@ -965,7 +1177,7 @@ def export_optimization_plots(history: dict | None) -> tuple[str | None, str | N
|
|
| 965 |
u = np.array([history["u_x"][i], history["u_y"][i], history["u_z"][i]])
|
| 966 |
monitor.record_step(int(step), history["loss"][i], u, twist=history.get("twist", [0])[i])
|
| 967 |
return (
|
| 968 |
-
save_plotly_png(
|
| 969 |
save_plotly_png(_make_loss_figure(df), "loss.png"),
|
| 970 |
save_plotly_png(_make_string_vu_figure(monitor), "vu.png"),
|
| 971 |
)
|
|
|
|
| 12 |
import numpy as np
|
| 13 |
import plotly.express as px
|
| 14 |
import plotly.graph_objects as go
|
| 15 |
+
from plotly.subplots import make_subplots
|
| 16 |
import pandas as pd
|
| 17 |
import torch
|
| 18 |
|
|
|
|
| 52 |
|
| 53 |
KNOWN_LIMITATIONS_BODY = """
|
| 54 |
- **CPU only** — optimization + Plotly streaming can feel slow; use **Quick Demo** first.
|
| 55 |
+
- **WebGL required for 3D** — embedded views, privacy browsers, or older GPUs may block WebGL; use **2D (WebGL-safe)** trajectory view.
|
| 56 |
- **Cold starts** — first load after sleep may take 30–60s to build.
|
| 57 |
- **ETA is approximate** — actual time varies with server load and concurrent users.
|
| 58 |
- **Compare vs RiemannianAdam** doubles wall time; capped at 3000 steps on HF.
|
|
|
|
| 60 |
- **Best experience:** desktop Chrome/Firefox · let Quick Demo finish before switching tabs.
|
| 61 |
""".strip()
|
| 62 |
|
| 63 |
+
WEBGL_PROBE_JS = """() => {
|
| 64 |
+
let ok = false;
|
| 65 |
+
try {
|
| 66 |
+
if (localStorage.getItem('6string-webgl-failed') === '1') ok = false;
|
| 67 |
+
else {
|
| 68 |
+
const c = document.createElement('canvas');
|
| 69 |
+
ok = !!(window.WebGLRenderingContext && (c.getContext('webgl') || c.getContext('experimental-webgl')));
|
| 70 |
+
}
|
| 71 |
+
} catch (e) { ok = false; }
|
| 72 |
+
return ok ? "1" : "0";
|
| 73 |
+
}"""
|
| 74 |
+
|
| 75 |
+
TRAJECTORY_VIEW_CHOICES: list[tuple[str, str]] = [
|
| 76 |
+
("Auto (3D if WebGL available)", "auto"),
|
| 77 |
+
("3D interactive", "3d"),
|
| 78 |
+
("2D projection (WebGL-safe)", "2d"),
|
| 79 |
+
]
|
| 80 |
+
|
| 81 |
KNOWN_LIMITATIONS_MD = f"### Known limitations (HF free tier)\n{KNOWN_LIMITATIONS_BODY}"
|
| 82 |
|
| 83 |
LOCAL_RUN_MD = """
|
|
|
|
| 439 |
return cls(trajectory_every=1, trajectory_max_points=400, loss_max_points=1500, live_3d=True)
|
| 440 |
|
| 441 |
|
| 442 |
+
def resolve_trajectory_mode(view_pref: str, webgl_ok: bool) -> str:
|
| 443 |
+
"""Map UI preference + client WebGL probe to '3d' or '2d'."""
|
| 444 |
+
pref = (view_pref or "auto").strip().lower()
|
| 445 |
+
if pref == "2d":
|
| 446 |
+
return "2d"
|
| 447 |
+
if pref == "3d":
|
| 448 |
+
return "3d" if webgl_ok else "2d"
|
| 449 |
+
return "2d" if not webgl_ok else "3d"
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def webgl_status_markdown(webgl_ok: bool, view_pref: str) -> str:
|
| 453 |
+
resolved = resolve_trajectory_mode(view_pref, webgl_ok)
|
| 454 |
+
if not webgl_ok:
|
| 455 |
+
return (
|
| 456 |
+
"⚠ **WebGL unavailable** — trajectory uses **2D stereographic projections**. "
|
| 457 |
+
"Select **2D (WebGL-safe)** below, or try desktop Chrome/Firefox."
|
| 458 |
+
)
|
| 459 |
+
if view_pref == "2d":
|
| 460 |
+
return "Showing **2D projections** (WebGL-safe mode)."
|
| 461 |
+
if view_pref == "3d":
|
| 462 |
+
return "✓ WebGL detected — **interactive 3D** trajectory."
|
| 463 |
+
return "✓ WebGL detected — **Auto** will use 3D trajectory."
|
| 464 |
+
|
| 465 |
+
|
| 466 |
def _subsample_df(df: pd.DataFrame, max_points: int) -> pd.DataFrame:
|
| 467 |
if len(df) <= max_points:
|
| 468 |
return df
|
|
|
|
| 489 |
"asset": (
|
| 490 |
f"Missing bundled asset ({msg}). Re-deploy the Space or use the GitHub repo copy."
|
| 491 |
),
|
| 492 |
+
"webgl": (
|
| 493 |
+
"3D trajectory needs WebGL. Switch **Trajectory view** to **2D (WebGL-safe)** "
|
| 494 |
+
"in the OPTIMIZE tab, or open in desktop Chrome/Firefox."
|
| 495 |
+
),
|
| 496 |
}
|
| 497 |
lead = hints.get(context, "Something went wrong.")
|
| 498 |
return f"⚠ {lead}\n\nDetails: {msg[:300]}"
|
|
|
|
| 569 |
return opt
|
| 570 |
|
| 571 |
|
| 572 |
+
def _make_trajectory_2d_figure(
|
| 573 |
+
df: pd.DataFrame,
|
| 574 |
+
*,
|
| 575 |
+
max_points: int | None = None,
|
| 576 |
+
lite: bool = False,
|
| 577 |
+
webgl_fallback: bool = False,
|
| 578 |
+
) -> go.Figure:
|
| 579 |
+
"""Canvas/SVG 2D projections — no WebGL required."""
|
| 580 |
+
if df.empty or not {"u_x", "u_y", "u_z"}.issubset(df.columns):
|
| 581 |
+
return _trajectory_placeholder("Waiting for trajectory data…")
|
| 582 |
+
|
| 583 |
+
plot_df = _subsample_df(df, max_points) if max_points else df
|
| 584 |
+
marker_size = 3 if lite else 4
|
| 585 |
+
fallback_note = " · 2D fallback (WebGL unavailable)" if webgl_fallback else ""
|
| 586 |
+
fig = make_subplots(
|
| 587 |
+
rows=1,
|
| 588 |
+
cols=2,
|
| 589 |
+
subplot_titles=("u_x vs u_y", "u_y vs u_z"),
|
| 590 |
+
horizontal_spacing=0.1,
|
| 591 |
+
)
|
| 592 |
+
marker_kw = dict(
|
| 593 |
+
size=marker_size,
|
| 594 |
+
color=plot_df["loss"],
|
| 595 |
+
colorscale="Viridis",
|
| 596 |
+
showscale=not lite,
|
| 597 |
+
colorbar=dict(title="loss", len=0.55, y=0.5) if not lite else None,
|
| 598 |
+
)
|
| 599 |
+
line_kw = dict(width=1 if lite else 2, color="rgba(255,170,0,0.65)")
|
| 600 |
+
for col, xcol, ycol in ((1, "u_x", "u_y"), (2, "u_y", "u_z")):
|
| 601 |
+
fig.add_trace(
|
| 602 |
+
go.Scatter(
|
| 603 |
+
x=plot_df[xcol],
|
| 604 |
+
y=plot_df[ycol],
|
| 605 |
+
mode="lines+markers",
|
| 606 |
+
marker=marker_kw,
|
| 607 |
+
line=line_kw,
|
| 608 |
+
name=f"{xcol}/{ycol}",
|
| 609 |
+
showlegend=False,
|
| 610 |
+
),
|
| 611 |
+
row=1,
|
| 612 |
+
col=col,
|
| 613 |
+
)
|
| 614 |
+
fig.add_trace(
|
| 615 |
+
go.Scatter(
|
| 616 |
+
x=[1],
|
| 617 |
+
y=[1],
|
| 618 |
+
mode="markers",
|
| 619 |
+
marker=dict(size=9, color="#FF4444", symbol="diamond"),
|
| 620 |
+
name="min [1,1,1]",
|
| 621 |
+
showlegend=False,
|
| 622 |
+
),
|
| 623 |
+
row=1,
|
| 624 |
+
col=col,
|
| 625 |
+
)
|
| 626 |
+
fig.update_layout(
|
| 627 |
+
title=f"S³ → R³ Stereographic — 2D projections{fallback_note}",
|
| 628 |
+
paper_bgcolor="#0a0a0a",
|
| 629 |
+
font=dict(color="#FFB000"),
|
| 630 |
+
height=380,
|
| 631 |
+
margin=dict(l=40, r=20, t=50, b=30),
|
| 632 |
+
)
|
| 633 |
+
for col, xlab, ylab in ((1, "u_x", "u_y"), (2, "u_y", "u_z")):
|
| 634 |
+
fig.update_xaxes(title_text=xlab, gridcolor="#333", row=1, col=col)
|
| 635 |
+
fig.update_yaxes(title_text=ylab, gridcolor="#333", row=1, col=col)
|
| 636 |
+
return fig
|
| 637 |
+
|
| 638 |
+
|
| 639 |
def _make_trajectory_figure(
|
| 640 |
df: pd.DataFrame,
|
| 641 |
*,
|
|
|
|
| 687 |
return fig
|
| 688 |
|
| 689 |
|
| 690 |
+
def make_trajectory_figure(
|
| 691 |
+
df: pd.DataFrame,
|
| 692 |
+
*,
|
| 693 |
+
mode: str = "3d",
|
| 694 |
+
max_points: int | None = None,
|
| 695 |
+
lite: bool = False,
|
| 696 |
+
webgl_fallback: bool = False,
|
| 697 |
+
) -> go.Figure:
|
| 698 |
+
"""Dispatch 3D WebGL or 2D canvas trajectory."""
|
| 699 |
+
if mode == "2d":
|
| 700 |
+
return _make_trajectory_2d_figure(
|
| 701 |
+
df,
|
| 702 |
+
max_points=max_points,
|
| 703 |
+
lite=lite,
|
| 704 |
+
webgl_fallback=webgl_fallback,
|
| 705 |
+
)
|
| 706 |
+
if df.empty:
|
| 707 |
+
return _trajectory_placeholder("Waiting for trajectory data…")
|
| 708 |
+
return _make_trajectory_figure(df, max_points=max_points, lite=lite)
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
def _make_loss_figure(
|
| 712 |
+
df: pd.DataFrame,
|
| 713 |
+
*,
|
| 714 |
+
max_points: int | None = None,
|
| 715 |
+
monitor: OptimizerMonitor | None = None,
|
| 716 |
+
step: int | None = None,
|
| 717 |
+
best_loss: float | None = None,
|
| 718 |
+
) -> go.Figure:
|
| 719 |
+
if df.empty or "loss" not in df.columns:
|
| 720 |
+
fig = go.Figure()
|
| 721 |
+
fig.add_annotation(
|
| 722 |
+
text="Loss curve will appear after the first optimizer update",
|
| 723 |
+
xref="paper", yref="paper", x=0.5, y=0.5,
|
| 724 |
+
showarrow=False, font=dict(size=13, color="#FFB000"),
|
| 725 |
+
)
|
| 726 |
+
fig.update_layout(
|
| 727 |
+
title="Loss Envelope (log scale)",
|
| 728 |
+
paper_bgcolor="#0a0a0a",
|
| 729 |
+
plot_bgcolor="#111318",
|
| 730 |
+
font=dict(color="#FFB000"),
|
| 731 |
+
height=280,
|
| 732 |
+
xaxis=dict(visible=False),
|
| 733 |
+
yaxis=dict(visible=False),
|
| 734 |
+
)
|
| 735 |
+
return fig
|
| 736 |
+
|
| 737 |
plot_df = _subsample_df(df, max_points) if max_points else df
|
| 738 |
+
current_loss = float(plot_df["loss"].iloc[-1])
|
| 739 |
+
best = best_loss if best_loss is not None else float(plot_df["loss"].min())
|
| 740 |
+
cur_step = step if step is not None else int(plot_df["step"].iloc[-1])
|
| 741 |
+
burst_n = len(monitor.burst_events) if monitor else 0
|
| 742 |
+
title = (
|
| 743 |
+
f"Loss Envelope — step {cur_step:,} · current {current_loss:.4g} · "
|
| 744 |
+
f"best {best:.4g} · {burst_n} burst{'s' if burst_n != 1 else ''}"
|
| 745 |
+
)
|
| 746 |
+
fig = px.line(plot_df, x="step", y="loss", title=title, log_y=True)
|
| 747 |
fig.update_traces(line=dict(color="#FFB000", width=2))
|
| 748 |
+
if monitor:
|
| 749 |
+
color_cycle = STRING_COLORS
|
| 750 |
+
for i, (burst_step, layer, _) in enumerate(monitor.burst_events[-12:]):
|
| 751 |
+
color = color_cycle[i % len(color_cycle)]
|
| 752 |
+
fig.add_vline(
|
| 753 |
+
x=burst_step,
|
| 754 |
+
line_width=1,
|
| 755 |
+
line_dash="dot",
|
| 756 |
+
line_color=color,
|
| 757 |
+
opacity=0.55,
|
| 758 |
+
)
|
| 759 |
+
if i == len(monitor.burst_events[-12:]) - 1:
|
| 760 |
+
fig.add_annotation(
|
| 761 |
+
x=burst_step,
|
| 762 |
+
y=current_loss,
|
| 763 |
+
text=f"{layer} burst",
|
| 764 |
+
showarrow=False,
|
| 765 |
+
font=dict(size=9, color=color),
|
| 766 |
+
yshift=12,
|
| 767 |
+
)
|
| 768 |
+
losses = plot_df["loss"].astype(float)
|
| 769 |
+
y_min = max(losses.min() * 0.5, 1e-12)
|
| 770 |
+
y_max = max(losses.max() * 2.0, y_min * 10)
|
| 771 |
fig.update_layout(
|
| 772 |
paper_bgcolor="#0a0a0a",
|
| 773 |
plot_bgcolor="#111318",
|
| 774 |
font=dict(color="#FFB000"),
|
| 775 |
+
xaxis=dict(gridcolor="#333", title="Step"),
|
| 776 |
+
yaxis=dict(gridcolor="#333", title="Loss", type="log", range=[np.log10(y_min), np.log10(y_max)]),
|
| 777 |
height=280,
|
| 778 |
+
margin=dict(l=40, r=20, t=50, b=30),
|
| 779 |
)
|
| 780 |
return fig
|
| 781 |
|
|
|
|
| 849 |
total_steps: int,
|
| 850 |
last_traj: go.Figure | None,
|
| 851 |
loss_builder,
|
| 852 |
+
trajectory_mode: str = "3d",
|
| 853 |
+
trajectory_webgl_fallback: bool = False,
|
| 854 |
+
best_loss: float | None = None,
|
| 855 |
) -> tuple[go.Figure, go.Figure, go.Figure, go.Figure]:
|
| 856 |
"""Throttle expensive 3D redraws; subsample points for 2D plots."""
|
| 857 |
final = step >= total_steps
|
| 858 |
lite = not final
|
| 859 |
+
loss_fig = _make_loss_figure(
|
| 860 |
+
df,
|
| 861 |
+
max_points=None if final else plot_cfg.loss_max_points,
|
| 862 |
+
monitor=monitor,
|
| 863 |
+
step=step,
|
| 864 |
+
best_loss=best_loss,
|
| 865 |
+
)
|
| 866 |
vu_fig = _make_string_vu_figure(monitor)
|
| 867 |
twist_fig = _make_twist_figure(df, max_points=None if final else plot_cfg.loss_max_points)
|
| 868 |
|
| 869 |
+
use_2d = trajectory_mode == "2d"
|
| 870 |
+
refresh_traj = final or (update_idx % plot_cfg.trajectory_every == 0) or use_2d
|
| 871 |
+
if not plot_cfg.live_3d and not final and not use_2d:
|
| 872 |
traj_fig = last_traj or _trajectory_placeholder()
|
| 873 |
+
elif refresh_traj:
|
| 874 |
+
traj_fig = make_trajectory_figure(
|
| 875 |
df,
|
| 876 |
+
mode=trajectory_mode,
|
| 877 |
max_points=None if final else plot_cfg.trajectory_max_points,
|
| 878 |
lite=lite and not final,
|
| 879 |
+
webgl_fallback=trajectory_webgl_fallback,
|
| 880 |
)
|
| 881 |
else:
|
| 882 |
+
traj_fig = last_traj or make_trajectory_figure(
|
| 883 |
+
df,
|
| 884 |
+
mode=trajectory_mode,
|
| 885 |
+
max_points=plot_cfg.trajectory_max_points,
|
| 886 |
+
lite=True,
|
| 887 |
+
webgl_fallback=trajectory_webgl_fallback,
|
| 888 |
)
|
| 889 |
|
| 890 |
if loss_builder is not None:
|
|
|
|
| 901 |
burst_factor_max: float,
|
| 902 |
use_fisher_modulation: bool,
|
| 903 |
plot_cfg: PlotStreamConfig | None = None,
|
| 904 |
+
trajectory_mode: str = "3d",
|
| 905 |
+
trajectory_webgl_fallback: bool = False,
|
| 906 |
progress_cb=None,
|
| 907 |
) -> Generator[tuple, None, None]:
|
| 908 |
import time
|
|
|
|
| 978 |
total_steps=total_steps,
|
| 979 |
last_traj=last_traj,
|
| 980 |
loss_builder=None,
|
| 981 |
+
trajectory_mode=trajectory_mode,
|
| 982 |
+
trajectory_webgl_fallback=trajectory_webgl_fallback,
|
| 983 |
+
best_loss=best_loss,
|
| 984 |
)
|
| 985 |
last_traj = traj_fig
|
| 986 |
yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history))
|
|
|
|
| 1046 |
burst_factor_max: float,
|
| 1047 |
use_fisher_modulation: bool,
|
| 1048 |
plot_cfg: PlotStreamConfig | None = None,
|
| 1049 |
+
trajectory_mode: str = "3d",
|
| 1050 |
+
trajectory_webgl_fallback: bool = False,
|
| 1051 |
progress_cb=None,
|
| 1052 |
) -> Generator[tuple, None, None]:
|
| 1053 |
"""Side-by-side burst optimizer vs geoopt RiemannianAdam on identical Rosenbrock S³ init."""
|
|
|
|
| 1129 |
f"ADAM {loss_a:.6f} (best {best_a:.6f}) │ Δ {loss_a - loss_b:+.4f} │ {eta}"
|
| 1130 |
)
|
| 1131 |
update_idx += 1
|
| 1132 |
+
traj_fig, loss_fig, vu_fig, twist_fig = _build_live_figures(
|
| 1133 |
+
df,
|
| 1134 |
+
monitor,
|
| 1135 |
+
plot_cfg=plot_cfg,
|
| 1136 |
+
update_idx=update_idx,
|
| 1137 |
+
step=step,
|
| 1138 |
+
total_steps=cap,
|
| 1139 |
+
last_traj=last_traj,
|
| 1140 |
+
loss_builder=lambda d: _make_dual_loss_figure(
|
| 1141 |
+
hist_burst,
|
| 1142 |
+
hist_adam,
|
| 1143 |
+
max_points=None if step >= cap else plot_cfg.loss_max_points,
|
| 1144 |
+
),
|
| 1145 |
+
trajectory_mode=trajectory_mode,
|
| 1146 |
+
trajectory_webgl_fallback=trajectory_webgl_fallback,
|
| 1147 |
+
best_loss=best_b,
|
| 1148 |
+
)
|
|
|
|
|
|
|
|
|
|
| 1149 |
last_traj = traj_fig
|
| 1150 |
yield (traj_fig, loss_fig, vu_fig, twist_fig, status, dict(monitor.history))
|
| 1151 |
|
|
|
|
| 1177 |
u = np.array([history["u_x"][i], history["u_y"][i], history["u_z"][i]])
|
| 1178 |
monitor.record_step(int(step), history["loss"][i], u, twist=history.get("twist", [0])[i])
|
| 1179 |
return (
|
| 1180 |
+
save_plotly_png(make_trajectory_figure(df, mode="2d"), "trajectory.png"),
|
| 1181 |
save_plotly_png(_make_loss_figure(df), "loss.png"),
|
| 1182 |
save_plotly_png(_make_string_vu_figure(monitor), "vu.png"),
|
| 1183 |
)
|