hchevva commited on
Commit
a337e1e
·
verified ·
1 Parent(s): 153bdc6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -113
app.py CHANGED
@@ -6,7 +6,6 @@ from quread.engine import QuantumStateVector
6
  from quread.exporters import to_openqasm2, to_qiskit, to_cirq
7
  from quread.llm_explain import explain_circuit_with_hf, ExplainConfig
8
 
9
-
10
  # ---------- Helpers ----------
11
  def _new_sim(n_qubits: int):
12
  qc = QuantumStateVector(int(n_qubits))
@@ -14,7 +13,6 @@ def _new_sim(n_qubits: int):
14
  selected_gate = "H"
15
  return qc, last_counts, selected_gate
16
 
17
-
18
  def _probs_top(qc, n_qubits: int, k: int = 6):
19
  probs = (np.abs(qc.state) ** 2)
20
  top = sorted(
@@ -24,79 +22,62 @@ def _probs_top(qc, n_qubits: int, k: int = 6):
24
  )[:k]
25
  return top
26
 
27
-
28
  def _counts_str(counts):
29
  if not counts:
30
- return "No counts yet. Click **Sample shots**."
31
  return "\n".join([f"{k}: {v}" for k, v in counts.items()])
32
 
 
 
33
 
34
- def _ket_str(qc, max_terms: int = 16):
35
- return qc.ket_notation(max_terms=max_terms)
 
36
 
 
 
 
 
37
 
38
- # ---------- Actions ----------
39
  def init_or_reset(n_qubits):
40
  qc, last_counts, selected_gate = _new_sim(n_qubits)
41
  return qc, last_counts, selected_gate, "✅ Reset done."
42
 
43
-
44
- def set_gate(gate, selected_gate):
45
  return gate, f"✅ Selected gate: {gate}"
46
 
47
-
48
- def apply_selected_gate(qc, last_counts, selected_gate, target, n_qubits):
49
  qc.apply_single(selected_gate, target=int(target))
50
  last_counts = None
51
  return qc, last_counts, f"✅ Applied {selected_gate} on q{target}."
52
 
53
-
54
- def apply_cnot(qc, last_counts, control, target, n_qubits):
55
  if int(control) == int(target):
56
  return qc, last_counts, "❌ Control and target must be different."
57
  qc.apply_cnot(control=int(control), target=int(target))
58
  last_counts = None
59
  return qc, last_counts, f"✅ Applied CNOT (q{control} -> q{target})."
60
 
61
-
62
  def sample_shots(qc, shots):
63
  last_counts = qc.sample(shots=int(shots))
64
  return last_counts, "✅ Sampled shots."
65
 
66
-
67
  def measure_collapse(qc, shots):
68
  res = qc.measure_collapse()
69
  last_counts = qc.sample(shots=int(shots))
70
  return last_counts, f"✅ Collapsed to |{res}⟩ and sampled shots."
71
 
72
-
73
- def update_views(qc, last_counts, n_qubits):
74
- ket = _ket_str(qc, max_terms=16)
75
- probs_top = _probs_top(qc, n_qubits, k=6)
76
-
77
- qasm = to_openqasm2(qc.history, n_qubits=int(n_qubits))
78
- qiskit = to_qiskit(qc.history, n_qubits=int(n_qubits))
79
- cirq = to_cirq(qc.history, n_qubits=int(n_qubits))
80
-
81
- counts_text = _counts_str(last_counts)
82
-
83
- return ket, counts_text, qasm, qiskit, cirq, probs_top
84
-
85
-
86
  def explain_llm(qc, n_qubits, shots, hf_model_id):
87
- # Use the same state/prob logic you already implemented in Streamlit
88
  state_ket = qc.ket_notation(max_terms=6)
89
  probs_top = _probs_top(qc, int(n_qubits), k=6)
90
 
91
  cfg = ExplainConfig(
92
- # IMPORTANT: model id must be like "google/flan-t5-large" (no extra prefix)
93
- # We'll sanitize in llm_explain if you implemented that.
94
- model_id=hf_model_id.strip(),
95
  max_new_tokens=280,
96
  temperature=0.2,
97
  )
98
 
99
- explanation = explain_circuit_with_hf(
100
  n_qubits=int(n_qubits),
101
  history=qc.history,
102
  state_ket=state_ket,
@@ -104,84 +85,129 @@ def explain_llm(qc, n_qubits, shots, hf_model_id):
104
  shots=int(shots),
105
  cfg=cfg,
106
  )
107
- return explanation
108
-
109
 
110
- # ---------- UI ----------
111
- with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
112
- gr.Markdown("# Quread.ai — State Vector Studio (Dev Tool Edition)")
113
- gr.Markdown("Build circuits, inspect the statevector, export to QASM/Qiskit/Cirq, and get grounded explanations.")
 
 
 
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  qc_state = gr.State()
116
  last_counts_state = gr.State()
117
  selected_gate_state = gr.State()
118
 
119
  with gr.Row():
120
- n_qubits = gr.Slider(1, 10, value=2, step=1, label="Number of qubits")
121
- shots = gr.Slider(128, 8192, value=1024, step=128, label="Shots (for sampling)")
122
-
123
- with gr.Row():
124
- hf_model_id = gr.Textbox(value="google/flan-t5-large", label="HF model repo id for Explain")
125
- status = gr.Markdown("")
126
-
127
- reset_btn = gr.Button("Reset Simulator")
128
-
129
- with gr.Row():
130
- gate_btns = []
131
- for g in ["H", "X", "Y", "Z", "S", "T"]:
132
- gate_btns.append(gr.Button(g))
133
- target = gr.Dropdown(choices=[0, 1], value=0, label="Target qubit")
134
-
135
- with gr.Row():
136
- apply_gate_btn = gr.Button("Apply Selected Gate")
137
- control = gr.Dropdown(choices=[0, 1], value=0, label="CNOT Control")
138
- cnot_target = gr.Dropdown(choices=[0, 1], value=1, label="CNOT Target")
139
- apply_cnot_btn = gr.Button("Apply CNOT")
140
-
141
- with gr.Row():
142
- sample_btn = gr.Button("Sample shots")
143
- measure_btn = gr.Button("Measure + Collapse")
144
- explain_btn = gr.Button("Explain (LLM)")
145
-
146
- with gr.Row():
147
- ket_out = gr.Code(label="Statevector (ket notation)", language="python")
148
- counts_out = gr.Textbox(label="Counts", lines=10)
149
-
150
- with gr.Row():
151
- qasm_out = gr.Textbox(label="OpenQASM 2.0", lines=10)
152
- qiskit_out = gr.Textbox(label="Qiskit code", lines=10)
153
- cirq_out = gr.Textbox(label="Cirq code", lines=10)
154
-
155
- probs_out = gr.Dataframe(headers=["bitstring", "prob"], label="Top probabilities")
156
- llm_out = gr.Markdown()
157
-
158
- def _refresh_dropdowns(n):
159
- opts = list(range(int(n)))
160
- return (
161
- gr.Dropdown(choices=opts, value=0), # target
162
- gr.Dropdown(choices=opts, value=0), # control
163
- gr.Dropdown(choices=opts, value=min(1, int(n)-1)), # cnot target
164
- )
165
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  def _init_all(n):
167
  qc, last_counts, selected_gate = _new_sim(n)
168
  return qc, last_counts, selected_gate
169
 
170
- # Initialize once
171
- demo.load(
172
- fn=_init_all,
173
- inputs=[n_qubits],
174
- outputs=[qc_state, last_counts_state, selected_gate_state],
175
- )
176
 
177
- # Keep dropdowns in sync with n_qubits
178
- n_qubits.change(
179
- fn=_refresh_dropdowns,
180
- inputs=[n_qubits],
181
- outputs=[target, control, cnot_target],
182
- )
183
 
184
- # Reset
185
  reset_btn.click(
186
  fn=init_or_reset,
187
  inputs=[n_qubits],
@@ -192,18 +218,18 @@ with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
192
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
193
  )
194
 
195
- # Gate selection buttons
196
- for btn, g in zip(gate_btns, ["H", "X", "Y", "Z", "S", "T"]):
197
- btn.click(
198
- fn=lambda _, cur, gg=g: set_gate(gg, cur),
199
- inputs=[btn, selected_gate_state],
200
- outputs=[selected_gate_state, status],
201
- )
202
 
203
- # Apply selected gate
204
  apply_gate_btn.click(
205
  fn=apply_selected_gate,
206
- inputs=[qc_state, last_counts_state, selected_gate_state, target, n_qubits],
207
  outputs=[qc_state, last_counts_state, status],
208
  ).then(
209
  fn=update_views,
@@ -211,10 +237,10 @@ with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
211
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
212
  )
213
 
214
- # Apply CNOT
215
  apply_cnot_btn.click(
216
  fn=apply_cnot,
217
- inputs=[qc_state, last_counts_state, control, cnot_target, n_qubits],
218
  outputs=[qc_state, last_counts_state, status],
219
  ).then(
220
  fn=update_views,
@@ -222,7 +248,7 @@ with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
222
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
223
  )
224
 
225
- # Sample shots
226
  sample_btn.click(
227
  fn=sample_shots,
228
  inputs=[qc_state, shots],
@@ -233,7 +259,7 @@ with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
233
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
234
  )
235
 
236
- # Measure + collapse
237
  measure_btn.click(
238
  fn=measure_collapse,
239
  inputs=[qc_state, shots],
@@ -244,7 +270,7 @@ with gr.Blocks(title="Quread.ai — State Vector Studio (Gradio)") as demo:
244
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
245
  )
246
 
247
- # Explain
248
  explain_btn.click(
249
  fn=explain_llm,
250
  inputs=[qc_state, n_qubits, shots, hf_model_id],
 
6
  from quread.exporters import to_openqasm2, to_qiskit, to_cirq
7
  from quread.llm_explain import explain_circuit_with_hf, ExplainConfig
8
 
 
9
  # ---------- Helpers ----------
10
  def _new_sim(n_qubits: int):
11
  qc = QuantumStateVector(int(n_qubits))
 
13
  selected_gate = "H"
14
  return qc, last_counts, selected_gate
15
 
 
16
  def _probs_top(qc, n_qubits: int, k: int = 6):
17
  probs = (np.abs(qc.state) ** 2)
18
  top = sorted(
 
22
  )[:k]
23
  return top
24
 
 
25
  def _counts_str(counts):
26
  if not counts:
27
+ return ""
28
  return "\n".join([f"{k}: {v}" for k, v in counts.items()])
29
 
30
+ def update_views(qc, last_counts, n_qubits):
31
+ ket = qc.ket_notation(max_terms=16)
32
 
33
+ qasm = to_openqasm2(qc.history, n_qubits=int(n_qubits))
34
+ qiskit = to_qiskit(qc.history, n_qubits=int(n_qubits))
35
+ cirq = to_cirq(qc.history, n_qubits=int(n_qubits))
36
 
37
+ probs_top = _probs_top(qc, int(n_qubits), k=6)
38
+ counts_text = _counts_str(last_counts)
39
+
40
+ return ket, counts_text, qasm, qiskit, cirq, probs_top
41
 
 
42
  def init_or_reset(n_qubits):
43
  qc, last_counts, selected_gate = _new_sim(n_qubits)
44
  return qc, last_counts, selected_gate, "✅ Reset done."
45
 
46
+ def set_gate(gate):
 
47
  return gate, f"✅ Selected gate: {gate}"
48
 
49
+ def apply_selected_gate(qc, last_counts, selected_gate, target):
 
50
  qc.apply_single(selected_gate, target=int(target))
51
  last_counts = None
52
  return qc, last_counts, f"✅ Applied {selected_gate} on q{target}."
53
 
54
+ def apply_cnot(qc, last_counts, control, target):
 
55
  if int(control) == int(target):
56
  return qc, last_counts, "❌ Control and target must be different."
57
  qc.apply_cnot(control=int(control), target=int(target))
58
  last_counts = None
59
  return qc, last_counts, f"✅ Applied CNOT (q{control} -> q{target})."
60
 
 
61
  def sample_shots(qc, shots):
62
  last_counts = qc.sample(shots=int(shots))
63
  return last_counts, "✅ Sampled shots."
64
 
 
65
  def measure_collapse(qc, shots):
66
  res = qc.measure_collapse()
67
  last_counts = qc.sample(shots=int(shots))
68
  return last_counts, f"✅ Collapsed to |{res}⟩ and sampled shots."
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  def explain_llm(qc, n_qubits, shots, hf_model_id):
 
71
  state_ket = qc.ket_notation(max_terms=6)
72
  probs_top = _probs_top(qc, int(n_qubits), k=6)
73
 
74
  cfg = ExplainConfig(
75
+ model_id=hf_model_id.strip(), # use exact repo id like "google/flan-t5-base"
 
 
76
  max_new_tokens=280,
77
  temperature=0.2,
78
  )
79
 
80
+ return explain_circuit_with_hf(
81
  n_qubits=int(n_qubits),
82
  history=qc.history,
83
  state_ket=state_ket,
 
85
  shots=int(shots),
86
  cfg=cfg,
87
  )
 
 
88
 
89
+ def _refresh_choices(n):
90
+ opts = list(range(int(n)))
91
+ return (
92
+ gr.Dropdown(choices=opts, value=0),
93
+ gr.Dropdown(choices=opts, value=0),
94
+ gr.Dropdown(choices=opts, value=min(1, int(n)-1)),
95
+ )
96
 
97
+ # ---------- Streamlit-like styling ----------
98
+ CSS = """
99
+ #title h1 { font-size: 42px !important; margin-bottom: 6px; }
100
+ #subtitle { color: #6b7280; margin-top: 0px; }
101
+ .sidebar {
102
+ background: #f6f7fb;
103
+ border-right: 1px solid #e5e7eb;
104
+ border-radius: 14px;
105
+ padding: 18px 14px;
106
+ height: calc(100vh - 34px);
107
+ position: sticky;
108
+ top: 16px;
109
+ }
110
+ .card {
111
+ background: white;
112
+ border: 1px solid #e5e7eb;
113
+ border-radius: 14px;
114
+ padding: 14px;
115
+ }
116
+ .section-title { font-size: 22px; font-weight: 700; margin: 6px 0 10px; }
117
+ .small-note { color: #6b7280; font-size: 12px; }
118
+ """
119
+
120
+ theme = gr.themes.Soft(
121
+ radius_size="lg",
122
+ text_size="md"
123
+ )
124
+
125
+ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio") as demo:
126
  qc_state = gr.State()
127
  last_counts_state = gr.State()
128
  selected_gate_state = gr.State()
129
 
130
  with gr.Row():
131
+ # ----- Sidebar -----
132
+ with gr.Column(scale=3, elem_classes=["sidebar"]):
133
+ gr.Markdown("### Simulator Settings")
134
+ n_qubits = gr.Slider(1, 10, value=2, step=1, label="Number of qubits")
135
+ shots = gr.Slider(128, 8192, value=1024, step=128, label="Shots (for sampling)")
136
+
137
+ gr.Markdown("---")
138
+ gr.Markdown("### LLM Explanation (Hugging Face)")
139
+ hf_model_id = gr.Textbox(value="google/flan-t5-base", label="HF model repo id")
140
+ gr.Markdown("<div class='small-note'>Set secret in HF Space: <b>HF_TOKEN</b></div>")
141
+
142
+ reset_btn = gr.Button("Reset Simulator", variant="secondary")
143
+
144
+ # ----- Main content -----
145
+ with gr.Column(scale=9):
146
+ gr.Markdown("<div id='title'><h1>Quread.ai — State Vector Studio (Dev Tool Edition)</h1></div>")
147
+ gr.Markdown("<div id='subtitle'>Build circuits, inspect the statevector, visualize the circuit, export to QASM/Qiskit/Cirq, and get grounded explanations.</div>")
148
+
149
+ status = gr.Markdown("")
150
+
151
+ # Gate palette (Streamlit-like buttons row)
152
+ with gr.Group(elem_classes=["card"]):
153
+ gr.Markdown("<div class='section-title'>Gate Palette</div>")
154
+ with gr.Row():
155
+ gate_H = gr.Button("H")
156
+ gate_X = gr.Button("X")
157
+ gate_Y = gr.Button("Y")
158
+ gate_Z = gr.Button("Z")
159
+ gate_S = gr.Button("S")
160
+ gate_T = gr.Button("T")
161
+
162
+ target = gr.Dropdown(choices=[0, 1], value=0, label="Target qubit for single-qubit gates")
163
+
164
+ with gr.Row():
165
+ apply_gate_btn = gr.Button("Apply Selected Gate", variant="primary")
166
+ sample_btn = gr.Button("Sample shots")
167
+ measure_btn = gr.Button("Measure + Collapse")
168
+
169
+ gr.Markdown("**CNOT**")
170
+ with gr.Row():
171
+ control = gr.Dropdown(choices=[0, 1], value=0, label="Control")
172
+ cnot_target = gr.Dropdown(choices=[0, 1], value=1, label="Target")
173
+ apply_cnot_btn = gr.Button("Apply CNOT")
174
+
175
+ # Outputs (two-column like your screenshots)
176
+ with gr.Row():
177
+ with gr.Column(scale=7):
178
+ with gr.Group(elem_classes=["card"]):
179
+ gr.Markdown("<div class='section-title'>Statevector (human-readable)</div>")
180
+ ket_out = gr.Code(label="", language="python")
181
+ gr.Markdown("<div class='section-title'>Top probabilities</div>")
182
+ probs_out = gr.Dataframe(headers=["bitstring", "prob"], label="", interactive=False)
183
+
184
+ with gr.Column(scale=5):
185
+ with gr.Group(elem_classes=["card"]):
186
+ gr.Markdown("<div class='section-title'>Measurement distribution</div>")
187
+ counts_out = gr.Textbox(label="", lines=10, placeholder="Click 'Sample shots' or 'Measure + Collapse' to generate counts.")
188
+ with gr.Group(elem_classes=["card"]):
189
+ gr.Markdown("<div class='section-title'>Export</div>")
190
+ qasm_out = gr.Textbox(label="OpenQASM 2.0", lines=8)
191
+ qiskit_out = gr.Textbox(label="Qiskit code", lines=8)
192
+ cirq_out = gr.Textbox(label="Cirq code", lines=8)
193
+
194
+ with gr.Group(elem_classes=["card"]):
195
+ gr.Markdown("<div class='section-title'>Explain (HF Inference)</div>")
196
+ explain_btn = gr.Button("Explain (LLM)", variant="primary")
197
+ llm_out = gr.Markdown("")
198
+ gr.Markdown("<div class='small-note'>Tip: Sample shots first for better explanations.</div>")
199
+
200
+ # init
201
  def _init_all(n):
202
  qc, last_counts, selected_gate = _new_sim(n)
203
  return qc, last_counts, selected_gate
204
 
205
+ demo.load(fn=_init_all, inputs=[n_qubits], outputs=[qc_state, last_counts_state, selected_gate_state])
 
 
 
 
 
206
 
207
+ # keep dropdowns synced with n_qubits
208
+ n_qubits.change(fn=_refresh_choices, inputs=[n_qubits], outputs=[target, control, cnot_target])
 
 
 
 
209
 
210
+ # reset
211
  reset_btn.click(
212
  fn=init_or_reset,
213
  inputs=[n_qubits],
 
218
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
219
  )
220
 
221
+ # gate select
222
+ gate_H.click(fn=lambda: set_gate("H"), outputs=[selected_gate_state, status])
223
+ gate_X.click(fn=lambda: set_gate("X"), outputs=[selected_gate_state, status])
224
+ gate_Y.click(fn=lambda: set_gate("Y"), outputs=[selected_gate_state, status])
225
+ gate_Z.click(fn=lambda: set_gate("Z"), outputs=[selected_gate_state, status])
226
+ gate_S.click(fn=lambda: set_gate("S"), outputs=[selected_gate_state, status])
227
+ gate_T.click(fn=lambda: set_gate("T"), outputs=[selected_gate_state, status])
228
 
229
+ # apply gate
230
  apply_gate_btn.click(
231
  fn=apply_selected_gate,
232
+ inputs=[qc_state, last_counts_state, selected_gate_state, target],
233
  outputs=[qc_state, last_counts_state, status],
234
  ).then(
235
  fn=update_views,
 
237
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
238
  )
239
 
240
+ # cnot
241
  apply_cnot_btn.click(
242
  fn=apply_cnot,
243
+ inputs=[qc_state, last_counts_state, control, cnot_target],
244
  outputs=[qc_state, last_counts_state, status],
245
  ).then(
246
  fn=update_views,
 
248
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
249
  )
250
 
251
+ # sample
252
  sample_btn.click(
253
  fn=sample_shots,
254
  inputs=[qc_state, shots],
 
259
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
260
  )
261
 
262
+ # measure
263
  measure_btn.click(
264
  fn=measure_collapse,
265
  inputs=[qc_state, shots],
 
270
  outputs=[ket_out, counts_out, qasm_out, qiskit_out, cirq_out, probs_out],
271
  )
272
 
273
+ # explain
274
  explain_btn.click(
275
  fn=explain_llm,
276
  inputs=[qc_state, n_qubits, shots, hf_model_id],