File size: 10,578 Bytes
d3527de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c22ec7
 
64d0d65
 
 
 
 
 
2c22ec7
64d0d65
 
2c22ec7
64d0d65
2c22ec7
 
 
d3527de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4591829
d3527de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4591829
d3527de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4591829
d3527de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"""
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()