Laborator commited on
Commit
b3907b1
·
1 Parent(s): 4de469e

fix(space): stateless IBM UI - instant Job ID return, manual check button

Browse files
Files changed (1) hide show
  1. app.py +20 -115
app.py CHANGED
@@ -384,27 +384,17 @@ def _final_payload(
384
  }
385
 
386
 
387
- def verify_on_ibm(label: str, mode: str) -> Iterator[dict[str, Any]]:
388
- """Submit the example to IBM and yield progressive status updates.
389
-
390
- First yield: ``status="submitted"`` with the IBM job_id and dashboard
391
- URL. Subsequent yields: ``status="in_progress"`` with the IBM-reported
392
- status and elapsed seconds. Final yield: ``status="completed"`` with
393
- the full verification result, or ``status="timeout"`` if the live
394
- poll loop exceeded :data:`LIVE_POLL_TIMEOUT_SECONDS`, or
395
- ``status="error"`` if something went wrong.
396
- """
397
  if label not in EXAMPLES:
398
- yield {"status": "error", "error": f"unknown example: {label}"}
399
- return
400
  try:
401
  narrow_mode = _coerce_mode(mode)
402
  except ValueError as exc:
403
- yield {"status": "error", "error": str(exc)}
404
- return
405
 
406
  if not IBM_AVAILABLE:
407
- yield {
408
  "status": "error",
409
  "error": (
410
  "IBM_QUANTUM_TOKEN and/or IBM_QUANTUM_INSTANCE are not set "
@@ -412,125 +402,34 @@ def verify_on_ibm(label: str, mode: str) -> Iterator[dict[str, Any]]:
412
  "unavailable; use the simulator button."
413
  ),
414
  }
415
- return
416
 
417
  start = time.monotonic()
418
  try:
419
  job, job_id, backend_name, prepared = _prepare_and_submit(label)
420
  except Exception as exc:
421
- yield {
422
  "status": "error",
423
  "error": f"{type(exc).__name__}: {exc}",
424
  }
425
- return
426
 
427
- yield {
 
428
  "status": "submitted",
429
  "ibm_job_id": job_id,
430
  "ibm_job_url": IBM_CONSOLE_BASE + job_id,
431
  "ibm_status": "QUEUED",
432
- "seconds_elapsed": int(time.monotonic() - start),
433
  "backend": backend_name,
434
  "example": label,
435
  "mode": narrow_mode,
436
  "message": (
437
- "Job submitted. Polling every "
438
- f"{POLL_INTERVAL_SECONDS} s for up to "
439
- f"{LIVE_POLL_TIMEOUT_SECONDS // 60} minutes. If your browser "
440
- "loses the live stream before the job finishes, copy the "
441
- "Job ID from this output and use the 'Recover a previous "
442
- "job' panel below."
443
  ),
444
  }
445
-
446
- while True:
447
- elapsed = int(time.monotonic() - start)
448
- if elapsed > LIVE_POLL_TIMEOUT_SECONDS:
449
- yield {
450
- "status": "timeout",
451
- "ibm_job_id": job_id,
452
- "ibm_job_url": IBM_CONSOLE_BASE + job_id,
453
- "seconds_elapsed": elapsed,
454
- "message": (
455
- "Live wait exceeded "
456
- f"{LIVE_POLL_TIMEOUT_SECONDS} s without the job "
457
- "completing. The job is still running on IBM. Use "
458
- "the 'Recover a previous job' panel below with this "
459
- "Job ID once it finishes (typical wall-clock 2-20 "
460
- "minutes for free-tier queue + run)."
461
- ),
462
- }
463
- return
464
-
465
- time.sleep(POLL_INTERVAL_SECONDS)
466
-
467
- try:
468
- ibm_status = str(job.status())
469
- except Exception as exc:
470
- yield {
471
- "status": "error",
472
- "ibm_job_id": job_id,
473
- "ibm_job_url": IBM_CONSOLE_BASE + job_id,
474
- "seconds_elapsed": int(time.monotonic() - start),
475
- "error": f"polling failed: {type(exc).__name__}: {exc}",
476
- }
477
- return
478
-
479
- if ibm_status not in IBM_TERMINAL_STATUSES:
480
- yield {
481
- "status": "in_progress",
482
- "ibm_job_id": job_id,
483
- "ibm_job_url": IBM_CONSOLE_BASE + job_id,
484
- "ibm_status": ibm_status,
485
- "seconds_elapsed": int(time.monotonic() - start),
486
- }
487
- continue
488
-
489
- if ibm_status != "DONE":
490
- yield {
491
- "status": "error",
492
- "ibm_job_id": job_id,
493
- "ibm_job_url": IBM_CONSOLE_BASE + job_id,
494
- "ibm_status": ibm_status,
495
- "seconds_elapsed": int(time.monotonic() - start),
496
- "error": f"IBM job ended with status {ibm_status}",
497
- }
498
- return
499
-
500
- try:
501
- result_obj = job.result()
502
- pub_result = result_obj[0]
503
- raw_counts = pub_result.data.meas.get_counts()
504
- except Exception as exc:
505
- yield {
506
- "status": "error",
507
- "ibm_job_id": job_id,
508
- "ibm_job_url": IBM_CONSOLE_BASE + job_id,
509
- "seconds_elapsed": int(time.monotonic() - start),
510
- "error": f"result fetch failed: {type(exc).__name__}: {exc}",
511
- }
512
- return
513
-
514
- counts = _decode_counts(raw_counts)
515
- verification = _build_verification_result(counts, prepared, backend_name, narrow_mode)
516
- wall = round(time.monotonic() - start, 1)
517
- yield _final_payload(
518
- label, narrow_mode, job_id, backend_name, verification, wall, ibm_status, int(wall)
519
- )
520
- return
521
-
522
-
523
- # --------------------------------------------------------------------------
524
- # Manual recovery path - look up a previous job by ID
525
- # --------------------------------------------------------------------------
526
-
527
-
528
- def check_job_status(job_id: str) -> dict[str, Any]:
529
- """One-shot status query for a job already submitted (live stream lost)."""
530
- job_id = (job_id or "").strip()
531
- if not job_id:
532
- return {"error": "Enter a Job ID first."}
533
-
534
  if not IBM_AVAILABLE:
535
  return {
536
  "error": (
@@ -686,6 +585,7 @@ with gr.Blocks(title="QVerify") as demo:
686
  with gr.Column(scale=1):
687
  gr.Markdown("### Result")
688
  output = gr.JSON(label="Verification result")
 
689
 
690
  example_dd.change(
691
  _on_example_change,
@@ -702,6 +602,11 @@ with gr.Blocks(title="QVerify") as demo:
702
  inputs=[example_dd, mode_radio],
703
  outputs=output,
704
  )
 
 
 
 
 
705
 
706
  gr.Markdown("---")
707
  gr.Markdown("### 4. Recover a previous job (fallback)")
 
384
  }
385
 
386
 
387
+ def verify_on_ibm(label: str, mode: str) -> dict[str, Any]:
388
+ """Submit the example to IBM and return job ID immediately (no polling)."""
 
 
 
 
 
 
 
 
389
  if label not in EXAMPLES:
390
+ return {"status": "error", "error": f"unknown example: {label}"}
 
391
  try:
392
  narrow_mode = _coerce_mode(mode)
393
  except ValueError as exc:
394
+ return {"status": "error", "error": str(exc)}
 
395
 
396
  if not IBM_AVAILABLE:
397
+ return {
398
  "status": "error",
399
  "error": (
400
  "IBM_QUANTUM_TOKEN and/or IBM_QUANTUM_INSTANCE are not set "
 
402
  "unavailable; use the simulator button."
403
  ),
404
  }
 
405
 
406
  start = time.monotonic()
407
  try:
408
  job, job_id, backend_name, prepared = _prepare_and_submit(label)
409
  except Exception as exc:
410
+ return {
411
  "status": "error",
412
  "error": f"{type(exc).__name__}: {exc}",
413
  }
 
414
 
415
+ elapsed = int(time.monotonic() - start)
416
+ return {
417
  "status": "submitted",
418
  "ibm_job_id": job_id,
419
  "ibm_job_url": IBM_CONSOLE_BASE + job_id,
420
  "ibm_status": "QUEUED",
421
+ "seconds_elapsed": elapsed,
422
  "backend": backend_name,
423
  "example": label,
424
  "mode": narrow_mode,
425
  "message": (
426
+ f"Job submitted to {backend_name} in {elapsed}s. "
427
+ "The job is now running on IBM Quantum hardware. "
428
+ "Click 'Check job status' below to retrieve the result when ready "
429
+ "(typically 1-3 minutes for queue + execution). "
430
+ f"Job ID: {job_id}"
 
431
  ),
432
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  if not IBM_AVAILABLE:
434
  return {
435
  "error": (
 
585
  with gr.Column(scale=1):
586
  gr.Markdown("### Result")
587
  output = gr.JSON(label="Verification result")
588
+ btn_check_status = gr.Button("Check job status", variant="secondary", visible=True)
589
 
590
  example_dd.change(
591
  _on_example_change,
 
602
  inputs=[example_dd, mode_radio],
603
  outputs=output,
604
  )
605
+ btn_check_status.click(
606
+ lambda out: check_job_status(out.get("ibm_job_id", "")) if isinstance(out, dict) else {"error": "No job submitted yet"},
607
+ inputs=output,
608
+ outputs=output,
609
+ )
610
 
611
  gr.Markdown("---")
612
  gr.Markdown("### 4. Recover a previous job (fallback)")