Upload app.py
Browse files
app.py
CHANGED
|
@@ -43,9 +43,19 @@ def _counts_str(counts):
|
|
| 43 |
|
| 44 |
|
| 45 |
def _write_tmp(filename: str, content: str) -> str:
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
|
| 51 |
def _circuit_hash(history):
|
|
@@ -114,27 +124,41 @@ def measure_collapse(qc, shots):
|
|
| 114 |
return last_counts, f"✅ Collapsed to |{res}⟩ and sampled shots."
|
| 115 |
|
| 116 |
|
| 117 |
-
def explain_llm(qc, n_qubits, shots, last_hash):
|
| 118 |
# circuit-change gating
|
| 119 |
curr_hash = _circuit_hash(qc.history)
|
| 120 |
if curr_hash == last_hash:
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
# cost guard
|
| 124 |
EST_TOKENS = 900
|
| 125 |
if not allow_request(EST_TOKENS):
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
state_ket = qc.ket_notation(max_terms=6)
|
| 129 |
probs_top = _probs_top(qc, int(n_qubits), k=6)
|
| 130 |
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
return explanation, curr_hash, explanation
|
| 140 |
|
|
@@ -148,6 +172,13 @@ def _refresh_choices(n):
|
|
| 148 |
)
|
| 149 |
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
# ---------- Styling ----------
|
| 152 |
CSS = """
|
| 153 |
#title h1 { font-size: 42px !important; margin-bottom: 6px; }
|
|
@@ -298,9 +329,9 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
|
|
| 298 |
)
|
| 299 |
|
| 300 |
n_qubits.change(
|
| 301 |
-
fn=
|
| 302 |
inputs=[n_qubits],
|
| 303 |
-
outputs=[target, control, cnot_target],
|
| 304 |
).then(
|
| 305 |
fn=update_views,
|
| 306 |
inputs=[qc_state, last_counts_state, n_qubits],
|
|
@@ -390,7 +421,7 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
|
|
| 390 |
|
| 391 |
explain_btn.click(
|
| 392 |
fn=explain_llm,
|
| 393 |
-
inputs=[qc_state, n_qubits, shots, last_explained_hash],
|
| 394 |
outputs=[llm_out, last_explained_hash, explanation_md],
|
| 395 |
)
|
| 396 |
|
|
@@ -409,13 +440,18 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
|
|
| 409 |
return _write_tmp("explanation.md", md_text)
|
| 410 |
|
| 411 |
def dl_explain_pdf(md_text):
|
| 412 |
-
path =
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
|
| 416 |
dl_md.click(fn=dl_explain_md, inputs=[explanation_md], outputs=[dl_md])
|
| 417 |
dl_pdf.click(fn=dl_explain_pdf, inputs=[explanation_md], outputs=[dl_pdf])
|
| 418 |
|
| 419 |
|
| 420 |
if __name__ == "__main__":
|
| 421 |
-
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
def _write_tmp(filename: str, content: str) -> str:
|
| 46 |
+
base = pathlib.Path(filename).name
|
| 47 |
+
stem = pathlib.Path(base).stem or "quread"
|
| 48 |
+
suffix = pathlib.Path(base).suffix or ".txt"
|
| 49 |
+
with tempfile.NamedTemporaryFile(
|
| 50 |
+
mode="w",
|
| 51 |
+
encoding="utf-8",
|
| 52 |
+
prefix=f"{stem}_",
|
| 53 |
+
suffix=suffix,
|
| 54 |
+
dir=tempfile.gettempdir(),
|
| 55 |
+
delete=False,
|
| 56 |
+
) as f:
|
| 57 |
+
f.write(content)
|
| 58 |
+
return f.name
|
| 59 |
|
| 60 |
|
| 61 |
def _circuit_hash(history):
|
|
|
|
| 124 |
return last_counts, f"✅ Collapsed to |{res}⟩ and sampled shots."
|
| 125 |
|
| 126 |
|
| 127 |
+
def explain_llm(qc, n_qubits, shots, last_hash, previous_explanation):
|
| 128 |
# circuit-change gating
|
| 129 |
curr_hash = _circuit_hash(qc.history)
|
| 130 |
if curr_hash == last_hash:
|
| 131 |
+
if previous_explanation:
|
| 132 |
+
shown = f"ℹ️ Circuit unchanged. Reusing previous explanation.\n\n{previous_explanation}"
|
| 133 |
+
return shown, last_hash, previous_explanation
|
| 134 |
+
return "ℹ️ Circuit unchanged. No previous explanation available.", last_hash, previous_explanation
|
| 135 |
|
| 136 |
# cost guard
|
| 137 |
EST_TOKENS = 900
|
| 138 |
if not allow_request(EST_TOKENS):
|
| 139 |
+
if previous_explanation:
|
| 140 |
+
shown = "🚫 Explanation disabled (daily token limit reached). Showing previous explanation.\n\n"
|
| 141 |
+
shown += previous_explanation
|
| 142 |
+
return shown, last_hash, previous_explanation
|
| 143 |
+
return "🚫 Explanation disabled (daily token limit reached).", last_hash, previous_explanation
|
| 144 |
|
| 145 |
state_ket = qc.ket_notation(max_terms=6)
|
| 146 |
probs_top = _probs_top(qc, int(n_qubits), k=6)
|
| 147 |
|
| 148 |
+
try:
|
| 149 |
+
explanation = explain_with_gpt4o(
|
| 150 |
+
n_qubits=int(n_qubits),
|
| 151 |
+
history=qc.history,
|
| 152 |
+
state_ket=state_ket,
|
| 153 |
+
probs_top=probs_top,
|
| 154 |
+
shots=int(shots),
|
| 155 |
+
)
|
| 156 |
+
except Exception as exc:
|
| 157 |
+
safe_msg = f"❌ Explanation request failed: {exc}"
|
| 158 |
+
if previous_explanation:
|
| 159 |
+
shown = f"{safe_msg}\n\nShowing previous explanation:\n\n{previous_explanation}"
|
| 160 |
+
return shown, last_hash, previous_explanation
|
| 161 |
+
return safe_msg, last_hash, previous_explanation
|
| 162 |
|
| 163 |
return explanation, curr_hash, explanation
|
| 164 |
|
|
|
|
| 172 |
)
|
| 173 |
|
| 174 |
|
| 175 |
+
def _on_qubit_count_change(n):
|
| 176 |
+
qc, last_counts, selected_gate = _new_sim(n)
|
| 177 |
+
t, c, ct = _refresh_choices(n)
|
| 178 |
+
msg = f"✅ Reinitialized simulator with {int(n)} qubits."
|
| 179 |
+
return qc, last_counts, selected_gate, t, c, ct, msg
|
| 180 |
+
|
| 181 |
+
|
| 182 |
# ---------- Styling ----------
|
| 183 |
CSS = """
|
| 184 |
#title h1 { font-size: 42px !important; margin-bottom: 6px; }
|
|
|
|
| 329 |
)
|
| 330 |
|
| 331 |
n_qubits.change(
|
| 332 |
+
fn=_on_qubit_count_change,
|
| 333 |
inputs=[n_qubits],
|
| 334 |
+
outputs=[qc_state, last_counts_state, selected_gate_state, target, control, cnot_target, status],
|
| 335 |
).then(
|
| 336 |
fn=update_views,
|
| 337 |
inputs=[qc_state, last_counts_state, n_qubits],
|
|
|
|
| 421 |
|
| 422 |
explain_btn.click(
|
| 423 |
fn=explain_llm,
|
| 424 |
+
inputs=[qc_state, n_qubits, shots, last_explained_hash, explanation_md],
|
| 425 |
outputs=[llm_out, last_explained_hash, explanation_md],
|
| 426 |
)
|
| 427 |
|
|
|
|
| 440 |
return _write_tmp("explanation.md", md_text)
|
| 441 |
|
| 442 |
def dl_explain_pdf(md_text):
|
| 443 |
+
fd, path = tempfile.mkstemp(
|
| 444 |
+
prefix="explanation_",
|
| 445 |
+
suffix=".pdf",
|
| 446 |
+
dir=tempfile.gettempdir(),
|
| 447 |
+
)
|
| 448 |
+
os.close(fd)
|
| 449 |
+
md_to_pdf(md_text, path)
|
| 450 |
+
return path
|
| 451 |
|
| 452 |
dl_md.click(fn=dl_explain_md, inputs=[explanation_md], outputs=[dl_md])
|
| 453 |
dl_pdf.click(fn=dl_explain_pdf, inputs=[explanation_md], outputs=[dl_pdf])
|
| 454 |
|
| 455 |
|
| 456 |
if __name__ == "__main__":
|
| 457 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|