hchevva commited on
Commit
ff61605
·
verified ·
1 Parent(s): 87ea89f

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -6
app.py CHANGED
@@ -32,7 +32,7 @@ from quread.metrics import (
32
  MetricThresholds,
33
  )
34
  from quread.layout_mapper import parse_layout_csv_text
35
- from quread.trends import compute_metric_trends
36
 
37
  # --- Qubit cap (configurable) ---
38
  DEFAULT_MAX_QUBITS = 16 # safe default for CPU Spaces; change if you want
@@ -498,6 +498,59 @@ def _plot_metric_trends(series, labels, ranking_rows, metric_key, top_k):
498
  return fig
499
 
500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  # ---------- Styling ----------
502
  CSS = """
503
  #title h1 { font-size: 38px !important; margin-bottom: 4px; letter-spacing: -0.02em; }
@@ -540,6 +593,7 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
540
  selected_gate_state = gr.State()
541
  explanation_md = gr.State("")
542
  last_explained_hash = gr.State("")
 
543
 
544
  with gr.Row(elem_classes=["app-shell"]):
545
  # Sidebar
@@ -755,6 +809,12 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
755
  )
756
  trend_top_k = gr.Slider(1, 32, value=8, step=1, label="Trend lines (top qubits)")
757
  trend_btn = gr.Button("Analyze drift", variant="secondary")
 
 
 
 
 
 
758
  with gr.Accordion("Snapshot Input", open=False):
759
  trend_snapshots_file = gr.File(
760
  label="Snapshots file (.json/.jsonl/.txt)",
@@ -768,12 +828,20 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
768
  )
769
  gr.Markdown("<div class='small-note'>If both are provided, file input is used.</div>")
770
  trend_status = gr.Markdown("Upload snapshots and click Analyze drift.")
 
771
  trend_plot = gr.Plot()
772
  trend_table = gr.Dataframe(
773
  headers=["qubit", "latest", "baseline", "delta"],
774
  interactive=False,
775
- label="Latest ranking (highest selected metric first)",
 
 
 
 
 
 
776
  )
 
777
 
778
  with gr.Tab("Explain"):
779
  with gr.Group(elem_classes=["card"]):
@@ -1175,6 +1243,11 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1175
  snapshots_text,
1176
  trend_metric_value,
1177
  trend_top_qubits,
 
 
 
 
 
1178
  activity_w,
1179
  gate_error_w,
1180
  readout_error_w,
@@ -1193,7 +1266,15 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1193
  ax.text(0.5, 0.5, "Provide calibration snapshots (JSON/JSONL).", ha="center", va="center")
1194
  ax.axis("off")
1195
  fig.tight_layout()
1196
- return fig, "No snapshots provided.", []
 
 
 
 
 
 
 
 
1197
 
1198
  csv_text = to_csv(qc.history)
1199
  weights, thresholds = _metric_controls_to_models(
@@ -1222,7 +1303,15 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1222
  ax.text(0.5, 0.5, f"Unable to parse snapshots: {exc}", ha="center", va="center")
1223
  ax.axis("off")
1224
  fig.tight_layout()
1225
- return fig, f"Drift analysis failed: {exc}", []
 
 
 
 
 
 
 
 
1226
 
1227
  fig = _plot_metric_trends(
1228
  series,
@@ -1241,14 +1330,47 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1241
  round(float(row["delta"]), 6),
1242
  ]
1243
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1244
  status = (
1245
  f"Snapshots parsed: {int(meta.get('parsed', 0))}"
1246
  f" | Skipped: {int(meta.get('skipped', 0))}"
1247
  f" | Format: {meta.get('format', 'unknown')}"
1248
  f" | Points: {int(meta.get('points', 0))}"
1249
  f" | Metric: {meta.get('metric', trend_metric_value)}"
 
1250
  )
1251
- return fig, status, table_rows
 
 
 
 
1252
 
1253
  heat_btn.click(
1254
  fn=_heat_and_hotspots_from_current,
@@ -1301,6 +1423,11 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1301
  trend_snapshots_text,
1302
  trend_metric,
1303
  trend_top_k,
 
 
 
 
 
1304
  w_activity,
1305
  w_gate,
1306
  w_readout,
@@ -1309,7 +1436,21 @@ with gr.Blocks(theme=theme, css=CSS, title="Quread.ai — State Vector Studio")
1309
  thr_warning,
1310
  thr_critical,
1311
  ],
1312
- outputs=[trend_plot, trend_status, trend_table],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1313
  )
1314
 
1315
  synopsys_tcl_dl.click(
 
32
  MetricThresholds,
33
  )
34
  from quread.layout_mapper import parse_layout_csv_text
35
+ from quread.trends import compute_metric_trends, compute_drift_alerts, alerts_to_csv
36
 
37
  # --- Qubit cap (configurable) ---
38
  DEFAULT_MAX_QUBITS = 16 # safe default for CPU Spaces; change if you want
 
498
  return fig
499
 
500
 
501
+ def _drift_alert_summary(alert_rows):
502
+ total = len(alert_rows)
503
+ critical = sum(1 for r in alert_rows if str(r.get("level")) == "critical")
504
+ warning = sum(1 for r in alert_rows if str(r.get("level")) == "warning")
505
+ ok = max(0, total - critical - warning)
506
+ if critical > 0:
507
+ badge = "critical drift detected"
508
+ elif warning > 0:
509
+ badge = "warning drift detected"
510
+ else:
511
+ badge = "stable"
512
+ return f"Auto-flag summary: critical={critical}, warning={warning}, ok={ok} ({badge})."
513
+
514
+
515
+ def _mitigation_hint_for_metric(metric_key, level):
516
+ severity = str(level or "ok")
517
+ if metric_key == "gate_error":
518
+ return "Prioritize gate pulse recalibration and schedule RB/IRB validation."
519
+ if metric_key == "readout_error":
520
+ return "Run readout discriminator retuning and measurement calibration refresh."
521
+ if metric_key == "decoherence_risk":
522
+ return "Reduce circuit depth on flagged qubits and revisit idle decoupling timing."
523
+ if metric_key in {"fidelity", "state_fidelity", "process_fidelity"}:
524
+ return "Re-characterize fidelity with tomography/benchmarking and re-tune control stack."
525
+ if metric_key == "coherence_health":
526
+ return "Investigate T1/T2 drift, thermal environment, and schedule recalibration windows."
527
+ if severity == "critical":
528
+ return "Lock flagged qubits out of high-depth paths and escalate full calibration."
529
+ if severity == "warning":
530
+ return "Increase monitoring cadence and rebalance workloads away from flagged qubits."
531
+ return "No immediate action required; continue scheduled monitoring."
532
+
533
+
534
+ def _drift_recommendations_markdown(alert_rows, metric_key, max_items=6):
535
+ if not alert_rows:
536
+ return "No recommendations available yet. Run drift analysis first."
537
+
538
+ actionable = [r for r in alert_rows if str(r.get("level")) in {"critical", "warning"}]
539
+ if not actionable:
540
+ return "### Recommended Actions\n- No active drift alerts. Maintain regular calibration cadence."
541
+
542
+ lines = ["### Recommended Actions"]
543
+ k = max(1, min(int(max_items), len(actionable)))
544
+ for row in actionable[:k]:
545
+ q = int(row.get("qubit", -1))
546
+ level = str(row.get("level", "warning"))
547
+ hint = _mitigation_hint_for_metric(str(metric_key), level)
548
+ triggers = row.get("triggers") or []
549
+ reason = ", ".join(str(t) for t in triggers[:2]) if triggers else "trend threshold crossing"
550
+ lines.append(f"- q{q} ({level}): {hint} Trigger: {reason}.")
551
+ return "\n".join(lines)
552
+
553
+
554
  # ---------- Styling ----------
555
  CSS = """
556
  #title h1 { font-size: 38px !important; margin-bottom: 4px; letter-spacing: -0.02em; }
 
593
  selected_gate_state = gr.State()
594
  explanation_md = gr.State("")
595
  last_explained_hash = gr.State("")
596
+ drift_alert_rows_state = gr.State([])
597
 
598
  with gr.Row(elem_classes=["app-shell"]):
599
  # Sidebar
 
809
  )
810
  trend_top_k = gr.Slider(1, 32, value=8, step=1, label="Trend lines (top qubits)")
811
  trend_btn = gr.Button("Analyze drift", variant="secondary")
812
+ with gr.Row():
813
+ drift_delta_warning = gr.Slider(0.0, 1.0, value=0.08, step=0.01, label="Drift delta warning")
814
+ drift_delta_critical = gr.Slider(0.0, 1.0, value=0.18, step=0.01, label="Drift delta critical")
815
+ drift_slope_warning = gr.Slider(0.0, 0.5, value=0.02, step=0.005, label="Drift slope warning")
816
+ drift_slope_critical = gr.Slider(0.0, 0.5, value=0.05, step=0.005, label="Drift slope critical")
817
+ drift_alert_top_k = gr.Slider(1, 64, value=16, step=1, label="Auto-flag rows")
818
  with gr.Accordion("Snapshot Input", open=False):
819
  trend_snapshots_file = gr.File(
820
  label="Snapshots file (.json/.jsonl/.txt)",
 
828
  )
829
  gr.Markdown("<div class='small-note'>If both are provided, file input is used.</div>")
830
  trend_status = gr.Markdown("Upload snapshots and click Analyze drift.")
831
+ drift_alert_csv_dl = gr.DownloadButton("Download auto-flag CSV")
832
  trend_plot = gr.Plot()
833
  trend_table = gr.Dataframe(
834
  headers=["qubit", "latest", "baseline", "delta"],
835
  interactive=False,
836
+ label="Latest ranking (highest metric risk first)",
837
+ )
838
+ drift_alert_status = gr.Markdown("Auto-flag summary: awaiting trend analysis.")
839
+ drift_alert_table = gr.Dataframe(
840
+ headers=["qubit", "alert", "latest_risk", "risk_delta", "risk_slope", "triggers"],
841
+ interactive=False,
842
+ label="Auto-flag panel",
843
  )
844
+ drift_reco_md = gr.Markdown("Recommendations appear after drift analysis.")
845
 
846
  with gr.Tab("Explain"):
847
  with gr.Group(elem_classes=["card"]):
 
1243
  snapshots_text,
1244
  trend_metric_value,
1245
  trend_top_qubits,
1246
+ drift_delta_warn,
1247
+ drift_delta_crit,
1248
+ drift_slope_warn,
1249
+ drift_slope_crit,
1250
+ drift_alert_rows_max,
1251
  activity_w,
1252
  gate_error_w,
1253
  readout_error_w,
 
1266
  ax.text(0.5, 0.5, "Provide calibration snapshots (JSON/JSONL).", ha="center", va="center")
1267
  ax.axis("off")
1268
  fig.tight_layout()
1269
+ return (
1270
+ fig,
1271
+ "No snapshots provided.",
1272
+ [],
1273
+ "Auto-flag summary: no data.",
1274
+ [],
1275
+ [],
1276
+ "No recommendations available yet. Run drift analysis first.",
1277
+ )
1278
 
1279
  csv_text = to_csv(qc.history)
1280
  weights, thresholds = _metric_controls_to_models(
 
1303
  ax.text(0.5, 0.5, f"Unable to parse snapshots: {exc}", ha="center", va="center")
1304
  ax.axis("off")
1305
  fig.tight_layout()
1306
+ return (
1307
+ fig,
1308
+ f"Drift analysis failed: {exc}",
1309
+ [],
1310
+ f"Auto-flag summary unavailable: {exc}",
1311
+ [],
1312
+ [],
1313
+ f"Recommendation generation unavailable: {exc}",
1314
+ )
1315
 
1316
  fig = _plot_metric_trends(
1317
  series,
 
1330
  round(float(row["delta"]), 6),
1331
  ]
1332
  )
1333
+ alerts = compute_drift_alerts(
1334
+ ranking,
1335
+ warning_threshold=float(warning_thr),
1336
+ critical_threshold=float(critical_thr),
1337
+ delta_warning=float(drift_delta_warn),
1338
+ delta_critical=float(drift_delta_crit),
1339
+ slope_warning=float(drift_slope_warn),
1340
+ slope_critical=float(drift_slope_crit),
1341
+ )
1342
+ max_rows = max(1, min(int(drift_alert_rows_max), len(alerts)))
1343
+ alert_table_rows = []
1344
+ for row in alerts[:max_rows]:
1345
+ alert_table_rows.append(
1346
+ [
1347
+ int(row["qubit"]),
1348
+ str(row["level"]),
1349
+ round(float(row["latest_risk"]), 6),
1350
+ round(float(row["risk_delta"]), 6),
1351
+ round(float(row["risk_slope"]), 6),
1352
+ "; ".join(row["triggers"]) if row["triggers"] else "-",
1353
+ ]
1354
+ )
1355
+ alert_status = _drift_alert_summary(alerts)
1356
+ recommendation_md = _drift_recommendations_markdown(
1357
+ alerts,
1358
+ str(meta.get("metric", trend_metric_value)),
1359
+ max_items=6,
1360
+ )
1361
  status = (
1362
  f"Snapshots parsed: {int(meta.get('parsed', 0))}"
1363
  f" | Skipped: {int(meta.get('skipped', 0))}"
1364
  f" | Format: {meta.get('format', 'unknown')}"
1365
  f" | Points: {int(meta.get('points', 0))}"
1366
  f" | Metric: {meta.get('metric', trend_metric_value)}"
1367
+ f" | Risk mode: {meta.get('risk_mode', 'unknown')}"
1368
  )
1369
+ return fig, status, table_rows, alert_status, alert_table_rows, alerts, recommendation_md
1370
+
1371
+ def _dl_drift_alert_csv(alert_rows):
1372
+ rows = alert_rows or []
1373
+ return _write_tmp("drift_alerts.csv", alerts_to_csv(rows))
1374
 
1375
  heat_btn.click(
1376
  fn=_heat_and_hotspots_from_current,
 
1423
  trend_snapshots_text,
1424
  trend_metric,
1425
  trend_top_k,
1426
+ drift_delta_warning,
1427
+ drift_delta_critical,
1428
+ drift_slope_warning,
1429
+ drift_slope_critical,
1430
+ drift_alert_top_k,
1431
  w_activity,
1432
  w_gate,
1433
  w_readout,
 
1436
  thr_warning,
1437
  thr_critical,
1438
  ],
1439
+ outputs=[
1440
+ trend_plot,
1441
+ trend_status,
1442
+ trend_table,
1443
+ drift_alert_status,
1444
+ drift_alert_table,
1445
+ drift_alert_rows_state,
1446
+ drift_reco_md,
1447
+ ],
1448
+ )
1449
+
1450
+ drift_alert_csv_dl.click(
1451
+ fn=_dl_drift_alert_csv,
1452
+ inputs=[drift_alert_rows_state],
1453
+ outputs=[drift_alert_csv_dl],
1454
  )
1455
 
1456
  synopsys_tcl_dl.click(