Spaces:
Runtime error
Runtime error
| """ | |
| QAOA Pedagogical Tool β Main Entry Point | |
| ========================================= | |
| Run with: streamlit run app.py | |
| Navigation is driven entirely by st.session_state["page"]. | |
| The landing page renders three clickable module cards. | |
| Each module page has a "β Back to Home" button. | |
| """ | |
| import streamlit as st | |
| # ββ Page config (must be first Streamlit call) βββββββββββββββββββββββββββββββ | |
| st.set_page_config( | |
| page_title="QAOA Pedagogical Tool", | |
| page_icon="βοΈ", | |
| layout="wide", | |
| initial_sidebar_state="collapsed", | |
| ) | |
| # ββ Global CSS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown(""" | |
| <style> | |
| /* hide default Streamlit hamburger / footer */ | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| /* global typography */ | |
| .block-container {padding-top: 2rem; padding-bottom: 2rem;} | |
| /* ββ Landing card styles ββ */ | |
| .card-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1.8rem; | |
| margin-top: 2rem; | |
| } | |
| .card { | |
| border-radius: 14px; | |
| padding: 2rem 1.8rem; | |
| cursor: pointer; | |
| transition: transform 0.15s, box-shadow 0.15s; | |
| border: 2px solid transparent; | |
| } | |
| .card:hover { | |
| transform: translateY(-4px); | |
| box-shadow: 0 8px 28px rgba(0,0,0,0.12); | |
| } | |
| .card-standard { background: #EEF2FF; border-color: #6366F1; } | |
| .card-equality { background: #F5F3FF; border-color: #7C3AED; } | |
| .card-inequality{ background: #F0FDFA; border-color: #0F766E; } | |
| .card-icon { font-size: 2.8rem; margin-bottom: 0.6rem; } | |
| .card-title { font-size: 1.35rem; font-weight: 700; margin-bottom: 0.4rem; } | |
| .card-badge { | |
| display: inline-block; | |
| font-size: 0.72rem; font-weight: 600; | |
| padding: 2px 10px; border-radius: 20px; | |
| margin-bottom: 0.8rem; | |
| } | |
| .badge-std { background: #6366F1; color: white; } | |
| .badge-eq { background: #7C3AED; color: white; } | |
| .badge-ineq { background: #0F766E; color: white; } | |
| .card-desc { font-size: 0.95rem; color: #475569; line-height: 1.55; } | |
| /* math-box callout */ | |
| .math-box { | |
| background: #F8FAFC; | |
| border-left: 5px solid #6366F1; | |
| padding: 14px 18px; | |
| border-radius: 5px; | |
| margin: 1rem 0; | |
| font-family: "Georgia", serif; | |
| font-size: 1.0rem; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.markdown(""" | |
| <style> | |
| /* 1. Force the main container to hide horizontal overflow */ | |
| .main { overflow-x: hidden !important; } | |
| /* 2. Prevent Plotly from triggering the IFrame Resizer loop */ | |
| div[data-testid="stPlotlyChart"] > div { | |
| overflow: hidden !important; | |
| } | |
| /* 3. Add a tiny bottom margin to prevent the 'bottom-edge' jitter */ | |
| div[data-testid="stPlotlyChart"] { | |
| margin-bottom: 20px !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # ββ Session state initialisation ββββββββββββββββββββββββββββββββββββββββββββββ | |
| if "page" not in st.session_state: | |
| st.session_state["page"] = "home" | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # LANDING PAGE | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def render_home(): | |
| # ββ Header βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown( | |
| '<h1 style="text-align:center;color:#1E293B;font-size:2.6rem;margin-bottom:0;">' | |
| 'QAOA Pedagogical Tool</h1>', | |
| unsafe_allow_html=True, | |
| ) | |
| st.markdown( | |
| '<p style="text-align:center;color:#64748B;font-size:1.1rem;margin-top:0.4rem;">' | |
| 'An interactive step-by-step guide to the ' | |
| 'Quantum Approximate Optimization Algorithm</p>', | |
| unsafe_allow_html=True, | |
| ) | |
| # ββ Quick-reference math ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown("---") | |
| col_a, col_b, col_c = st.columns(3) | |
| with col_a: | |
| with st.container(border=True, height=120): | |
| st.markdown("**QUBO β Hamiltonian**") | |
| st.latex(r"x_i \to \frac{I - Z_i}{2}") | |
| with col_b: | |
| with st.container(border=True, height=120): | |
| st.markdown("**Cost Layer**") | |
| st.latex(r"U_C(\gamma) = e^{-i\gamma H_C}") | |
| with col_c: | |
| with st.container(border=True, height=120): | |
| st.markdown("**Mixer Layer**") | |
| st.latex(r"U_B(\beta) = e^{-i\beta \sum X_i}") | |
| # ββ Module cards βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown( | |
| '<h2 style="text-align:center;color:#1E293B;margin-top:2rem;">Choose a Module</h2>', | |
| unsafe_allow_html=True, | |
| ) | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.markdown(""" | |
| <div class="card card-standard"> | |
| <div class="card-icon">βοΈ</div> | |
| <div class="card-title" style="color:#4338CA;">Standard QAOA</div> | |
| <span class="card-badge badge-std">3 Qubits Β· Unconstrained</span> | |
| <div class="card-desc"> | |
| Start here. Learn QUBO formulation, Hamiltonian mapping, and the | |
| PhaseβProbability Disconnect from first principles. | |
| <br><br> | |
| <b>Problem:</b> Minimize C(x) = xβ + xβ β xβxβ | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("Open β", key="go_standard", use_container_width=False): | |
| st.session_state["page"] = "standard" | |
| st.rerun() | |
| with col2: | |
| st.markdown(""" | |
| <div class="card card-equality"> | |
| <div class="card-icon">π</div> | |
| <div class="card-title" style="color:#6D28D9;">Equality Constraint</div> | |
| <span class="card-badge badge-eq">3 Qubits Β· Penalty Method</span> | |
| <div class="card-desc"> | |
| Learn how to encode equality constraints as quadratic penalty terms, | |
| and see why this creates a fully-connected Kβ cost circuit. | |
| <br><br> | |
| <b>Problem:</b> Minimize C(x) = xβ + xβ β xβxβ<br> | |
| <b>Constraint:</b> xβ + xβ + xβ = 1 | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("Open β", key="go_equality", use_container_width=False): | |
| st.session_state["page"] = "equality" | |
| st.rerun() | |
| with col3: | |
| st.markdown(""" | |
| <div class="card card-inequality"> | |
| <div class="card-icon">π</div> | |
| <div class="card-title" style="color:#0F766E;">Inequality Constraint</div> | |
| <span class="card-badge badge-ineq">4 Qubits Β· Slack Variable</span> | |
| <div class="card-desc"> | |
| Advanced module. Introduce a binary slack qubit to convert an | |
| inequality into an equality, building a Kβ fully-connected circuit. | |
| <br><br> | |
| <b>Problem:</b> Minimize C(x) = xβ + xβ β xβxβ<br> | |
| <b>Constraint:</b> xβ + xβ + xβ β₯ 2 | |
| </div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if st.button("Open β", key="go_inequality", use_container_width=False): | |
| st.session_state["page"] = "inequality" | |
| st.rerun() | |
| # ββ How to use βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown("---") | |
| with st.expander("How to use this tool"): | |
| st.markdown(""" | |
| Each module walks you through **six pedagogical steps**: | |
| 1. **Problem Formulation** β binary optimization problem + truth table | |
| 2. **QUBO Matrix** β encode the cost as a matrix | |
| 3. **Hamiltonian Mapping** β translate to quantum spin operators | |
| 4. **Circuit Construction** β see how each Hamiltonian term becomes a gate; | |
| observe the **PhaseβProbability Disconnect** using sv_disc visualizations | |
| 5. **Interactive Simulation** β adjust Ξ³ and Ξ² sliders to explore the energy landscape | |
| 6. **COBYLA Optimization + Replay** β run the variational loop, then scrub through | |
| the optimization history iteration by iteration | |
| **PhaseβProbability Disconnect** (Step 4): Each disc represents one basis state. | |
| The *radius* encodes the probability amplitude; the *angle* of the pointer encodes the quantum phase. | |
| After the Cost Layer, all radii are equal (probabilities unchanged) but angles differ. | |
| After the Mixer Layer, radii change β this is where interference converts phase into probability. | |
| """) | |
| # ββ Footer ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.markdown( | |
| '<p style="text-align:center;color:#94A3B8;font-size:0.82rem;margin-top:3rem;">' | |
| 'Built with Qiskit Β· Streamlit Β· Plotly Β· sv_disc | ' | |
| 'IEEE QSEEC 2026 Submission</p>', | |
| unsafe_allow_html=True, | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # ROUTER | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def main(): | |
| page = st.session_state.get("page", "home") | |
| if page == "home": | |
| render_home() | |
| elif page == "standard": | |
| from pages.standard import render | |
| render() | |
| elif page == "equality": | |
| from pages.equality import render | |
| render() | |
| elif page == "inequality": | |
| from pages.inequality import render | |
| render() | |
| else: | |
| st.session_state["page"] = "home" | |
| st.rerun() | |
| if __name__ == "__main__": | |
| main() | |