Download app.py from arudradey/universal-math-game: direct link, hf CLI and curl.
- Browser
- Download file 18.6 kB
-
https://huggingface.co/spaces/arudradey/universal-math-game/resolve/main/app.py
- Command line
-
hf download hf://spaces/arudradey/universal-math-game/app.py
-
curl -L -o app.py https://huggingface.co/spaces/arudradey/universal-math-game/resolve/main/app.py
18.6 kB
| import os | |
| import json | |
| import time | |
| from typing import Dict, List, Any, Tuple | |
| import gradio as gr | |
| from umg_engine import UniversalMathGame, MathematicalState | |
| from laya_engine import LayaDecisionOracle | |
| # Session storage for active games | |
| GAMES: Dict[str, UniversalMathGame] = {} | |
| def get_game(sess_id: str, prob_str: str) -> UniversalMathGame: | |
| if sess_id not in GAMES or GAMES[sess_id].initial_problem != prob_str: | |
| GAMES[sess_id] = UniversalMathGame(prob_str) | |
| return GAMES[sess_id] | |
| PRESETS = { | |
| "Quadratic Equation": "x^2 - 5x + 6 = 0", | |
| "Difference of Squares": "x^2 - 49 = 0", | |
| "Cubic Polynomial": "x^3 - 4*x = 0", | |
| "Linear Equation": "3*x + 9 = 24", | |
| "Adversarial Fallacy Trap": "x^2 = x", | |
| "Rational Fraction": "(x^2 - 1)/(x - 1) = 4", | |
| } | |
| # ============================================================ | |
| # Visual HTML Renderers | |
| # ============================================================ | |
| def render_proof_timeline(history: List[Dict[str, Any]], current_eqs: List[str], is_goal: bool) -> str: | |
| """Render interactive vertical/horizontal proof steps.""" | |
| cards = [] | |
| # Initial state S0 | |
| init_eq = history[0]["state_before"][0] if history else (current_eqs[0] if current_eqs else "None") | |
| cards.append(f""" | |
| <div style="background: rgba(59, 130, 246, 0.08); border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 10px; padding: 10px 14px; margin-bottom: 10px;"> | |
| <div style="display: flex; justify-content: space-between; align-items: center;"> | |
| <span style="font-weight: 700; color: #2563eb; font-size: 0.85rem;">STATE S₀ (Initial)</span> | |
| <span style="font-size: 0.75rem; background: #dbeafe; color: #1e40af; padding: 2px 8px; border-radius: 999px;">Start</span> | |
| </div> | |
| <div style="font-family: monospace; font-size: 1.1rem; margin: 6px 0; color: #1e293b; font-weight: 600;">{init_eq}</div> | |
| </div> | |
| """) | |
| for idx, item in enumerate(history): | |
| act = item.get("action", {}) | |
| act_name = act.get("type", "unknown") | |
| state_after = " and ".join(item.get("state_after", [])) | |
| delta = item.get("delta_distance", 0.0) | |
| badge_color = "#10b981" if delta > 0 else "#6b7280" | |
| cards.append(f""" | |
| <div style="display: flex; align-items: center; justify-content: center; margin: 4px 0;"> | |
| <span style="background: #f1f5f9; border: 1px solid #cbd5e1; border-radius: 999px; padding: 2px 10px; font-size: 0.75rem; color: #475569; font-weight: 600;"> | |
| ⚡ Action: <b>{act_name}</b> (Δdist: {delta:+.1f}) | |
| </span> | |
| </div> | |
| <div style="background: rgba(16, 185, 129, 0.06); border: 1px solid rgba(16, 185, 129, 0.3); border-radius: 10px; padding: 10px 14px; margin-bottom: 10px;"> | |
| <div style="display: flex; justify-content: space-between; align-items: center;"> | |
| <span style="font-weight: 700; color: #059669; font-size: 0.85rem;">STATE S_{idx+1} (Verified by SymPy)</span> | |
| <span style="font-size: 0.75rem; background: #d1fae5; color: #065f46; padding: 2px 8px; border-radius: 999px;">Valid</span> | |
| </div> | |
| <div style="font-family: monospace; font-size: 1.1rem; margin: 6px 0; color: #0f172a; font-weight: 600;">{state_after}</div> | |
| </div> | |
| """) | |
| if is_goal: | |
| cards.append(""" | |
| <div style="background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.15)); border: 2px solid #10b981; border-radius: 12px; padding: 14px; text-align: center;"> | |
| <div style="font-size: 1.2rem; font-weight: 800; color: #065f46;">🏆 Q.E.D. — Proof Formally Completed!</div> | |
| <div style="font-size: 0.85rem; color: #047857; margin-top: 4px;">All roots isolated · Invariant equivalence verified · Proof Certificate generated</div> | |
| </div> | |
| """) | |
| return "".join(cards) | |
| def render_laya_radar(conf: float, dist: float, is_goal: bool, latency_ms: float = 24.5) -> str: | |
| """Render Laya System-1 Intuition Radar.""" | |
| conf_pct = int(conf * 100) | |
| risk_label = "0% (Invariant Geodesic)" if conf > 0.85 else "12% (Medium Complexity)" | |
| status_label = "SOLVED (Q.E.D.)" if is_goal else f"ACTIVE (D={dist:.1f})" | |
| return f""" | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; margin-bottom: 12px;"> | |
| <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; text-align: center;"> | |
| <div style="font-size: 0.75rem; color: #64748b; font-weight: 600;">LAYA INTUITION</div> | |
| <div style="font-size: 1.15rem; font-weight: 800; color: #2563eb;">{conf_pct}%</div> | |
| <div style="font-size: 0.7rem; color: #16a34a;">Calibrated Confidence</div> | |
| </div> | |
| <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; text-align: center;"> | |
| <div style="font-size: 0.75rem; color: #64748b; font-weight: 600;">DIVERGENCE RISK</div> | |
| <div style="font-size: 1.05rem; font-weight: 800; color: #16a34a;">{risk_label}</div> | |
| <div style="font-size: 0.7rem; color: #64748b;">Error Well Barrier</div> | |
| </div> | |
| <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; text-align: center;"> | |
| <div style="font-size: 0.75rem; color: #64748b; font-weight: 600;">GOAL DISTANCE</div> | |
| <div style="font-size: 1.15rem; font-weight: 800; color: #7c3aed;">{dist:.1f}</div> | |
| <div style="font-size: 0.7rem; color: #64748b;">{status_label}</div> | |
| </div> | |
| <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; text-align: center;"> | |
| <div style="font-size: 0.75rem; color: #64748b; font-weight: 600;">SYSTEM-1 LATENCY</div> | |
| <div style="font-size: 1.15rem; font-weight: 800; color: #0284c7;">~{latency_ms:.1f}ms</div> | |
| <div style="font-size: 0.7rem; color: #64748b;">Single Forward Pass</div> | |
| </div> | |
| </div> | |
| """ | |
| # ============================================================ | |
| # Handlers with Step-by-Step Autoplay Streaming | |
| # ============================================================ | |
| def init_game_ui(preset_name: str, custom_problem: str): | |
| prob = custom_problem.strip() if preset_name == "Custom" and custom_problem.strip() else PRESETS.get(preset_name, PRESETS["Quadratic Equation"]) | |
| game = UniversalMathGame(prob) | |
| sess_id = str(time.time()) | |
| GAMES[sess_id] = game | |
| latex_display = f"$${game.state.to_display_latex()}$$" | |
| m_ir_json = json.dumps(game.state.to_dict(), indent=2) | |
| dist = game.heuristic_distance(game.state) | |
| timeline_html = render_proof_timeline([], game.state.equations, False) | |
| radar_html = render_laya_radar(0.95, dist, False) | |
| legal_acts = [a["description"] for a in game.legal_actions()] | |
| legal_choices = gr.update(choices=legal_acts, value=legal_acts[0] if legal_acts else None) | |
| cert_json = json.dumps(game.export_proof_certificate(), indent=2) | |
| return ( | |
| sess_id, | |
| prob, | |
| latex_display, | |
| radar_html, | |
| timeline_html, | |
| "Game initialized at state S₀. Click 'Start Autoplay' or 'Laya Single Step' to watch Laya solve the problem.", | |
| m_ir_json, | |
| legal_choices, | |
| cert_json, | |
| ) | |
| def laya_step_ui(sess_id: str, prob: str): | |
| game = get_game(sess_id, prob) | |
| if game.is_goal(game.state): | |
| dist = 0.0 | |
| return ( | |
| f"$${game.state.to_display_latex()}$$", | |
| render_laya_radar(0.99, dist, True), | |
| render_proof_timeline(game.state.proof_history, game.state.equations, True), | |
| "🏆 Goal already reached! Proof certificate is verified.", | |
| json.dumps(game.state.to_dict(), indent=2), | |
| gr.update(), | |
| json.dumps(game.export_proof_certificate(), indent=2), | |
| ) | |
| legal_acts = game.legal_actions() | |
| chosen_act, conf, reason = LayaDecisionOracle.decide_next_action( | |
| game.state.equations, | |
| game.state.goals, | |
| legal_acts, | |
| depth=game.state.depth, | |
| ) | |
| res = game.step(chosen_act) | |
| dist = game.heuristic_distance(game.state) | |
| is_goal = game.is_goal(game.state) | |
| latex_display = f"$${game.state.to_display_latex()}$$" | |
| radar_html = render_laya_radar(conf, dist, is_goal) | |
| timeline_html = render_proof_timeline(game.state.proof_history, game.state.equations, is_goal) | |
| log_msg = f"{reason}\n{res['message']}" | |
| m_ir_json = json.dumps(game.state.to_dict(), indent=2) | |
| new_legal = [a["description"] for a in game.legal_actions()] | |
| legal_choices = gr.update(choices=new_legal, value=new_legal[0] if new_legal else None) | |
| cert_json = json.dumps(game.export_proof_certificate(), indent=2) | |
| return ( | |
| latex_display, | |
| radar_html, | |
| timeline_html, | |
| log_msg, | |
| m_ir_json, | |
| legal_choices, | |
| cert_json, | |
| ) | |
| def laya_autoplay_stream(sess_id: str, prob: str, step_delay: float = 1.0, max_steps: int = 8): | |
| """ | |
| Generator streaming step-by-step updates with animated time delay. | |
| Users watch the self-play game evolve live! | |
| """ | |
| game = get_game(sess_id, prob) | |
| for step_idx in range(int(max_steps)): | |
| if game.is_goal(game.state): | |
| # Final Q.E.D. state | |
| dist = 0.0 | |
| yield ( | |
| f"$${game.state.to_display_latex()}$$", | |
| render_laya_radar(0.99, dist, True), | |
| render_proof_timeline(game.state.proof_history, game.state.equations, True), | |
| "🎉 Formally Solved! Laya completed the mathematical game in self-play.", | |
| json.dumps(game.state.to_dict(), indent=2), | |
| gr.update(), | |
| json.dumps(game.export_proof_certificate(), indent=2), | |
| ) | |
| break | |
| legal_acts = game.legal_actions() | |
| chosen_act, conf, reason = LayaDecisionOracle.decide_next_action( | |
| game.state.equations, | |
| game.state.goals, | |
| legal_acts, | |
| depth=game.state.depth, | |
| ) | |
| res = game.step(chosen_act) | |
| dist = game.heuristic_distance(game.state) | |
| is_goal = game.is_goal(game.state) | |
| latex_display = f"$${game.state.to_display_latex()}$$" | |
| radar_html = render_laya_radar(conf, dist, is_goal) | |
| timeline_html = render_proof_timeline(game.state.proof_history, game.state.equations, is_goal) | |
| log_msg = f"Step {game.state.depth}: {reason}\n{res['message']}" | |
| m_ir_json = json.dumps(game.state.to_dict(), indent=2) | |
| new_legal = [a["description"] for a in game.legal_actions()] | |
| legal_choices = gr.update(choices=new_legal, value=new_legal[0] if new_legal else None) | |
| cert_json = json.dumps(game.export_proof_certificate(), indent=2) | |
| yield ( | |
| latex_display, | |
| radar_html, | |
| timeline_html, | |
| log_msg, | |
| m_ir_json, | |
| legal_choices, | |
| cert_json, | |
| ) | |
| if not res["valid"] or is_goal: | |
| break | |
| time.sleep(float(step_delay)) | |
| def human_step_ui(sess_id: str, prob: str, selected_action_desc: str, custom_val: str): | |
| game = get_game(sess_id, prob) | |
| legal_acts = game.legal_actions() | |
| chosen_act = None | |
| for a in legal_acts: | |
| if a["description"] == selected_action_desc: | |
| chosen_act = dict(a) | |
| break | |
| if not chosen_act: | |
| chosen_act = legal_acts[0] if legal_acts else {"type": "finish"} | |
| if custom_val and custom_val.strip(): | |
| chosen_act["value"] = custom_val.strip() | |
| res = game.step(chosen_act) | |
| dist = game.heuristic_distance(game.state) | |
| is_goal = game.is_goal(game.state) | |
| latex_display = f"$${game.state.to_display_latex()}$$" | |
| radar_html = render_laya_radar(0.85, dist, is_goal) | |
| timeline_html = render_proof_timeline(game.state.proof_history, game.state.equations, is_goal) | |
| m_ir_json = json.dumps(game.state.to_dict(), indent=2) | |
| new_legal = [a["description"] for a in game.legal_actions()] | |
| legal_choices = gr.update(choices=new_legal, value=new_legal[0] if new_legal else None) | |
| cert_json = json.dumps(game.export_proof_certificate(), indent=2) | |
| return ( | |
| latex_display, | |
| radar_html, | |
| timeline_html, | |
| res["message"], | |
| m_ir_json, | |
| legal_choices, | |
| cert_json, | |
| ) | |
| # ============================================================ | |
| # Gradio Interface | |
| # ============================================================ | |
| CUSTOM_CSS = """ | |
| .gradio-container { max-width: 1260px !important; margin: 0 auto !important; } | |
| .hero-title { font-size: 2.2rem; font-weight: 800; text-align: center; margin-bottom: 0.25rem; } | |
| .hero-desc { text-align: center; color: var(--body-text-color-subdued); margin-bottom: 1.5rem; font-size: 1.05rem; } | |
| .control-card { background: var(--background-fill-secondary); border-radius: 12px; padding: 1.25rem; border: 1px solid var(--border-color-primary); } | |
| """ | |
| with gr.Blocks(title="Universal Math Game (UMG v0.1) with Laya") as demo: | |
| session_holder = gr.State(value="init_session") | |
| gr.Markdown( | |
| "# ♟️ Universal Math Game (UMG v0.1) with Laya\n" | |
| "### Transforming Mathematics into a Verifiable Self-Play RL Game\n" | |
| "**System-1 Decision Agent (Laya)** proposes moves in <50ms · **Multi-Tier Symbolic Engine (SymPy)** serves as the referee" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| with gr.Group(): | |
| gr.Markdown("### 🎯 Problem Setup") | |
| preset_dropdown = gr.Dropdown( | |
| choices=list(PRESETS.keys()) + ["Custom"], | |
| value="Quadratic Equation", | |
| label="Choose Problem Preset", | |
| ) | |
| problem_text = gr.Textbox( | |
| value=PRESETS["Quadratic Equation"], | |
| label="Equation Expression", | |
| lines=1, | |
| ) | |
| init_btn = gr.Button("🔄 Initialize S₀ State", variant="secondary") | |
| with gr.Group(): | |
| gr.Markdown("### 🤖 Laya System-1 Autoplay") | |
| gr.Markdown("Watch Laya solve the problem autonomously in a live self-play animation.") | |
| with gr.Row(): | |
| laya_autoplay_btn = gr.Button("▶️ Start Autoplay", variant="primary") | |
| laya_single_btn = gr.Button("⏭️ Single Step", variant="secondary") | |
| step_delay_slider = gr.Slider( | |
| minimum=0.3, | |
| maximum=2.5, | |
| value=1.0, | |
| step=0.1, | |
| label="Autoplay Step Speed (Seconds per move)", | |
| ) | |
| with gr.Group(): | |
| gr.Markdown("### 🎮 Play as Human") | |
| human_action_dropdown = gr.Dropdown( | |
| choices=[], | |
| label="Select Legal Move", | |
| ) | |
| custom_arg_input = gr.Textbox( | |
| label="Optional Parameter", | |
| placeholder="e.g. constant or divisor", | |
| ) | |
| human_step_btn = gr.Button("🎯 Play Move as Human", variant="secondary") | |
| with gr.Column(scale=8): | |
| with gr.Group(): | |
| gr.Markdown("#### 📐 Current Mathematical State ($S_t$)") | |
| state_latex = gr.Markdown("$$x^2 - 5x + 6 = 0$$") | |
| radar_display = gr.HTML() | |
| with gr.Group(): | |
| gr.Markdown("#### 🗺️ Verified Proof Trajectory (Live Timeline)") | |
| timeline_display = gr.HTML() | |
| with gr.Group(): | |
| gr.Markdown("#### 🛡️ Verifier & Laya Oracle Feedback") | |
| verifier_log = gr.Textbox( | |
| label="Referee Judgment & System-1 Gut Feeling", | |
| interactive=False, | |
| lines=3, | |
| ) | |
| with gr.Accordion("🧬 M-IR (Mathematical Intermediate Representation)", open=False): | |
| m_ir_display = gr.Code( | |
| language="json", | |
| label="Structured JSON State Object", | |
| ) | |
| with gr.Accordion("📜 Verified Proof Certificate", open=True): | |
| cert_display = gr.Code( | |
| language="json", | |
| label="Machine-Checkable Proof Certificate", | |
| ) | |
| # Wire up Event Handlers | |
| preset_dropdown.change( | |
| fn=lambda p: PRESETS.get(p, ""), | |
| inputs=[preset_dropdown], | |
| outputs=[problem_text], | |
| ) | |
| init_btn.click( | |
| fn=init_game_ui, | |
| inputs=[preset_dropdown, problem_text], | |
| outputs=[ | |
| session_holder, | |
| problem_text, | |
| state_latex, | |
| radar_display, | |
| timeline_display, | |
| verifier_log, | |
| m_ir_display, | |
| human_action_dropdown, | |
| cert_display, | |
| ], | |
| ) | |
| laya_single_btn.click( | |
| fn=laya_step_ui, | |
| inputs=[session_holder, problem_text], | |
| outputs=[ | |
| state_latex, | |
| radar_display, | |
| timeline_display, | |
| verifier_log, | |
| m_ir_display, | |
| human_action_dropdown, | |
| cert_display, | |
| ], | |
| ) | |
| laya_autoplay_btn.click( | |
| fn=laya_autoplay_stream, | |
| inputs=[session_holder, problem_text, step_delay_slider], | |
| outputs=[ | |
| state_latex, | |
| radar_display, | |
| timeline_display, | |
| verifier_log, | |
| m_ir_display, | |
| human_action_dropdown, | |
| cert_display, | |
| ], | |
| ) | |
| human_step_btn.click( | |
| fn=human_step_ui, | |
| inputs=[session_holder, problem_text, human_action_dropdown, custom_arg_input], | |
| outputs=[ | |
| state_latex, | |
| radar_display, | |
| timeline_display, | |
| verifier_log, | |
| m_ir_display, | |
| human_action_dropdown, | |
| cert_display, | |
| ], | |
| ) | |
| demo.load( | |
| fn=init_game_ui, | |
| inputs=[preset_dropdown, problem_text], | |
| outputs=[ | |
| session_holder, | |
| problem_text, | |
| state_latex, | |
| radar_display, | |
| timeline_display, | |
| verifier_log, | |
| m_ir_display, | |
| human_action_dropdown, | |
| cert_display, | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(css=CUSTOM_CSS) | |