"""
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("""
""", unsafe_allow_html=True)
st.markdown("""
""", 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(
'
'
'QAOA Pedagogical Tool
',
unsafe_allow_html=True,
)
st.markdown(
''
'An interactive step-by-step guide to the '
'Quantum Approximate Optimization Algorithm
',
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(
'Choose a Module
',
unsafe_allow_html=True,
)
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("""
⚛️
Standard QAOA
3 Qubits · Unconstrained
Start here. Learn QUBO formulation, Hamiltonian mapping, and the
Phase–Probability Disconnect from first principles.
Problem: Minimize C(x) = x₁ + x₂ − x₂x₃
""", 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("""
🔒
Equality Constraint
3 Qubits · Penalty Method
Learn how to encode equality constraints as quadratic penalty terms,
and see why this creates a fully-connected K₃ cost circuit.
Problem: Minimize C(x) = x₁ + x₂ − x₂x₃
Constraint: x₁ + x₂ + x₃ = 1
""", 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("""
📐
Inequality Constraint
4 Qubits · Slack Variable
Advanced module. Introduce a binary slack qubit to convert an
inequality into an equality, building a K₄ fully-connected circuit.
Problem: Minimize C(x) = x₁ + x₂ − x₂x₃
Constraint: x₁ + x₂ + x₃ ≥ 2
""", 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(
''
'Built with Qiskit · Streamlit · Plotly · sv_disc | '
'IEEE QSEEC 2026 Submission
',
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()