-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathapp.py
More file actions
351 lines (301 loc) · 15 KB
/
app.py
File metadata and controls
351 lines (301 loc) · 15 KB
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
import streamlit as st
import numpy as np
import plotly.graph_objects as go
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector, DensityMatrix, partial_trace
st.set_page_config(page_title="Quantum Playground", layout="wide", page_icon="⚛️")
# ----------------- Utilities -------------------------
def run_counts(qc, shots=1024):
sim = AerSimulator()
qc2 = qc.copy()
qc2.measure_all()
job = sim.run(qc2, shots=shots)
res = job.result()
return res.get_counts()
def statevector_from_circuit(qc):
return Statevector.from_instruction(qc)
def bloch_vector_from_statevector(sv, qubit_index=0, num_qubits=None):
if num_qubits is None:
num_qubits = int(np.log2(len(sv.data)))
rho = DensityMatrix(sv)
if num_qubits > 1:
reduced = partial_trace(rho, [i for i in range(num_qubits) if i != qubit_index])
dm = reduced.data
else:
dm = rho.data
X = np.array([[0,1],[1,0]], dtype=complex)
Y = np.array([[0,-1j],[1j,0]], dtype=complex)
Z = np.array([[1,0],[0,-1]], dtype=complex)
vx = np.real(np.trace(dm @ X))
vy = np.real(np.trace(dm @ Y))
vz = np.real(np.trace(dm @ Z))
return np.array([vx, vy, vz])
def plot_bloch_plotly(vec, title="Bloch vector"):
u = np.linspace(0, 2*np.pi, 60)
v = np.linspace(0, np.pi, 30)
x = np.outer(np.cos(u), np.sin(v))
y = np.outer(np.sin(u), np.sin(v))
z = np.outer(np.ones_like(u), np.cos(v))
fig = go.Figure()
fig.add_trace(go.Surface(x=x, y=y, z=z, opacity=0.2, showscale=False, colorscale="Blues", hoverinfo="skip"))
axis_len = 1.2
fig.add_trace(go.Scatter3d(x=[0, axis_len], y=[0,0], z=[0,0], mode="lines", name="X"))
fig.add_trace(go.Scatter3d(x=[0,0], y=[0, axis_len], z=[0,0], mode="lines", name="Y"))
fig.add_trace(go.Scatter3d(x=[0,0], y=[0,0], z=[0, axis_len], mode="lines", name="Z"))
# Use the actual Bloch vector components (including magnitude) so the cone length
# reflects purity/decoherence. If vector is effectively zero, skip the cone to
# avoid rendering problems.
if np.linalg.norm(vec) > 1e-6:
# sizemode='scaled' makes the cone length proportional to the vector magnitude
fig.add_trace(go.Cone(x=[0], y=[0], z=[0], u=[vec[0]], v=[vec[1]], w=[vec[2]],
sizemode="scaled", sizeref=0.8, anchor="tail", showscale=False, colorscale="Viridis"))
else:
# tiny invisible marker so the scene still renders nicely
fig.add_trace(go.Scatter3d(x=[0], y=[0], z=[0], mode='markers', marker=dict(size=2, color='rgba(0,0,0,0)'), showlegend=False))
fig.update_layout(scene=dict(xaxis=dict(range=[-1.2,1.2]),
yaxis=dict(range=[-1.2,1.2]),
zaxis=dict(range=[-1.2,1.2])),
margin=dict(l=0,r=0,t=30,b=0), title=title, height=500)
return fig
def clickable_explanation(word, explanation):
st.markdown(f"**{word}** :arrow_right:", unsafe_allow_html=True)
with st.expander(f"What is {word}?"):
st.write(explanation)
# ----------------- Sections -------------------------
#we did ittttt
# -1. Welcome / Intro Page
def section_intro():
st.markdown("## Introduction & Team")
st.subheader("About Our Project")
st.write("""
Our goal is to make **quantum computing** feel **approachable and exciting** — not intimidating.
Quantum physics can sound complex, but beneath the math lies a world of elegant, visual, and understandable ideas.
This app is designed to **teach the core concepts** — superposition, entanglement, interference, and decoherence —
through **interactive simulations** and **visual explanations** built with **Qiskit**, **Plotly**, and **Streamlit**.
""")
st.markdown("---")
st.subheader("Meet the Creators")
st.write("We’re three high school students passionate about physics, programming, and making science accessible.")
# Add image upload or static display
col1, col2, col3 = st.columns(3)
with col1:
st.image("https://i.imgur.com/YjebwnP.jpeg", caption="Nathan DeMoss, Sophomore at Cary Academy", use_container_width=True)
with col2:
st.image("https://i.imgur.com/DjhYnge.jpeg", caption="Michael Wei, Sophomore at Cary Academy", use_container_width=True)
with col3:
st.image("https://i.imgur.com/QUIysQK.jpeg", caption="Ved Vainateya, Sophomore at Cary Academy", use_container_width=True)
st.markdown("---")
st.subheader("How to Use This App")
st.write("""
- Use the **sidebar** on the left to navigate sections.
- Start from the **top** and move downward — each section builds on the last.
- Adjust **sliders**, **dropdowns**, and **buttons** to explore quantum effects interactively.
- Each **concept** has a small ▶ **expander** where you can click to learn more.
- Graphs and Bloch spheres are **live visualizations** — experiment freely!
""")
st.markdown("---")
st.markdown("### Our Mission")
st.write("""
We believe anyone can understand quantum computing with the right approach.
By blending **interactivity**, **visual intuition**, and **clear explanations**,
we aim to inspire curiosity and make the quantum world less mysterious — one qubit at a time.
""")
# 0. The Tiny World
def section_0_tiny_world():
st.markdown("### 0. The Tiny World")
st.write("Atoms are composed of **protons**, **neutrons**, and **electrons**. Electrons behave as **probability clouds**, not fixed particles.")
clickable_explanation("Electron", "Negatively charged particle; behaves like a cloud in quantum mechanics.")
clickable_explanation("Probability cloud", "Represents the likelihood of an electron's position; denser = more probable.")
n_points = st.slider("Number of scatter points", 1000, 20000, 4000, step=1000)
scale = st.slider("Radial scale (a.u.)", 0.5, 4.0, 1.0, step=0.1)
u = np.random.rand(n_points)
r = -0.5 * scale * np.log(1 - u + 1e-12)
theta = np.arccos(1 - 2*np.random.rand(n_points))
phi = 2*np.pi*np.random.rand(n_points)
x = r*np.sin(theta)*np.cos(phi)
y = r*np.sin(theta)*np.sin(phi)
z = r*np.cos(theta)
points = np.vstack([x,y,z]).T
values = np.exp(-2*r/scale)
fig = go.Figure(data=go.Scatter3d(
x=points[:,0], y=points[:,1], z=points[:,2],
mode='markers',
marker=dict(size=3, color=values, colorscale="Viridis", opacity=0.8, showscale=True)
))
fig.update_layout(margin=dict(l=0,r=0,t=30,b=0), title="Electron cloud (1s approx)", height=550)
st.plotly_chart(fig, use_container_width=True)
st.markdown("**Explanation:** Darker regions = higher probability. Demonstrates **superposition in space**.")
# 1. The Classical World
def section_1_classical_world():
st.markdown("### 1. The Classical World")
st.write("Classical bits are either 0 or 1. Logic gates combine bits deterministically.")
clickable_explanation("Bit", "Binary variable: 0 or 1.")
clickable_explanation("Logic gate", "Basic operation on bits like AND, OR, XOR.")
a, b = st.columns(2)
bit_a = a.radio("Bit A", [0,1], index=0, horizontal=True)
bit_b = b.radio("Bit B", [0,1], index=0, horizontal=True)
st.markdown(f"**Values:** A={bit_a}, B={bit_b}")
col1, col2, col3 = st.columns(3)
col1.metric("A AND B", bit_a & bit_b)
col2.metric("A OR B", bit_a | bit_b)
col3.metric("A XOR B", bit_a ^ bit_b)
st.markdown("**Quantum connection:** Classical bits are definite, unlike qubits in **superposition**.")
# 2. Enter the Quantum
def section_2_enter_quantum():
st.markdown("### 2. Enter the Quantum")
st.write("Qubits can exist in **superposition**, meaning they are simultaneously 0 and 1 until measured.")
clickable_explanation("Qubit", "The quantum analogue of a classical bit.")
clickable_explanation("Superposition", "A qubit exists in a combination of |0> and |1>.")
clickable_explanation("Statevector", "Represents amplitudes of all qubit states.")
gate = st.selectbox("Gate to apply", ["Hadamard (H)", "RX", "RY", "RZ", "X"])
angle = 0.0
if gate in ("RX","RY","RZ"):
angle = st.slider("Angle (radians)", 0.0, 2*np.pi, np.pi/2, 0.01)
qc = QuantumCircuit(1)
if gate=="Hadamard (H)": qc.h(0)
elif gate=="X": qc.x(0)
elif gate=="RX": qc.rx(angle,0)
elif gate=="RY": qc.ry(angle,0)
elif gate=="RZ": qc.rz(angle,0)
st.subheader("Statevector amplitudes")
sv = statevector_from_circuit(qc)
st.code(np.round(sv.data,4).tolist())
st.plotly_chart(plot_bloch_plotly(bloch_vector_from_statevector(sv)))
st.markdown("""
**Bloch sphere notes:**
- Axes X, Y, Z are shown.
- Arrow shows qubit state vector.
- Arrow not along Z-axis → qubit in superposition.
""")
counts = run_counts(qc)
fig = go.Figure([go.Bar(x=list(counts.keys()), y=list(counts.values()), marker_color='indigo')])
fig.update_layout(title="Measurement Probabilities", yaxis_title="Counts")
st.plotly_chart(fig)
st.markdown("**Histogram notes:** Peaks correspond to probability of measuring 0 or 1.")
# 3. Spooky Connections
def section_3_spooky_connections():
st.markdown("### 3. Spooky Connections")
st.write("Entanglement: two qubits share a state; measuring one affects the other.")
clickable_explanation("Entanglement", "Correlation between qubits; measuring one affects the other.")
clickable_explanation("Bell pair", "Maximally entangled two-qubit state.")
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0,1)
sv = statevector_from_circuit(qc)
st.subheader("Statevector amplitudes")
st.code(np.round(sv.data,4).tolist())
st.plotly_chart(plot_bloch_plotly(bloch_vector_from_statevector(sv,0,2)))
st.markdown("**Qubit 0 arrow:** Shows reduced density matrix. Entanglement correlations affect probabilities.")
st.plotly_chart(plot_bloch_plotly(bloch_vector_from_statevector(sv,1,2)))
st.markdown("**Qubit 1 arrow:** Similar effect; shows local qubit state influenced by entanglement.")
counts = run_counts(qc)
fig = go.Figure([go.Bar(x=list(counts.keys()), y=list(counts.values()), marker_color='teal')])
fig.update_layout(title="Entangled Qubits Measurement", yaxis_title="Counts")
st.plotly_chart(fig)
# 4. The Quantum Trick
def section_4_interference():
st.markdown("### 4. The Quantum Trick")
st.write("Interference allows amplitudes to add or cancel, affecting probabilities.")
clickable_explanation("Interference", "Quantum amplitudes combine constructively or destructively.")
phase = st.slider("Phase shift (radians)", 0.0, 2*np.pi, np.pi/2, 0.01)
qc = QuantumCircuit(1)
qc.h(0)
qc.rz(phase,0)
qc.h(0)
sv = statevector_from_circuit(qc)
st.subheader("Statevector amplitudes")
st.code(np.round(sv.data,4).tolist())
counts = run_counts(qc)
fig = go.Figure([go.Bar(x=list(counts.keys()), y=list(counts.values()), marker_color='orange')])
fig.update_layout(title="Interference Probabilities", yaxis_title="Counts")
st.plotly_chart(fig)
st.markdown("""
**Histogram notes:**
- Peaks show constructive interference.
- Valleys show destructive interference.
- Adjust phase slider to see which outcome is amplified.
""")
# 5. Quantum in Action
def section_5_quantum_action():
st.markdown("### 5. Quantum in Action")
st.write("Superposition + interference + entanglement → quantum algorithms.")
clickable_explanation("Grover's algorithm", "Quantum search faster than classical brute force.")
qc = QuantumCircuit(2)
qc.h([0,1])
qc.cz(0,1)
qc.h([0,1])
sv = statevector_from_circuit(qc)
st.subheader("Statevector amplitudes")
st.code(np.round(sv.data,4).tolist())
counts = run_counts(qc)
fig = go.Figure([go.Bar(x=list(counts.keys()), y=list(counts.values()), marker_color='purple')])
fig.update_layout(title="Algorithm Measurement", yaxis_title="Counts")
st.plotly_chart(fig)
st.markdown("""
**Histogram notes:**
- Shows probabilities after interference + entanglement.
- Peaks correspond to “amplified correct answers.”
- Demonstrates how quantum algorithms exploit superposition and interference.
""")
# 6. Quantum in the Real World
def section_6_real_world():
st.markdown("### 6. Quantum in the Real World")
st.write("Real qubits suffer **decoherence**, losing information over time.")
clickable_explanation("Decoherence", "Loss of quantum information due to environment.")
decoherence_strength = st.slider("Decoherence factor", 0.0, 1.0, 0.3, 0.01)
qc = QuantumCircuit(1)
qc.h(0)
sv = statevector_from_circuit(qc)
vec_deco = bloch_vector_from_statevector(sv) * (1 - decoherence_strength)
st.plotly_chart(plot_bloch_plotly(vec_deco), use_container_width=True)
st.markdown(f"""
**Decoherence notes:**
- Arrow shrinks as decoherence increases (loss of purity).
- Direction shows average state.
""")
# 7. Applications & Wrap-up
def section_7_applications():
st.markdown("### 7. Quantum Computing Applications & Wrap-up")
st.write("""
Quantum computing is real and growing. Key applications:
- **Optimization**: logistics, scheduling.
- **Chemistry & Materials**: simulate molecules.
- **Cryptography**: secure encryption or code-breaking.
- **Machine Learning**: quantum-enhanced pattern recognition.
""")
clickable_explanation("Superconducting qubits", "Used by IBM/Google; circuits cooled near absolute zero.")
clickable_explanation("Trapped ions", "Used by IonQ; ions held with lasers.")
clickable_explanation("Quantum algorithm", "Operations exploiting superposition, entanglement, interference.")
st.write("**Quantum Computers (Illustrative images):**")
col1, col2 = st.columns(2)
col1.image("https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/IBM_Q_system_%28Fraunhofer_2%29.jpg/500px-IBM_Q_system_%28Fraunhofer_2%29.jpg", caption="IBM Quantum Computer")
col2.image("https://upload.wikimedia.org/wikipedia/commons/thumb/1/1c/Quantum_Computing%3B_Ion_Trapping_%285941055642%29.jpg/500px-Quantum_Computing%3B_Ion_Trapping_%285941055642%29.jpg", caption="IonQ Trapped Ion Computer")
st.write("""
**Key Takeaways:**
- Qubits in **superposition**.
- **Entanglement** links qubits.
- **Interference** amplifies correct answers.
- **Decoherence** challenges real systems.
- Quantum computers are being built today for real-world applications.
""")
# ----------------- Routing -------------------------
sections = [
("Introduction", section_intro),
("0. The Tiny World", section_0_tiny_world),
("1. The Classical World", section_1_classical_world),
("2. Enter the Quantum", section_2_enter_quantum),
("3. Spooky Connections", section_3_spooky_connections),
("4. The Quantum Trick", section_4_interference),
("5. Quantum in Action", section_5_quantum_action),
("6. Quantum in the Real World", section_6_real_world),
("7. Applications & Wrap-up", section_7_applications)
]
st.sidebar.title("Navigate Sections")
choice = st.sidebar.radio("Section", [s[0] for s in sections])
for name, fn in sections:
if name == choice:
fn()
st.divider()
break
st.caption("Quantum Playground")