InteractiveQAOATool / src /streamlit_app.py
speederracer123's picture
Update src/streamlit_app.py
64d0d65 verified
Raw
History Blame Contribute Delete
10.6 kB
"""
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 &nbsp;|&nbsp; '
'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()