Download server/app.py from poseidon666/quantum_circuit_optimizer: direct link, hf CLI and curl.
- Browser
- Download file 20.6 kB
-
https://huggingface.co/spaces/poseidon666/quantum_circuit_optimizer/resolve/main/server/app.py
- Command line
-
hf download hf://spaces/poseidon666/quantum_circuit_optimizer/server/app.py
-
curl -L -o app.py https://huggingface.co/spaces/poseidon666/quantum_circuit_optimizer/resolve/main/server/app.py
20.6 kB
| # -*- coding: utf-8 -*- | |
| """ | |
| Minimal FastAPI + Gradio server for the Quantum Circuit Optimization Environment. | |
| Deployment spec: | |
| - Root endpoint: GET / → health check JSON | |
| - Gradio UI: mounted at /ui | |
| - Port: 7860 (managed externally by Docker / HF Spaces) | |
| - No uvicorn.run() in this file | |
| Run locally: | |
| uvicorn server.app:app --host 0.0.0.0 --port 7860 | |
| """ | |
| import json | |
| import logging | |
| import os | |
| import sys | |
| # sys.path bootstrap so this file can be run directly | |
| _HERE = os.path.dirname(os.path.abspath(__file__)) | |
| _ROOT = os.path.dirname(_HERE) | |
| for _p in (_ROOT, _HERE): | |
| if _p not in sys.path: | |
| sys.path.insert(0, _p) | |
| from fastapi import FastAPI, Body | |
| from fastapi.responses import RedirectResponse | |
| import gradio as gr | |
| from pydantic import BaseModel | |
| from openenv.core.env_server import create_fastapi_app | |
| from models import ActionType, GateType, QuantumAction, QuantumObservation | |
| from .my_env_environment import QuantumCircuitEnvironment | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # --------------------------------------------------------------------------- | |
| # FastAPI base app - Includes OpenEnv WebSocket endpoints | |
| # --------------------------------------------------------------------------- | |
| fastapi_app = create_fastapi_app( | |
| lambda: QuantumCircuitEnvironment(seed=42), | |
| QuantumAction, | |
| QuantumObservation | |
| ) | |
| def web_redirect(): | |
| """Redirect /web to root Gradio UI.""" | |
| return RedirectResponse(url="/") | |
| def health(): | |
| return {"status": "ok", "message": "Quantum Circuit Optimizer is running"} | |
| def info(): | |
| return { | |
| "name": "Quantum Circuit Optimization Environment", | |
| "version": "1.0.0", | |
| "gradio_ui": "Available at root path /" | |
| } | |
| class ResetRequest(BaseModel): | |
| task_id: str = "easy" | |
| # Root-level endpoints for OpenEnv validation | |
| def reset_endpoint(body: dict = Body(default={})): | |
| """OpenEnv-compliant reset endpoint.""" | |
| task_id = body.get("task_id", "easy") | |
| temp_env = QuantumCircuitEnvironment(seed=42) | |
| obs = temp_env.reset(config={"task_id": task_id}) | |
| obs_dict = obs.model_dump() if hasattr(obs, "model_dump") else obs.dict() | |
| return JSONResponse(content={"observation": obs_dict}) | |
| def step_endpoint(action: QuantumAction): | |
| """OpenEnv-compliant step endpoint.""" | |
| temp_env = QuantumCircuitEnvironment(seed=42) | |
| temp_env.reset(config={"task_id": "easy"}) | |
| obs = temp_env.step(action) | |
| obs_dict = obs.model_dump() if hasattr(obs, "model_dump") else obs.dict() | |
| return JSONResponse(content={"observation": obs_dict}) | |
| def state_endpoint(): | |
| """OpenEnv-compliant state endpoint.""" | |
| temp_env = QuantumCircuitEnvironment(seed=42) | |
| temp_env.reset(config={"task_id": "easy"}) | |
| state = temp_env.state | |
| state_dict = state.model_dump() if hasattr(state, "model_dump") else state.dict() | |
| return JSONResponse(content={"state": state_dict}) | |
| # API endpoints (for programmatic access with global env) | |
| def api_reset(req: ResetRequest): | |
| """Programmatic endpoint to reset the environment.""" | |
| global current_obs, step_history | |
| current_obs = env.reset(config={"task_id": req.task_id}) | |
| step_history = [] | |
| return current_obs | |
| def api_step(action: QuantumAction): | |
| """Programmatic endpoint to step the environment.""" | |
| global current_obs | |
| current_obs = env.step(action) | |
| return current_obs | |
| def api_state(): | |
| """Programmatic endpoint to fetch the full internal state.""" | |
| return env.state | |
| # --------------------------------------------------------------------------- | |
| # Global environment instance (for Gradio UI) | |
| # --------------------------------------------------------------------------- | |
| env = QuantumCircuitEnvironment(seed=42) | |
| # Gradio State | |
| current_obs = None | |
| step_history: list = [] | |
| # --------------------------------------------------------------------------- | |
| # Helpers | |
| # --------------------------------------------------------------------------- | |
| def _format_circuit(obs) -> str: | |
| """ASCII circuit summary.""" | |
| if not obs or not obs.circuit_gates: | |
| return "(empty circuit)" | |
| lines = [] | |
| for i, g in enumerate(obs.circuit_gates): | |
| gate = g.get("gate", "?") | |
| qubits = g.get("qubits", []) | |
| param = g.get("parameter") | |
| q_str = ",".join(str(q) for q in qubits) | |
| if param is not None: | |
| lines.append(f" {i+1:>2}. {gate}({q_str}, θ={param:.4f})") | |
| else: | |
| lines.append(f" {i+1:>2}. {gate}({q_str})") | |
| return "\n".join(lines) | |
| def _format_scores(obs) -> str: | |
| """Plain-text score summary.""" | |
| if not obs: | |
| return "" | |
| meta = obs.metadata or {} | |
| fid = round(meta.get("fidelity_score", obs.fidelity), 4) | |
| eff = round(meta.get("efficiency_score", 0), 4) | |
| noi = round(meta.get("noise_score", 0), 4) | |
| con = round(meta.get("constraints_score", 0), 4) | |
| agg = round(meta.get("aggregate_score", obs.score), 4) | |
| return ( | |
| f"Fidelity: {fid}\n" | |
| f"Efficiency: {eff}\n" | |
| f"Noise: {noi}\n" | |
| f"Constraints: {con}\n" | |
| f"AGGREGATE: {agg}" | |
| ) | |
| def _status(obs, extra: str = "") -> str: | |
| if not obs: | |
| return "No environment loaded — click Reset." | |
| s = ( | |
| f"Task: {obs.task_id} | Qubits: {obs.num_qubits} | " | |
| f"Step: {obs.steps_taken}/{obs.max_steps}\n" | |
| f"Fidelity: {obs.fidelity:.4f} | Score: {obs.score:.4f} | " | |
| f"Reward: {obs.reward:+.4f}" | |
| ) | |
| if obs.done: | |
| s += f"\n>>> EPISODE COMPLETE — final score: {obs.score:.4f} <<<" | |
| if extra: | |
| s += f"\n{extra}" | |
| return s | |
| # --------------------------------------------------------------------------- | |
| # Gradio callbacks | |
| # --------------------------------------------------------------------------- | |
| TASK_MAP = { | |
| "Bell State (Easy)": "easy", | |
| "GHZ State (Medium)": "medium", | |
| "Unitary Approx (Hard)": "hard", | |
| "Imperfect but Efficient": "efficient", | |
| "Noise-Dominant": "noisy", | |
| "Budgeted Optimization": "budget", | |
| "Approximate Target": "approx", | |
| } | |
| def do_reset(task_name): | |
| global current_obs, step_history | |
| task_id = TASK_MAP.get(task_name, "easy") | |
| current_obs = env.reset(config={"task_id": task_id}) | |
| step_history = [] | |
| return ( | |
| _status(current_obs, f"Reset to: {task_name}"), | |
| _format_circuit(current_obs), | |
| _format_scores(current_obs), | |
| "", | |
| ) | |
| def do_step(action_type, gate_type, qubit_0, qubit_1, parameter): | |
| global current_obs, step_history | |
| if current_obs is None: | |
| return "ERROR: Reset the environment first!", "(no circuit)", "", "" | |
| if current_obs.done: | |
| return _status(current_obs), _format_circuit(current_obs), _format_scores(current_obs), "\n".join(step_history) | |
| try: | |
| at = ActionType(action_type) | |
| gate = None | |
| qubits = [] | |
| param = None | |
| if at == ActionType.ADD: | |
| gate = GateType(gate_type) if gate_type else None | |
| if gate in (GateType.CNOT,): | |
| qubits = [int(qubit_0), int(qubit_1)] | |
| elif gate is not None and gate.value == "SWAP": | |
| qubits = [int(qubit_0), int(qubit_1)] | |
| else: | |
| qubits = [int(qubit_0)] | |
| if gate in (GateType.RX, GateType.RZ) and parameter is not None: | |
| param = float(parameter) | |
| elif at == ActionType.SWAP: | |
| qubits = [int(qubit_0), int(qubit_1)] | |
| elif at == ActionType.PARAM: | |
| if parameter is not None: | |
| param = float(parameter) | |
| action = QuantumAction(action_type=at, gate=gate, qubits=qubits, parameter=param) | |
| current_obs = env.step(action) | |
| except Exception as exc: | |
| logger.exception("Step error: %s", exc) | |
| return ( | |
| f"ACTION ERROR: {exc}", | |
| _format_circuit(current_obs) if current_obs else "(no circuit)", | |
| _format_scores(current_obs) if current_obs else "", | |
| "\n".join(step_history), | |
| ) | |
| gate_label = "" | |
| if gate: | |
| gate_label = f" {gate_type}({','.join(str(q) for q in qubits)})" | |
| entry = ( | |
| f"Step {current_obs.steps_taken}: {action_type}{gate_label}" | |
| f" → fid={current_obs.fidelity:.4f}" | |
| f" score={current_obs.score:.4f}" | |
| f" reward={current_obs.reward:+.4f}" | |
| + (" [DONE]" if current_obs.done else "") | |
| ) | |
| step_history.append(entry) | |
| return ( | |
| _status(current_obs), | |
| _format_circuit(current_obs), | |
| _format_scores(current_obs), | |
| "\n".join(step_history), | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Gradio UI — Elegant and User-Friendly | |
| # --------------------------------------------------------------------------- | |
| TASK_DESCRIPTIONS = { | |
| "Bell State (Easy)": { | |
| "description": "Create the famous Bell state: (|00⟩ + |11⟩)/√2", | |
| "difficulty": "Easy", | |
| "qubits": 2, | |
| "noise": "None", | |
| "connectivity": "Full", | |
| "goal": "Learn basic 2-qubit entanglement with H + CNOT" | |
| }, | |
| "GHZ State (Medium)": { | |
| "description": "Build a 3-qubit GHZ state: (|000⟩ + |111⟩)/√2", | |
| "difficulty": "Medium", | |
| "qubits": 3, | |
| "noise": "Depolarizing", | |
| "connectivity": "Linear (0↔1↔2)", | |
| "goal": "Handle noise and limited connectivity" | |
| }, | |
| "Unitary Approx (Hard)": { | |
| "description": "Approximate a complex unitary: Ry(π/3) ⊗ Rz(π/4) · CNOT", | |
| "difficulty": "Hard", | |
| "qubits": 2, | |
| "noise": "Thermal", | |
| "connectivity": "Restricted", | |
| "goal": "Precise parametric gate tuning under noise" | |
| }, | |
| "Imperfect but Efficient": { | |
| "description": "GHZ state with emphasis on circuit efficiency", | |
| "difficulty": "Medium", | |
| "qubits": 3, | |
| "noise": "None", | |
| "connectivity": "Full", | |
| "goal": "Balance fidelity (60%) vs efficiency (40%)" | |
| }, | |
| "Noise-Dominant": { | |
| "description": "Bell state optimized for noise resilience", | |
| "difficulty": "Medium", | |
| "qubits": 2, | |
| "noise": "Thermal (High)", | |
| "connectivity": "Full", | |
| "goal": "Minimize gate count to survive noise (40% weight)" | |
| }, | |
| "Budgeted Optimization": { | |
| "description": "GHZ state with strict 3-gate budget", | |
| "difficulty": "Medium", | |
| "qubits": 3, | |
| "noise": "None", | |
| "connectivity": "Full", | |
| "goal": "Achieve high fidelity with minimal gates" | |
| }, | |
| "Approximate Target": { | |
| "description": "4-qubit GHZ with relaxed fidelity threshold (0.80)", | |
| "difficulty": "Medium", | |
| "qubits": 4, | |
| "noise": "None", | |
| "connectivity": "Full", | |
| "goal": "Scale to larger circuits with approximate solutions" | |
| }, | |
| } | |
| GATE_DESCRIPTIONS = { | |
| "H": "Hadamard — Creates superposition: |0⟩ → (|0⟩+|1⟩)/√2", | |
| "X": "Pauli-X — Bit flip: |0⟩ ↔ |1⟩", | |
| "CNOT": "Controlled-NOT — Entangles 2 qubits (requires qubit 0 & 1)", | |
| "RX": "X-Rotation — Rotate around X-axis by angle θ (needs parameter)", | |
| "RZ": "Z-Rotation — Rotate around Z-axis by angle θ (needs parameter)", | |
| } | |
| ACTION_DESCRIPTIONS = { | |
| "ADD": "Add a quantum gate to the circuit", | |
| "REMOVE": "Remove the last gate from the circuit", | |
| "SWAP": "Swap two qubits (requires qubit 0 & 1)", | |
| "PARAM": "Tune the parameter of the last parametric gate (RX/RZ)", | |
| "STOP": "End the episode and finalize the circuit", | |
| } | |
| def create_gradio_app() -> gr.Blocks: | |
| with gr.Blocks(title="Quantum Circuit Optimizer", theme=gr.themes.Soft()) as demo: | |
| # Header | |
| gr.Markdown( | |
| """ | |
| # Quantum Circuit Optimizer | |
| ### Build quantum circuits step-by-step to maximize fidelity with target states | |
| **Goal:** Design circuits that are accurate, efficient, noise-resistant, and hardware-compliant. | |
| """ | |
| ) | |
| # Task Selection Section | |
| with gr.Accordion("Available Tasks & Challenges", open=False): | |
| gr.Markdown( | |
| """ | |
| Choose a task below to start optimizing. Each task has different constraints: | |
| | Task | Difficulty | Qubits | Noise | Connectivity | Focus | | |
| |------|------------|--------|-------|--------------|-------| | |
| | **Bell State** | Easy | 2 | None | Full | Basic entanglement | | |
| | **GHZ State** | Medium | 3 | Depolarizing | Linear | Noise + topology | | |
| | **Unitary Approx** | Hard | 2 | Thermal | Restricted | Parametric tuning | | |
| | **Efficient** | Medium | 3 | None | Full | Minimize depth/gates | | |
| | **Noise-Dominant** | Medium | 2 | High thermal | Full | Noise resilience | | |
| | **Budgeted** | Medium | 3 | None | Full | 3-gate limit | | |
| | **Approximate** | Medium | 4 | None | Full | Scale to 4 qubits | | |
| """ | |
| ) | |
| with gr.Accordion("Quick Start Guide", open=False): | |
| gr.Markdown( | |
| """ | |
| ### How to Use: | |
| 1. **Select a Task** — Choose from 7 quantum optimization challenges | |
| 2. **Reset Environment** — Initialize the task and clear the circuit | |
| 3. **Build Your Circuit** — Add gates step-by-step: | |
| - Use **H** and **CNOT** for entanglement | |
| - Use **RX/RZ** for parametric rotations (set angle in radians) | |
| - Use **SWAP** to rearrange qubits for connectivity | |
| 4. **Monitor Scores** — Watch fidelity, efficiency, noise, and constraints | |
| 5. **Stop** — End the episode when satisfied | |
| ### Scoring: | |
| - **Fidelity** (40-60%): How close to the target state | |
| - **Efficiency** (20%): Circuit depth and gate count | |
| - **Noise** (15-25%): Resilience to quantum errors | |
| - **Constraints** (10-15%): Hardware connectivity compliance | |
| """ | |
| ) | |
| gr.Markdown("---") | |
| # Step 1: Task Selection | |
| gr.Markdown("### Step 1: Select Task") | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| task_dd = gr.Dropdown( | |
| choices=list(TASK_MAP.keys()), | |
| value="Bell State (Easy)", | |
| label="Choose a Quantum Task", | |
| info="Select the optimization challenge you want to solve" | |
| ) | |
| with gr.Column(scale=1): | |
| reset_btn = gr.Button("Reset Environment", variant="primary", size="lg") | |
| # Task info display | |
| task_info = gr.Markdown("") | |
| def update_task_info(task_name): | |
| info = TASK_DESCRIPTIONS.get(task_name, {}) | |
| return f""" | |
| **{info.get('difficulty', '')}** | {info.get('qubits', 0)} qubits | Noise: {info.get('noise', 'N/A')} | Connectivity: {info.get('connectivity', 'N/A')} | |
| {info.get('description', '')} | |
| **Goal:** {info.get('goal', '')} | |
| """ | |
| task_dd.change(fn=update_task_info, inputs=[task_dd], outputs=[task_info]) | |
| gr.Markdown("---") | |
| # Step 2: Circuit Building | |
| gr.Markdown("### Step 2: Build Your Circuit") | |
| with gr.Row(): | |
| with gr.Column(): | |
| action_dd = gr.Dropdown( | |
| choices=["ADD", "REMOVE", "SWAP", "PARAM", "STOP"], | |
| value="ADD", | |
| label="Action Type", | |
| info="What operation do you want to perform?" | |
| ) | |
| action_help = gr.Markdown(ACTION_DESCRIPTIONS["ADD"]) | |
| action_dd.change(fn=lambda x: ACTION_DESCRIPTIONS.get(x, ""), inputs=[action_dd], outputs=[action_help]) | |
| with gr.Column(): | |
| gate_dd = gr.Dropdown( | |
| choices=["H", "X", "CNOT", "RX", "RZ"], | |
| value="H", | |
| label="Gate Type (for ADD action)", | |
| info="Which quantum gate to add?" | |
| ) | |
| gate_help = gr.Markdown(GATE_DESCRIPTIONS["H"]) | |
| gate_dd.change(fn=lambda x: GATE_DESCRIPTIONS.get(x, ""), inputs=[gate_dd], outputs=[gate_help]) | |
| with gr.Row(): | |
| qubit_0 = gr.Number( | |
| value=0, | |
| label="Qubit 0", | |
| info="Target qubit (or first qubit for 2-qubit gates)", | |
| precision=0 | |
| ) | |
| qubit_1 = gr.Number( | |
| value=1, | |
| label="Qubit 1", | |
| info="Second qubit (for CNOT/SWAP only)", | |
| precision=0 | |
| ) | |
| parameter = gr.Slider( | |
| minimum=-3.14159, | |
| maximum=3.14159, | |
| value=0, | |
| step=0.01, | |
| label="Parameter (θ in radians)", | |
| info="Rotation angle for RX/RZ gates" | |
| ) | |
| with gr.Row(): | |
| step_btn = gr.Button("Execute Step", variant="primary", size="lg", scale=2) | |
| state_btn = gr.Button("Get Full State", variant="secondary", scale=1) | |
| gr.Markdown("---") | |
| # Step 3: Monitor Progress | |
| gr.Markdown("### Step 3: Monitor Your Progress") | |
| with gr.Row(): | |
| with gr.Column(): | |
| status_out = gr.Textbox( | |
| label="Current Status", | |
| lines=4, | |
| interactive=False, | |
| placeholder="Click 'Reset Environment' to start..." | |
| ) | |
| scores_out = gr.Textbox( | |
| label="Score Breakdown", | |
| lines=6, | |
| interactive=False | |
| ) | |
| with gr.Column(): | |
| circuit_out = gr.Textbox( | |
| label="Current Circuit", | |
| lines=10, | |
| interactive=False, | |
| placeholder="Your circuit will appear here..." | |
| ) | |
| history_out = gr.Textbox( | |
| label="Step History", | |
| lines=8, | |
| interactive=False, | |
| placeholder="Action history will be logged here..." | |
| ) | |
| # Advanced: Full State | |
| with gr.Accordion("Advanced: Full Environment State", open=False): | |
| state_out = gr.JSON(label="Complete State Dump") | |
| # Footer | |
| gr.Markdown( | |
| """ | |
| --- | |
| **Tips:** | |
| - Start simple: H on qubit 0, then CNOT(0,1) creates a Bell state | |
| - Watch the fidelity score — aim for >0.90 on easy tasks | |
| - Fewer gates = better efficiency and noise scores | |
| - Use REMOVE to undo mistakes | |
| - Use PARAM to fine-tune RX/RZ angles | |
| """ | |
| ) | |
| # Event Handlers | |
| OUTS = [status_out, circuit_out, scores_out, history_out] | |
| reset_btn.click(fn=do_reset, inputs=[task_dd], outputs=OUTS) | |
| step_btn.click(fn=do_step, inputs=[action_dd, gate_dd, qubit_0, qubit_1, parameter], outputs=OUTS) | |
| def fetch_state(): | |
| s = env.state | |
| return s.dict() if hasattr(s, "dict") else s | |
| state_btn.click(fn=fetch_state, inputs=[], outputs=[state_out]) | |
| # Initialize task info on load | |
| demo.load(fn=update_task_info, inputs=[task_dd], outputs=[task_info]) | |
| return demo | |
| # --------------------------------------------------------------------------- | |
| # Create Gradio app and mount to FastAPI | |
| # --------------------------------------------------------------------------- | |
| demo = create_gradio_app() | |
| # Mount Gradio at root | |
| app = gr.mount_gradio_app(fastapi_app, demo, path="/") | |
| # --------------------------------------------------------------------------- | |
| # Entry Point | |
| # --------------------------------------------------------------------------- | |
| def main(): | |
| """Entry point for 'uv run server' command.""" | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=7860) | |
| if __name__ == "__main__": | |
| main() | |