Spaces:
Paused
Paused
harishaseebat92
commited on
Commit
·
dbdff89
1
Parent(s):
4f10fce
Add detailed progress tracking for IBM QPU
Browse files- em/simulation.py +44 -12
- qlbm/qlbm_sample_app.py +195 -44
- qlbm_embedded.py +33 -30
- utils/EBU_Quantum/no_body/base_functions.py +126 -21
em/simulation.py
CHANGED
|
@@ -451,8 +451,8 @@ async def _run_simulation_async():
|
|
| 451 |
if sve_selected:
|
| 452 |
try:
|
| 453 |
log_to_console("Running Statevector Estimator...")
|
| 454 |
-
state.status_message = "
|
| 455 |
-
state.simulation_progress =
|
| 456 |
await _flush_async()
|
| 457 |
|
| 458 |
state.qpu_ts_ready = False
|
|
@@ -479,10 +479,24 @@ async def _run_simulation_async():
|
|
| 479 |
p = getattr(state, f"qpu_monitor_gridpoints_{slot_num}", "") or ""
|
| 480 |
configs.append({"field": f, "points": p})
|
| 481 |
|
| 482 |
-
state.status_message = "
|
| 483 |
-
state.simulation_progress =
|
| 484 |
await _flush_async()
|
| 485 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
def _sve_series_runner(field_type, positions, total_time, snapshot_dt, nx, impulse_pos, progress_callback=None, print_callback=None):
|
| 487 |
return qutils.run_sve(
|
| 488 |
field_type,
|
|
@@ -503,13 +517,18 @@ async def _run_simulation_async():
|
|
| 503 |
return build_qpu_timeseries_plotly_multi(
|
| 504 |
configs, nx, T, snapshot_dt, impulse_pos,
|
| 505 |
series_runner=_sve_series_runner,
|
| 506 |
-
progress_callback=
|
| 507 |
print_callback=log_to_console
|
| 508 |
)
|
| 509 |
|
| 510 |
fig = await loop.run_in_executor(executor, _run_sve_blocking)
|
| 511 |
qpu_ts_cache["fig"] = fig
|
| 512 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
try:
|
| 514 |
ctrl.qpu_ts_update(fig)
|
| 515 |
except Exception:
|
|
@@ -594,18 +613,31 @@ async def _run_simulation_async():
|
|
| 594 |
if len(raw_pts) > 1:
|
| 595 |
log_to_console(f"Warning: IBM QPU only supports single position. Using first: ({monitor_x}, {monitor_y})")
|
| 596 |
|
| 597 |
-
state.status_message = "Step 1:
|
| 598 |
-
state.simulation_progress =
|
| 599 |
await _flush_async()
|
| 600 |
|
| 601 |
-
def _ibm_progress_callback(pct):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 602 |
state.simulation_progress = int(pct)
|
| 603 |
-
|
| 604 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
elif pct < 90:
|
| 606 |
-
state.status_message = f"Step
|
| 607 |
else:
|
| 608 |
-
state.status_message = f"Step
|
| 609 |
_flush_state_threadsafe() # Thread-safe flush from callback thread
|
| 610 |
|
| 611 |
# Call the IBM QPU get_field_values function in executor to keep UI responsive
|
|
|
|
| 451 |
if sve_selected:
|
| 452 |
try:
|
| 453 |
log_to_console("Running Statevector Estimator...")
|
| 454 |
+
state.status_message = "Step 1: Initializing Statevector Estimator..."
|
| 455 |
+
state.simulation_progress = 5
|
| 456 |
await _flush_async()
|
| 457 |
|
| 458 |
state.qpu_ts_ready = False
|
|
|
|
| 479 |
p = getattr(state, f"qpu_monitor_gridpoints_{slot_num}", "") or ""
|
| 480 |
configs.append({"field": f, "points": p})
|
| 481 |
|
| 482 |
+
state.status_message = "Step 1: Setting up Statevector Estimator..."
|
| 483 |
+
state.simulation_progress = 10
|
| 484 |
await _flush_async()
|
| 485 |
|
| 486 |
+
# SVE-specific progress callback that maps internal 0-100% to 10-90% range
|
| 487 |
+
# and shows appropriate step messages
|
| 488 |
+
def _sve_progress_callback(pct):
|
| 489 |
+
# Map internal progress (0-100%) to range 10-90%
|
| 490 |
+
mapped_pct = 10 + (pct * 0.8) # 10% to 90%
|
| 491 |
+
state.simulation_progress = int(mapped_pct)
|
| 492 |
+
if mapped_pct < 30:
|
| 493 |
+
state.status_message = f"Step 2: Building quantum circuits ({int(mapped_pct)}%)"
|
| 494 |
+
elif mapped_pct < 70:
|
| 495 |
+
state.status_message = f"Step 3: Running Statevector simulation ({int(mapped_pct)}%)"
|
| 496 |
+
else:
|
| 497 |
+
state.status_message = f"Step 4: Processing results ({int(mapped_pct)}%)"
|
| 498 |
+
_flush_state_threadsafe()
|
| 499 |
+
|
| 500 |
def _sve_series_runner(field_type, positions, total_time, snapshot_dt, nx, impulse_pos, progress_callback=None, print_callback=None):
|
| 501 |
return qutils.run_sve(
|
| 502 |
field_type,
|
|
|
|
| 517 |
return build_qpu_timeseries_plotly_multi(
|
| 518 |
configs, nx, T, snapshot_dt, impulse_pos,
|
| 519 |
series_runner=_sve_series_runner,
|
| 520 |
+
progress_callback=_sve_progress_callback,
|
| 521 |
print_callback=log_to_console
|
| 522 |
)
|
| 523 |
|
| 524 |
fig = await loop.run_in_executor(executor, _run_sve_blocking)
|
| 525 |
qpu_ts_cache["fig"] = fig
|
| 526 |
|
| 527 |
+
# Step 5: Creating plots (90-100%)
|
| 528 |
+
state.simulation_progress = 95
|
| 529 |
+
state.status_message = "Step 5: Creating plots (95%)"
|
| 530 |
+
_flush_state()
|
| 531 |
+
|
| 532 |
try:
|
| 533 |
ctrl.qpu_ts_update(fig)
|
| 534 |
except Exception:
|
|
|
|
| 613 |
if len(raw_pts) > 1:
|
| 614 |
log_to_console(f"Warning: IBM QPU only supports single position. Using first: ({monitor_x}, {monitor_y})")
|
| 615 |
|
| 616 |
+
state.status_message = "Step 1: Generating circuit..."
|
| 617 |
+
state.simulation_progress = 0
|
| 618 |
await _flush_async()
|
| 619 |
|
| 620 |
+
def _ibm_progress_callback(pct, message=None):
|
| 621 |
+
"""
|
| 622 |
+
Progress callback for IBM QPU with 4-step pattern:
|
| 623 |
+
Step 1: Generating circuit (0-10%)
|
| 624 |
+
Step 2: Optimising Circuit (10-60%)
|
| 625 |
+
Step 3: Job Submitted + Status monitoring (60-90%)
|
| 626 |
+
Step 4: Creating Plots (90-100%)
|
| 627 |
+
"""
|
| 628 |
state.simulation_progress = int(pct)
|
| 629 |
+
|
| 630 |
+
if message:
|
| 631 |
+
state.status_message = message
|
| 632 |
+
elif pct < 10:
|
| 633 |
+
state.status_message = f"Step 1: Generating circuit ({int(pct)}%)"
|
| 634 |
+
elif pct < 60:
|
| 635 |
+
# Map 10-40% internal to 10-60% display
|
| 636 |
+
state.status_message = f"Step 2: Optimising circuit ({int(pct)}%)"
|
| 637 |
elif pct < 90:
|
| 638 |
+
state.status_message = f"Step 3: Job execution ({int(pct)}%)"
|
| 639 |
else:
|
| 640 |
+
state.status_message = f"Step 4: Creating plots ({int(pct)}%)"
|
| 641 |
_flush_state_threadsafe() # Thread-safe flush from callback thread
|
| 642 |
|
| 643 |
# Call the IBM QPU get_field_values function in executor to keep UI responsive
|
qlbm/qlbm_sample_app.py
CHANGED
|
@@ -513,7 +513,8 @@ def run_sampling_hw_ibm(
|
|
| 513 |
vel_resolution=32,
|
| 514 |
output_resolution=40,
|
| 515 |
logger=None,
|
| 516 |
-
flag_qubits=True
|
|
|
|
| 517 |
):
|
| 518 |
"""
|
| 519 |
Run QLBM simulation on IBM quantum hardware.
|
|
@@ -536,6 +537,8 @@ def run_sampling_hw_ibm(
|
|
| 536 |
Grid resolution for density estimation output
|
| 537 |
logger : callable, optional
|
| 538 |
Function to log messages (e.g. print to console)
|
|
|
|
|
|
|
| 539 |
|
| 540 |
Returns
|
| 541 |
-------
|
|
@@ -544,6 +547,7 @@ def run_sampling_hw_ibm(
|
|
| 544 |
get_job_result : callable
|
| 545 |
Callback function to retrieve and process results. Returns (output, fig).
|
| 546 |
"""
|
|
|
|
| 547 |
|
| 548 |
def log(msg):
|
| 549 |
if logger:
|
|
@@ -551,62 +555,144 @@ def run_sampling_hw_ibm(
|
|
| 551 |
else:
|
| 552 |
print(msg)
|
| 553 |
|
| 554 |
-
|
| 555 |
-
|
|
|
|
| 556 |
|
| 557 |
-
#
|
| 558 |
-
|
| 559 |
-
|
| 560 |
|
| 561 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,flag_qubits=flag_qubits)
|
|
|
|
|
|
|
|
|
|
| 562 |
|
| 563 |
pm_optimization_level = 3
|
| 564 |
|
| 565 |
-
|
|
|
|
|
|
|
|
|
|
| 566 |
backend = service.least_busy()
|
|
|
|
|
|
|
| 567 |
|
| 568 |
qc_compiled_list=[]
|
|
|
|
| 569 |
|
| 570 |
-
for qc in qc_list:
|
|
|
|
|
|
|
|
|
|
| 571 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 572 |
-
log("
|
| 573 |
-
qc_compiled = pm.run(qc)
|
| 574 |
-
log(f"Compiled
|
| 575 |
-
log(f"Depth: {qc_compiled.depth()}")
|
| 576 |
qc_compiled_list+=[qc_compiled]
|
| 577 |
|
| 578 |
-
|
| 579 |
-
|
| 580 |
|
| 581 |
-
#
|
|
|
|
| 582 |
job = sampler.run(qc_compiled_list, shots=shots)
|
| 583 |
-
|
|
|
|
|
|
|
| 584 |
|
| 585 |
def get_job_result(j):
|
| 586 |
-
|
| 587 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
|
|
|
|
| 589 |
output=[]
|
|
|
|
| 590 |
|
| 591 |
-
for T_total,pub in zip(T_list,result):
|
|
|
|
|
|
|
| 592 |
|
| 593 |
-
# Try join_data() to combine multiple regs and get_counts() on it
|
| 594 |
try:
|
| 595 |
-
joined = pub.join_data()
|
| 596 |
joined_counts = joined.get_counts()
|
| 597 |
except Exception as e:
|
| 598 |
-
log(f"Error retrieving counts: {e}")
|
| 599 |
joined_counts = None
|
| 600 |
|
| 601 |
-
# Suppress verbose logging by passing None as logger
|
| 602 |
pts, counts = load_samples(joined_counts, T_total, logger=None, flag_qubits=flag_qubits)
|
| 603 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 604 |
|
| 605 |
log(f"Processing complete: {len(output)} timestep(s)")
|
|
|
|
|
|
|
| 606 |
fig = plot_density_isosurface_slider(output, T_list)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
return output, fig
|
| 608 |
|
| 609 |
-
return job,get_job_result
|
| 610 |
|
| 611 |
from qiskit_ionq import IonQProvider
|
| 612 |
|
|
@@ -623,7 +709,8 @@ def run_sampling_hw_ionq(
|
|
| 623 |
vel_resolution=32,
|
| 624 |
output_resolution=40,
|
| 625 |
logger=None,
|
| 626 |
-
flag_qubits=True
|
|
|
|
| 627 |
):
|
| 628 |
"""
|
| 629 |
Run QLBM simulation on IonQ quantum hardware.
|
|
@@ -646,6 +733,8 @@ def run_sampling_hw_ionq(
|
|
| 646 |
Grid resolution for density estimation output
|
| 647 |
logger : callable, optional
|
| 648 |
Function to log messages (e.g. print to console)
|
|
|
|
|
|
|
| 649 |
|
| 650 |
Returns
|
| 651 |
-------
|
|
@@ -654,6 +743,7 @@ def run_sampling_hw_ionq(
|
|
| 654 |
get_job_result : callable
|
| 655 |
Callback function to retrieve and process results. Returns (output, fig).
|
| 656 |
"""
|
|
|
|
| 657 |
|
| 658 |
def log(msg):
|
| 659 |
if logger:
|
|
@@ -661,49 +751,110 @@ def run_sampling_hw_ionq(
|
|
| 661 |
else:
|
| 662 |
print(msg)
|
| 663 |
|
| 664 |
-
|
| 665 |
-
|
|
|
|
| 666 |
|
| 667 |
-
#
|
| 668 |
-
|
| 669 |
-
|
| 670 |
|
| 671 |
# backend = provider.get_backend("simulator")
|
| 672 |
backend = provider.get_backend("qpu.forte-enterprise-1")
|
|
|
|
|
|
|
| 673 |
|
| 674 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,flag_qubits=flag_qubits,midcircuit_meas=False)
|
|
|
|
|
|
|
|
|
|
| 675 |
|
| 676 |
-
#
|
|
|
|
|
|
|
| 677 |
|
| 678 |
job = backend.run(qc_list, shots=shots)
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
# sampler = Sampler(mode=backend)
|
| 683 |
-
|
| 684 |
-
# # Submit job: pass a list of PUBs (we send one PUB [qc_compiled])
|
| 685 |
-
# job = sampler.run(qc_compiled_list, shots=shots)
|
| 686 |
-
log("Job submitted; waiting for result...")
|
| 687 |
|
| 688 |
def get_job_result(j):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
|
| 690 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 691 |
|
|
|
|
|
|
|
| 692 |
output=[]
|
|
|
|
| 693 |
|
| 694 |
-
for i,T_total in enumerate(T_list):
|
|
|
|
|
|
|
| 695 |
|
| 696 |
counts = j.get_counts(i)
|
| 697 |
-
|
| 698 |
-
# Suppress verbose logging by passing None as logger
|
| 699 |
pts, counts = load_samples(counts, T_total, logger=None, flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 700 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 701 |
|
| 702 |
log(f"Processing complete: {len(output)} timestep(s)")
|
|
|
|
|
|
|
| 703 |
fig = plot_density_isosurface_slider(output, T_list)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 704 |
return output, fig
|
| 705 |
|
| 706 |
-
return job,get_job_result
|
| 707 |
|
| 708 |
|
| 709 |
|
|
|
|
| 513 |
vel_resolution=32,
|
| 514 |
output_resolution=40,
|
| 515 |
logger=None,
|
| 516 |
+
flag_qubits=True,
|
| 517 |
+
progress_callback=None,
|
| 518 |
):
|
| 519 |
"""
|
| 520 |
Run QLBM simulation on IBM quantum hardware.
|
|
|
|
| 537 |
Grid resolution for density estimation output
|
| 538 |
logger : callable, optional
|
| 539 |
Function to log messages (e.g. print to console)
|
| 540 |
+
progress_callback : callable, optional
|
| 541 |
+
Function to report progress (0-100) with optional status message: progress_callback(percent, message)
|
| 542 |
|
| 543 |
Returns
|
| 544 |
-------
|
|
|
|
| 547 |
get_job_result : callable
|
| 548 |
Callback function to retrieve and process results. Returns (output, fig).
|
| 549 |
"""
|
| 550 |
+
import time as time_module
|
| 551 |
|
| 552 |
def log(msg):
|
| 553 |
if logger:
|
|
|
|
| 555 |
else:
|
| 556 |
print(msg)
|
| 557 |
|
| 558 |
+
def update_progress(percent, message=None):
|
| 559 |
+
if progress_callback:
|
| 560 |
+
progress_callback(percent, message)
|
| 561 |
|
| 562 |
+
# === STEP 1: Circuit Generation (0-50%) ===
|
| 563 |
+
log("Step 1: Generating quantum circuits...")
|
| 564 |
+
update_progress(5, "Generating quantum circuits...")
|
| 565 |
|
| 566 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,flag_qubits=flag_qubits)
|
| 567 |
+
|
| 568 |
+
log(f"Generated {len(qc_list)} circuit(s) for timesteps {T_list}")
|
| 569 |
+
update_progress(15, f"Generated {len(qc_list)} circuits")
|
| 570 |
|
| 571 |
pm_optimization_level = 3
|
| 572 |
|
| 573 |
+
log("Connecting to IBM Quantum service...")
|
| 574 |
+
update_progress(20, "Connecting to IBM Quantum...")
|
| 575 |
+
|
| 576 |
+
service = QiskitRuntimeService(channel="ibm_cloud", token="UMeZUDI5D7fjPJHD5x3MJFwURg4PrGzBnTm142ka9-Hj",instance="crn:v1:bluemix:public:quantum-computing:us-east:a/15157e4350c04a9dab51b8b8a4a93c86:e29afd91-64bf-4a82-8dbf-731e6c213595::")
|
| 577 |
backend = service.least_busy()
|
| 578 |
+
log(f"Selected backend: {backend.name}")
|
| 579 |
+
update_progress(25, f"Backend: {backend.name}")
|
| 580 |
|
| 581 |
qc_compiled_list=[]
|
| 582 |
+
total_circuits = len(qc_list)
|
| 583 |
|
| 584 |
+
for idx, qc in enumerate(qc_list):
|
| 585 |
+
circuit_progress = 25 + (idx / total_circuits) * 20 # 25-45%
|
| 586 |
+
update_progress(circuit_progress, f"Transpiling circuit {idx+1}/{total_circuits}...")
|
| 587 |
+
|
| 588 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 589 |
+
log(f"Transpiling circuit {idx+1}/{total_circuits} via PassManager...")
|
| 590 |
+
qc_compiled = pm.run(qc)
|
| 591 |
+
log(f" Compiled: {qc_compiled.num_qubits} qubits, {qc_compiled.num_clbits} clbits, depth={qc_compiled.depth()}")
|
|
|
|
| 592 |
qc_compiled_list+=[qc_compiled]
|
| 593 |
|
| 594 |
+
log("All circuits transpiled successfully.")
|
| 595 |
+
update_progress(50, "Circuits ready. Submitting job...")
|
| 596 |
|
| 597 |
+
# === STEP 2: Job Submission & Monitoring (50-90%) ===
|
| 598 |
+
sampler = Sampler(mode=backend)
|
| 599 |
job = sampler.run(qc_compiled_list, shots=shots)
|
| 600 |
+
job_id = job.job_id() if hasattr(job, 'job_id') else str(job)
|
| 601 |
+
log(f"Job submitted! Job ID: {job_id}")
|
| 602 |
+
update_progress(52, f"Job submitted: {job_id}")
|
| 603 |
|
| 604 |
def get_job_result(j):
|
| 605 |
+
"""Poll job status and retrieve results with progress updates."""
|
| 606 |
+
# Job status polling with progress updates
|
| 607 |
+
log("Monitoring job status...")
|
| 608 |
+
update_progress(55, "Job queued, waiting for execution...")
|
| 609 |
+
|
| 610 |
+
# Status mapping for progress estimation
|
| 611 |
+
# JobStatus: INITIALIZING, QUEUED, VALIDATING, RUNNING, CANCELLED, DONE, ERROR
|
| 612 |
+
status_progress_map = {
|
| 613 |
+
'INITIALIZING': (55, "Job initializing..."),
|
| 614 |
+
'QUEUED': (58, "Job queued, waiting..."),
|
| 615 |
+
'VALIDATING': (62, "Validating job..."),
|
| 616 |
+
'RUNNING': (70, "Job running on QPU..."),
|
| 617 |
+
'DONE': (85, "Job completed!"),
|
| 618 |
+
'ERROR': (85, "Job error occurred"),
|
| 619 |
+
'CANCELLED': (85, "Job cancelled"),
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
last_status = None
|
| 623 |
+
poll_count = 0
|
| 624 |
+
max_polls = 600 # ~10 minutes with 1s interval
|
| 625 |
+
|
| 626 |
+
while poll_count < max_polls:
|
| 627 |
+
try:
|
| 628 |
+
status = j.status()
|
| 629 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 630 |
+
|
| 631 |
+
if status_name != last_status:
|
| 632 |
+
last_status = status_name
|
| 633 |
+
progress_val, status_msg = status_progress_map.get(status_name, (60, f"Status: {status_name}"))
|
| 634 |
+
log(f"Job Status: {status_name}")
|
| 635 |
+
update_progress(progress_val, status_msg)
|
| 636 |
+
|
| 637 |
+
# Check if job is complete
|
| 638 |
+
if status_name in ('DONE', 'ERROR', 'CANCELLED'):
|
| 639 |
+
break
|
| 640 |
+
|
| 641 |
+
# Increment progress slightly while waiting (indeterminate feel)
|
| 642 |
+
if status_name == 'QUEUED':
|
| 643 |
+
# Slowly increment between 58-65 while queued
|
| 644 |
+
queue_progress = 58 + min(7, poll_count * 0.05)
|
| 645 |
+
update_progress(queue_progress, f"Queued... (waiting {poll_count}s)")
|
| 646 |
+
elif status_name == 'RUNNING':
|
| 647 |
+
# Slowly increment between 70-85 while running
|
| 648 |
+
run_progress = 70 + min(15, poll_count * 0.1)
|
| 649 |
+
update_progress(run_progress, f"Running on QPU... ({poll_count}s)")
|
| 650 |
+
|
| 651 |
+
time_module.sleep(1)
|
| 652 |
+
poll_count += 1
|
| 653 |
+
|
| 654 |
+
except Exception as e:
|
| 655 |
+
log(f"Status check error: {e}")
|
| 656 |
+
time_module.sleep(2)
|
| 657 |
+
poll_count += 2
|
| 658 |
+
|
| 659 |
+
# Get results
|
| 660 |
+
log("Retrieving job results...")
|
| 661 |
+
update_progress(87, "Retrieving results...")
|
| 662 |
+
|
| 663 |
+
result = j.result()
|
| 664 |
+
log("Results retrieved successfully.")
|
| 665 |
+
update_progress(90, "Processing results...")
|
| 666 |
|
| 667 |
+
# === STEP 3: Creating Plots (90-100%) ===
|
| 668 |
output=[]
|
| 669 |
+
total_timesteps = len(T_list)
|
| 670 |
|
| 671 |
+
for idx, (T_total, pub) in enumerate(zip(T_list, result)):
|
| 672 |
+
plot_progress = 90 + (idx / total_timesteps) * 8 # 90-98%
|
| 673 |
+
update_progress(plot_progress, f"Processing timestep T={T_total}...")
|
| 674 |
|
|
|
|
| 675 |
try:
|
| 676 |
+
joined = pub.join_data()
|
| 677 |
joined_counts = joined.get_counts()
|
| 678 |
except Exception as e:
|
| 679 |
+
log(f"Error retrieving counts for T={T_total}: {e}")
|
| 680 |
joined_counts = None
|
| 681 |
|
|
|
|
| 682 |
pts, counts = load_samples(joined_counts, T_total, logger=None, flag_qubits=flag_qubits)
|
| 683 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 684 |
|
| 685 |
log(f"Processing complete: {len(output)} timestep(s)")
|
| 686 |
+
update_progress(98, "Creating visualization...")
|
| 687 |
+
|
| 688 |
fig = plot_density_isosurface_slider(output, T_list)
|
| 689 |
+
|
| 690 |
+
update_progress(100, "Complete!")
|
| 691 |
+
log("IBM QPU job completed successfully.")
|
| 692 |
+
|
| 693 |
return output, fig
|
| 694 |
|
| 695 |
+
return job, get_job_result
|
| 696 |
|
| 697 |
from qiskit_ionq import IonQProvider
|
| 698 |
|
|
|
|
| 709 |
vel_resolution=32,
|
| 710 |
output_resolution=40,
|
| 711 |
logger=None,
|
| 712 |
+
flag_qubits=True,
|
| 713 |
+
progress_callback=None,
|
| 714 |
):
|
| 715 |
"""
|
| 716 |
Run QLBM simulation on IonQ quantum hardware.
|
|
|
|
| 733 |
Grid resolution for density estimation output
|
| 734 |
logger : callable, optional
|
| 735 |
Function to log messages (e.g. print to console)
|
| 736 |
+
progress_callback : callable, optional
|
| 737 |
+
Function to report progress (0-100) with optional status message: progress_callback(percent, message)
|
| 738 |
|
| 739 |
Returns
|
| 740 |
-------
|
|
|
|
| 743 |
get_job_result : callable
|
| 744 |
Callback function to retrieve and process results. Returns (output, fig).
|
| 745 |
"""
|
| 746 |
+
import time as time_module
|
| 747 |
|
| 748 |
def log(msg):
|
| 749 |
if logger:
|
|
|
|
| 751 |
else:
|
| 752 |
print(msg)
|
| 753 |
|
| 754 |
+
def update_progress(percent, message=None):
|
| 755 |
+
if progress_callback:
|
| 756 |
+
progress_callback(percent, message)
|
| 757 |
|
| 758 |
+
# === STEP 1: Circuit Generation (0-50%) ===
|
| 759 |
+
log("Step 1: Generating quantum circuits...")
|
| 760 |
+
update_progress(5, "Generating quantum circuits...")
|
| 761 |
|
| 762 |
# backend = provider.get_backend("simulator")
|
| 763 |
backend = provider.get_backend("qpu.forte-enterprise-1")
|
| 764 |
+
log(f"Selected IonQ backend: {backend.name()}")
|
| 765 |
+
update_progress(15, f"Backend: {backend.name()}")
|
| 766 |
|
| 767 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,flag_qubits=flag_qubits,midcircuit_meas=False)
|
| 768 |
+
|
| 769 |
+
log(f"Generated {len(qc_list)} circuit(s) for timesteps {T_list}")
|
| 770 |
+
update_progress(45, f"Generated {len(qc_list)} circuits")
|
| 771 |
|
| 772 |
+
# === STEP 2: Job Submission (50%) ===
|
| 773 |
+
log("Submitting job to IonQ...")
|
| 774 |
+
update_progress(50, "Submitting job to IonQ...")
|
| 775 |
|
| 776 |
job = backend.run(qc_list, shots=shots)
|
| 777 |
+
job_id = job.job_id() if hasattr(job, 'job_id') else str(job)
|
| 778 |
+
log(f"Job submitted! Job ID: {job_id}")
|
| 779 |
+
update_progress(52, f"Job submitted: {job_id}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 780 |
|
| 781 |
def get_job_result(j):
|
| 782 |
+
"""Poll job status and retrieve results with progress updates."""
|
| 783 |
+
log("Monitoring IonQ job status...")
|
| 784 |
+
update_progress(55, "Job queued, waiting for execution...")
|
| 785 |
+
|
| 786 |
+
# IonQ job status polling
|
| 787 |
+
last_status = None
|
| 788 |
+
poll_count = 0
|
| 789 |
+
max_polls = 600 # ~10 minutes with 1s interval
|
| 790 |
|
| 791 |
+
while poll_count < max_polls:
|
| 792 |
+
try:
|
| 793 |
+
status = j.status()
|
| 794 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 795 |
+
|
| 796 |
+
if status_name != last_status:
|
| 797 |
+
last_status = status_name
|
| 798 |
+
log(f"Job Status: {status_name}")
|
| 799 |
+
|
| 800 |
+
if status_name in ('QUEUED', 'VALIDATING'):
|
| 801 |
+
update_progress(58, f"Status: {status_name}")
|
| 802 |
+
elif status_name == 'RUNNING':
|
| 803 |
+
update_progress(70, "Job running on IonQ QPU...")
|
| 804 |
+
elif status_name == 'DONE':
|
| 805 |
+
update_progress(85, "Job completed!")
|
| 806 |
+
break
|
| 807 |
+
elif status_name in ('ERROR', 'CANCELLED'):
|
| 808 |
+
update_progress(85, f"Job {status_name.lower()}")
|
| 809 |
+
break
|
| 810 |
+
|
| 811 |
+
# Increment progress slightly while waiting
|
| 812 |
+
if status_name == 'QUEUED':
|
| 813 |
+
queue_progress = 58 + min(7, poll_count * 0.05)
|
| 814 |
+
update_progress(queue_progress, f"Queued... (waiting {poll_count}s)")
|
| 815 |
+
elif status_name == 'RUNNING':
|
| 816 |
+
run_progress = 70 + min(15, poll_count * 0.1)
|
| 817 |
+
update_progress(run_progress, f"Running on IonQ... ({poll_count}s)")
|
| 818 |
+
|
| 819 |
+
# Check if done
|
| 820 |
+
if status_name in ('DONE', 'ERROR', 'CANCELLED'):
|
| 821 |
+
break
|
| 822 |
+
|
| 823 |
+
time_module.sleep(1)
|
| 824 |
+
poll_count += 1
|
| 825 |
+
|
| 826 |
+
except Exception as e:
|
| 827 |
+
log(f"Status check error: {e}")
|
| 828 |
+
time_module.sleep(2)
|
| 829 |
+
poll_count += 2
|
| 830 |
+
|
| 831 |
+
log("Retrieving IonQ job results...")
|
| 832 |
+
update_progress(87, "Retrieving results...")
|
| 833 |
|
| 834 |
+
# === STEP 3: Creating Plots (90-100%) ===
|
| 835 |
+
update_progress(90, "Processing results...")
|
| 836 |
output=[]
|
| 837 |
+
total_timesteps = len(T_list)
|
| 838 |
|
| 839 |
+
for i, T_total in enumerate(T_list):
|
| 840 |
+
plot_progress = 90 + (i / total_timesteps) * 8 # 90-98%
|
| 841 |
+
update_progress(plot_progress, f"Processing timestep T={T_total}...")
|
| 842 |
|
| 843 |
counts = j.get_counts(i)
|
|
|
|
|
|
|
| 844 |
pts, counts = load_samples(counts, T_total, logger=None, flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 845 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 846 |
|
| 847 |
log(f"Processing complete: {len(output)} timestep(s)")
|
| 848 |
+
update_progress(98, "Creating visualization...")
|
| 849 |
+
|
| 850 |
fig = plot_density_isosurface_slider(output, T_list)
|
| 851 |
+
|
| 852 |
+
update_progress(100, "Complete!")
|
| 853 |
+
log("IonQ job completed successfully.")
|
| 854 |
+
|
| 855 |
return output, fig
|
| 856 |
|
| 857 |
+
return job, get_job_result
|
| 858 |
|
| 859 |
|
| 860 |
|
qlbm_embedded.py
CHANGED
|
@@ -1465,6 +1465,13 @@ async def _run_simulation_async():
|
|
| 1465 |
last_logged_percent[0] = percent
|
| 1466 |
_qlbm_flush_state_threadsafe() # Thread-safe flush!
|
| 1467 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1468 |
try:
|
| 1469 |
# === Qiskit Backend (IBM Qiskit Simulator) ===
|
| 1470 |
if use_qiskit:
|
|
@@ -1501,16 +1508,17 @@ async def _run_simulation_async():
|
|
| 1501 |
# === IBM QPU Backend ===
|
| 1502 |
elif use_ibm_qpu:
|
| 1503 |
log_to_console("Using IBM QPU backend...")
|
| 1504 |
-
_state.qlbm_status_message = "Preparing IBM QPU job..."
|
|
|
|
| 1505 |
await _qlbm_flush_async()
|
| 1506 |
|
| 1507 |
params = _map_state_to_qiskit_params()
|
| 1508 |
if params is None:
|
| 1509 |
raise RuntimeError("Failed to map state parameters")
|
| 1510 |
|
| 1511 |
-
# Create initial state circuit
|
| 1512 |
log_to_console("Creating initial state circuit...")
|
| 1513 |
-
_state.qlbm_simulation_progress =
|
| 1514 |
await _qlbm_flush_async()
|
| 1515 |
|
| 1516 |
init_state_prep_circ = get_named_init_state_circuit(
|
|
@@ -1528,14 +1536,12 @@ async def _run_simulation_async():
|
|
| 1528 |
mdd_kz_log2=params["mdd_kz_log2"],
|
| 1529 |
)
|
| 1530 |
|
| 1531 |
-
|
| 1532 |
-
_state.qlbm_status_message = "
|
| 1533 |
-
_state.qlbm_simulation_progress = 20
|
| 1534 |
await _qlbm_flush_async()
|
| 1535 |
|
| 1536 |
-
# Run HW simulation in executor
|
| 1537 |
def _run_ibm_qpu_blocking():
|
| 1538 |
-
log_to_console("Submitting and running IBM QPU job...")
|
| 1539 |
job, get_result = run_sampling_hw_ibm(
|
| 1540 |
n=params["n"],
|
| 1541 |
ux=params["vx_expr"],
|
|
@@ -1546,19 +1552,18 @@ async def _run_simulation_async():
|
|
| 1546 |
shots=2**14,
|
| 1547 |
vel_resolution=min(params['grid_size'], 32),
|
| 1548 |
output_resolution=min(2*params['grid_size'], 40),
|
| 1549 |
-
logger=log_to_console
|
|
|
|
| 1550 |
)
|
| 1551 |
-
|
| 1552 |
output, plotly_fig = get_result(job)
|
| 1553 |
return output, plotly_fig, init_state_prep_circ
|
| 1554 |
|
| 1555 |
-
_state.qlbm_status_message = "Waiting for IBM QPU results..."
|
| 1556 |
-
_state.qlbm_simulation_progress = 40
|
| 1557 |
-
await _qlbm_flush_async()
|
| 1558 |
-
|
| 1559 |
output, plotly_fig, init_state_prep_circ = await loop.run_in_executor(executor, _run_ibm_qpu_blocking)
|
| 1560 |
|
| 1561 |
-
|
|
|
|
|
|
|
| 1562 |
await _qlbm_flush_async()
|
| 1563 |
|
| 1564 |
# Generate T=0 initial snapshot and prepend to output
|
|
@@ -1618,16 +1623,17 @@ async def _run_simulation_async():
|
|
| 1618 |
# === IonQ QPU Backend ===
|
| 1619 |
elif use_ionq_qpu:
|
| 1620 |
log_to_console("Using IonQ QPU backend...")
|
| 1621 |
-
_state.qlbm_status_message = "Preparing IonQ QPU job..."
|
|
|
|
| 1622 |
await _qlbm_flush_async()
|
| 1623 |
|
| 1624 |
params = _map_state_to_qiskit_params()
|
| 1625 |
if params is None:
|
| 1626 |
raise RuntimeError("Failed to map state parameters")
|
| 1627 |
|
| 1628 |
-
# Create initial state circuit
|
| 1629 |
log_to_console("Creating initial state circuit...")
|
| 1630 |
-
_state.qlbm_simulation_progress =
|
| 1631 |
await _qlbm_flush_async()
|
| 1632 |
|
| 1633 |
init_state_prep_circ = get_named_init_state_circuit(
|
|
@@ -1645,14 +1651,12 @@ async def _run_simulation_async():
|
|
| 1645 |
mdd_kz_log2=params["mdd_kz_log2"],
|
| 1646 |
)
|
| 1647 |
|
| 1648 |
-
|
| 1649 |
-
_state.qlbm_status_message = "
|
| 1650 |
-
_state.qlbm_simulation_progress = 20
|
| 1651 |
await _qlbm_flush_async()
|
| 1652 |
|
| 1653 |
-
# Run IonQ HW simulation in executor
|
| 1654 |
def _run_ionq_qpu_blocking():
|
| 1655 |
-
log_to_console("Submitting and running IonQ QPU job...")
|
| 1656 |
job, get_result = run_sampling_hw_ionq(
|
| 1657 |
n=params["n"],
|
| 1658 |
ux=params["vx_expr"],
|
|
@@ -1663,19 +1667,18 @@ async def _run_simulation_async():
|
|
| 1663 |
shots=2**14,
|
| 1664 |
vel_resolution=min(params['grid_size'], 32),
|
| 1665 |
output_resolution=min(2*params['grid_size'], 40),
|
| 1666 |
-
logger=log_to_console
|
|
|
|
| 1667 |
)
|
| 1668 |
-
|
| 1669 |
output, plotly_fig = get_result(job)
|
| 1670 |
return output, plotly_fig, init_state_prep_circ
|
| 1671 |
|
| 1672 |
-
_state.qlbm_status_message = "Waiting for IonQ QPU results..."
|
| 1673 |
-
_state.qlbm_simulation_progress = 40
|
| 1674 |
-
await _qlbm_flush_async()
|
| 1675 |
-
|
| 1676 |
output, plotly_fig, init_state_prep_circ = await loop.run_in_executor(executor, _run_ionq_qpu_blocking)
|
| 1677 |
|
| 1678 |
-
|
|
|
|
|
|
|
| 1679 |
await _qlbm_flush_async()
|
| 1680 |
|
| 1681 |
# Generate T=0 initial snapshot and prepend to output
|
|
|
|
| 1465 |
last_logged_percent[0] = percent
|
| 1466 |
_qlbm_flush_state_threadsafe() # Thread-safe flush!
|
| 1467 |
|
| 1468 |
+
# QPU progress callback with status message support
|
| 1469 |
+
def _qpu_progress_callback(percent, message=None):
|
| 1470 |
+
_state.qlbm_simulation_progress = percent
|
| 1471 |
+
if message:
|
| 1472 |
+
_state.qlbm_status_message = message
|
| 1473 |
+
_qlbm_flush_state_threadsafe()
|
| 1474 |
+
|
| 1475 |
try:
|
| 1476 |
# === Qiskit Backend (IBM Qiskit Simulator) ===
|
| 1477 |
if use_qiskit:
|
|
|
|
| 1508 |
# === IBM QPU Backend ===
|
| 1509 |
elif use_ibm_qpu:
|
| 1510 |
log_to_console("Using IBM QPU backend...")
|
| 1511 |
+
_state.qlbm_status_message = "Step 1: Preparing IBM QPU job..."
|
| 1512 |
+
_state.qlbm_simulation_progress = 0
|
| 1513 |
await _qlbm_flush_async()
|
| 1514 |
|
| 1515 |
params = _map_state_to_qiskit_params()
|
| 1516 |
if params is None:
|
| 1517 |
raise RuntimeError("Failed to map state parameters")
|
| 1518 |
|
| 1519 |
+
# Create initial state circuit (part of Step 1)
|
| 1520 |
log_to_console("Creating initial state circuit...")
|
| 1521 |
+
_state.qlbm_simulation_progress = 2
|
| 1522 |
await _qlbm_flush_async()
|
| 1523 |
|
| 1524 |
init_state_prep_circ = get_named_init_state_circuit(
|
|
|
|
| 1536 |
mdd_kz_log2=params["mdd_kz_log2"],
|
| 1537 |
)
|
| 1538 |
|
| 1539 |
+
_state.qlbm_simulation_progress = 5
|
| 1540 |
+
_state.qlbm_status_message = "Step 1: Circuit generation..."
|
|
|
|
| 1541 |
await _qlbm_flush_async()
|
| 1542 |
|
| 1543 |
+
# Run HW simulation in executor with progress callback
|
| 1544 |
def _run_ibm_qpu_blocking():
|
|
|
|
| 1545 |
job, get_result = run_sampling_hw_ibm(
|
| 1546 |
n=params["n"],
|
| 1547 |
ux=params["vx_expr"],
|
|
|
|
| 1552 |
shots=2**14,
|
| 1553 |
vel_resolution=min(params['grid_size'], 32),
|
| 1554 |
output_resolution=min(2*params['grid_size'], 40),
|
| 1555 |
+
logger=log_to_console,
|
| 1556 |
+
progress_callback=_qpu_progress_callback,
|
| 1557 |
)
|
| 1558 |
+
# get_result already handles progress updates internally
|
| 1559 |
output, plotly_fig = get_result(job)
|
| 1560 |
return output, plotly_fig, init_state_prep_circ
|
| 1561 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1562 |
output, plotly_fig, init_state_prep_circ = await loop.run_in_executor(executor, _run_ibm_qpu_blocking)
|
| 1563 |
|
| 1564 |
+
# Step 3: Creating Plots is already handled in get_job_result, now add T=0 snapshot
|
| 1565 |
+
_state.qlbm_simulation_progress = 92
|
| 1566 |
+
_state.qlbm_status_message = "Step 3: Adding T=0 snapshot..."
|
| 1567 |
await _qlbm_flush_async()
|
| 1568 |
|
| 1569 |
# Generate T=0 initial snapshot and prepend to output
|
|
|
|
| 1623 |
# === IonQ QPU Backend ===
|
| 1624 |
elif use_ionq_qpu:
|
| 1625 |
log_to_console("Using IonQ QPU backend...")
|
| 1626 |
+
_state.qlbm_status_message = "Step 1: Preparing IonQ QPU job..."
|
| 1627 |
+
_state.qlbm_simulation_progress = 0
|
| 1628 |
await _qlbm_flush_async()
|
| 1629 |
|
| 1630 |
params = _map_state_to_qiskit_params()
|
| 1631 |
if params is None:
|
| 1632 |
raise RuntimeError("Failed to map state parameters")
|
| 1633 |
|
| 1634 |
+
# Create initial state circuit (part of Step 1)
|
| 1635 |
log_to_console("Creating initial state circuit...")
|
| 1636 |
+
_state.qlbm_simulation_progress = 2
|
| 1637 |
await _qlbm_flush_async()
|
| 1638 |
|
| 1639 |
init_state_prep_circ = get_named_init_state_circuit(
|
|
|
|
| 1651 |
mdd_kz_log2=params["mdd_kz_log2"],
|
| 1652 |
)
|
| 1653 |
|
| 1654 |
+
_state.qlbm_simulation_progress = 5
|
| 1655 |
+
_state.qlbm_status_message = "Step 1: Circuit generation..."
|
|
|
|
| 1656 |
await _qlbm_flush_async()
|
| 1657 |
|
| 1658 |
+
# Run IonQ HW simulation in executor with progress callback
|
| 1659 |
def _run_ionq_qpu_blocking():
|
|
|
|
| 1660 |
job, get_result = run_sampling_hw_ionq(
|
| 1661 |
n=params["n"],
|
| 1662 |
ux=params["vx_expr"],
|
|
|
|
| 1667 |
shots=2**14,
|
| 1668 |
vel_resolution=min(params['grid_size'], 32),
|
| 1669 |
output_resolution=min(2*params['grid_size'], 40),
|
| 1670 |
+
logger=log_to_console,
|
| 1671 |
+
progress_callback=_qpu_progress_callback,
|
| 1672 |
)
|
| 1673 |
+
# get_result already handles progress updates internally
|
| 1674 |
output, plotly_fig = get_result(job)
|
| 1675 |
return output, plotly_fig, init_state_prep_circ
|
| 1676 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1677 |
output, plotly_fig, init_state_prep_circ = await loop.run_in_executor(executor, _run_ionq_qpu_blocking)
|
| 1678 |
|
| 1679 |
+
# Step 3: Creating Plots is already handled in get_job_result, now add T=0 snapshot
|
| 1680 |
+
_state.qlbm_simulation_progress = 92
|
| 1681 |
+
_state.qlbm_status_message = "Step 3: Adding T=0 snapshot..."
|
| 1682 |
await _qlbm_flush_async()
|
| 1683 |
|
| 1684 |
# Generate T=0 initial snapshot and prepend to output
|
utils/EBU_Quantum/no_body/base_functions.py
CHANGED
|
@@ -277,25 +277,52 @@ def circuits_for_sign(field, x, y, nx, na, dt, R, initial_state, steps, xref, yr
|
|
| 277 |
|
| 278 |
return qc, qcdiff
|
| 279 |
|
| 280 |
-
def get_absolute_field_values(all_circuits, shots, pm_optimization_level, simulation = "True", platform = "IBM"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 281 |
jobs = {}
|
| 282 |
job_status = {key: "QUEUED" for key in jobs}
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
# if status == "DONE":
|
| 288 |
-
# pending.remove(key)
|
| 289 |
-
# time.sleep(2)
|
| 290 |
|
| 291 |
if simulation=="True":
|
| 292 |
backend = Aer.get_backend('qasm_simulator')
|
|
|
|
|
|
|
|
|
|
| 293 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 294 |
with Batch(backend=backend, max_time="2h") as batch:
|
| 295 |
estimator = Estimator()
|
| 296 |
-
for key, qc in all_circuits.items():
|
| 297 |
-
# Decompose high-level gates to avoid Rust panic
|
| 298 |
-
# qc = qc.decompose(["state_preparation", "initialize", "unitary", "isometry"])
|
| 299 |
qc = transpile(qc, basis_gates=['u', 'cx', 'rz', 'sx', 'x'])
|
| 300 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 301 |
qc_compiled = pm.run(qc)
|
|
@@ -303,11 +330,22 @@ def get_absolute_field_values(all_circuits, shots, pm_optimization_level, simula
|
|
| 303 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 304 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 305 |
jobs[key] = job
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
elif simulation == "False" and platform == "IBM":
|
|
|
|
|
|
|
|
|
|
| 307 |
service = QiskitRuntimeService(channel="ibm_cloud",
|
| 308 |
token="3Upysb5eOsRuBFlML0KV0uO4Jv4Lq01MhYsJ_Nu1pszy",
|
| 309 |
instance="crn:v1:bluemix:public:quantum-computing:us-east:a/15157e4350c04a9dab51b8b8a4a93c86:e29afd91-64bf-4a82-8dbf-731e6c213595::")
|
| 310 |
backend = service.least_busy(operational=True, simulator=False)
|
|
|
|
|
|
|
|
|
|
| 311 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 312 |
with Batch(backend=backend, max_time="2h") as batch:
|
| 313 |
estimator = Estimator()
|
|
@@ -318,9 +356,8 @@ def get_absolute_field_values(all_circuits, shots, pm_optimization_level, simula
|
|
| 318 |
estimator.options.resilience.zne_mitigation = True
|
| 319 |
estimator.options.resilience.zne.noise_factors = (1.1, 1.3, 1.5)
|
| 320 |
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
# qc = qc.decompose(["state_preparation", "initialize", "unitary", "isometry"])
|
| 324 |
qc = transpile(qc, basis_gates=['u', 'cx', 'rz', 'sx', 'x'])
|
| 325 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 326 |
qc_compiled = pm.run(qc)
|
|
@@ -328,30 +365,98 @@ def get_absolute_field_values(all_circuits, shots, pm_optimization_level, simula
|
|
| 328 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 329 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 330 |
jobs[key] = job
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
elif simulation == "False" and platform == "IONQ":
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
# ionq_backend.set_options(noise_model="ideal")
|
| 335 |
|
| 336 |
provider = IonQProvider(token = 'UzmBNbWXGKjyKrGyvwiw6E3rYhPdL8AU')
|
| 337 |
ionq_backend = provider.get_backend("qpu.forte-enterprise-1")
|
|
|
|
|
|
|
| 338 |
|
| 339 |
pm = generate_preset_pass_manager(backend=ionq_backend, optimization_level=1)
|
| 340 |
with Batch(backend=ionq_backend, max_time="2h") as batch:
|
| 341 |
estimator = Estimator()
|
| 342 |
-
for key, qc in all_circuits.items():
|
| 343 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 344 |
-
qc_compiled = transpile(qc,basis_gates=['ry','rz','rx','h','cx'])
|
| 345 |
layout = qc_compiled.layout
|
| 346 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 347 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 348 |
jobs[key] = job
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
#########################################################################################################
|
| 350 |
results = {}
|
| 351 |
-
for key, job in jobs.items():
|
| 352 |
res = job.result()[0]
|
| 353 |
z_exp = res.data.evs.item() # expectation value
|
| 354 |
results[key] = np.sqrt((1 - z_exp) / 2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
#########################################################################################
|
| 356 |
return results
|
| 357 |
|
|
@@ -590,7 +695,7 @@ def get_field_values(field, x, y, T, snapshot_time, nx, impulse_pos, shots, pm_o
|
|
| 590 |
except Exception:
|
| 591 |
pass
|
| 592 |
|
| 593 |
-
results = get_absolute_field_values(all_circuits, shots, pm_optimization_level, simulation, platform)
|
| 594 |
|
| 595 |
# --- Step 3: Result Processing (90-100%) ---
|
| 596 |
_log("Step 3: Result Processing")
|
|
|
|
| 277 |
|
| 278 |
return qc, qcdiff
|
| 279 |
|
| 280 |
+
def get_absolute_field_values(all_circuits, shots, pm_optimization_level, simulation = "True", platform = "IBM", progress_callback=None, print_callback=None):
|
| 281 |
+
"""
|
| 282 |
+
Execute circuits on quantum backend and return field values.
|
| 283 |
+
|
| 284 |
+
Parameters
|
| 285 |
+
----------
|
| 286 |
+
progress_callback : callable, optional
|
| 287 |
+
Function to report progress (40-90 range for Step 2 execution)
|
| 288 |
+
print_callback : callable, optional
|
| 289 |
+
Function for logging messages
|
| 290 |
+
"""
|
| 291 |
+
import time as time_module
|
| 292 |
+
|
| 293 |
+
def _log(msg):
|
| 294 |
+
if print_callback:
|
| 295 |
+
print_callback(msg)
|
| 296 |
+
|
| 297 |
+
def _progress(pct, message=None):
|
| 298 |
+
if progress_callback:
|
| 299 |
+
try:
|
| 300 |
+
progress_callback(pct, message)
|
| 301 |
+
except TypeError:
|
| 302 |
+
# Old-style callback without message parameter
|
| 303 |
+
try:
|
| 304 |
+
progress_callback(pct)
|
| 305 |
+
except Exception:
|
| 306 |
+
pass
|
| 307 |
+
except Exception:
|
| 308 |
+
pass
|
| 309 |
+
|
| 310 |
jobs = {}
|
| 311 |
job_status = {key: "QUEUED" for key in jobs}
|
| 312 |
+
|
| 313 |
+
total_circuits = len(all_circuits)
|
| 314 |
+
_log(f"Step 2: Submitting {total_circuits} circuit(s) for execution...")
|
| 315 |
+
_progress(40, f"Submitting {total_circuits} circuits...")
|
|
|
|
|
|
|
|
|
|
| 316 |
|
| 317 |
if simulation=="True":
|
| 318 |
backend = Aer.get_backend('qasm_simulator')
|
| 319 |
+
_log(f"Using simulator backend: {backend.name()}")
|
| 320 |
+
_progress(42, f"Backend: {backend.name()}")
|
| 321 |
+
|
| 322 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 323 |
with Batch(backend=backend, max_time="2h") as batch:
|
| 324 |
estimator = Estimator()
|
| 325 |
+
for idx, (key, qc) in enumerate(all_circuits.items()):
|
|
|
|
|
|
|
| 326 |
qc = transpile(qc, basis_gates=['u', 'cx', 'rz', 'sx', 'x'])
|
| 327 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 328 |
qc_compiled = pm.run(qc)
|
|
|
|
| 330 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 331 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 332 |
jobs[key] = job
|
| 333 |
+
|
| 334 |
+
# Progress: 42-50% for submission
|
| 335 |
+
submit_pct = 42 + ((idx + 1) / total_circuits) * 8
|
| 336 |
+
_progress(submit_pct, f"Submitted circuit {idx+1}/{total_circuits}")
|
| 337 |
+
|
| 338 |
elif simulation == "False" and platform == "IBM":
|
| 339 |
+
_log("Connecting to IBM Quantum service...")
|
| 340 |
+
_progress(42, "Connecting to IBM Quantum...")
|
| 341 |
+
|
| 342 |
service = QiskitRuntimeService(channel="ibm_cloud",
|
| 343 |
token="3Upysb5eOsRuBFlML0KV0uO4Jv4Lq01MhYsJ_Nu1pszy",
|
| 344 |
instance="crn:v1:bluemix:public:quantum-computing:us-east:a/15157e4350c04a9dab51b8b8a4a93c86:e29afd91-64bf-4a82-8dbf-731e6c213595::")
|
| 345 |
backend = service.least_busy(operational=True, simulator=False)
|
| 346 |
+
_log(f"Selected IBM QPU backend: {backend.name}")
|
| 347 |
+
_progress(45, f"Backend: {backend.name}")
|
| 348 |
+
|
| 349 |
pm = generate_preset_pass_manager(backend=backend, optimization_level=pm_optimization_level)
|
| 350 |
with Batch(backend=backend, max_time="2h") as batch:
|
| 351 |
estimator = Estimator()
|
|
|
|
| 356 |
estimator.options.resilience.zne_mitigation = True
|
| 357 |
estimator.options.resilience.zne.noise_factors = (1.1, 1.3, 1.5)
|
| 358 |
estimator.options.resilience.zne.extrapolator = ("exponential", "linear")
|
| 359 |
+
|
| 360 |
+
for idx, (key, qc) in enumerate(all_circuits.items()):
|
|
|
|
| 361 |
qc = transpile(qc, basis_gates=['u', 'cx', 'rz', 'sx', 'x'])
|
| 362 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 363 |
qc_compiled = pm.run(qc)
|
|
|
|
| 365 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 366 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 367 |
jobs[key] = job
|
| 368 |
+
|
| 369 |
+
# Progress: 45-55% for submission
|
| 370 |
+
submit_pct = 45 + ((idx + 1) / total_circuits) * 10
|
| 371 |
+
_progress(submit_pct, f"Submitted circuit {idx+1}/{total_circuits}")
|
| 372 |
+
|
| 373 |
elif simulation == "False" and platform == "IONQ":
|
| 374 |
+
_log("Connecting to IonQ service...")
|
| 375 |
+
_progress(42, "Connecting to IonQ...")
|
|
|
|
| 376 |
|
| 377 |
provider = IonQProvider(token = 'UzmBNbWXGKjyKrGyvwiw6E3rYhPdL8AU')
|
| 378 |
ionq_backend = provider.get_backend("qpu.forte-enterprise-1")
|
| 379 |
+
_log(f"Selected IonQ backend: {ionq_backend.name()}")
|
| 380 |
+
_progress(45, f"Backend: {ionq_backend.name()}")
|
| 381 |
|
| 382 |
pm = generate_preset_pass_manager(backend=ionq_backend, optimization_level=1)
|
| 383 |
with Batch(backend=ionq_backend, max_time="2h") as batch:
|
| 384 |
estimator = Estimator()
|
| 385 |
+
for idx, (key, qc) in enumerate(all_circuits.items()):
|
| 386 |
pauli_label = 'Z'+'I'*(qc.num_qubits-1)
|
| 387 |
+
qc_compiled = transpile(qc,basis_gates=['ry','rz','rx','h','cx'])
|
| 388 |
layout = qc_compiled.layout
|
| 389 |
observable = SparsePauliOp(Pauli(pauli_label)).apply_layout(layout)
|
| 390 |
job = estimator.run([(qc_compiled,observable)], precision=1/np.sqrt(shots))
|
| 391 |
jobs[key] = job
|
| 392 |
+
|
| 393 |
+
submit_pct = 45 + ((idx + 1) / total_circuits) * 10
|
| 394 |
+
_progress(submit_pct, f"Submitted circuit {idx+1}/{total_circuits}")
|
| 395 |
+
|
| 396 |
+
# Poll for job completion with status updates (55-85%)
|
| 397 |
+
_log("Jobs submitted. Waiting for results...")
|
| 398 |
+
_progress(55, "Jobs queued, waiting for execution...")
|
| 399 |
+
|
| 400 |
+
total_jobs = len(jobs)
|
| 401 |
+
completed_jobs = 0
|
| 402 |
+
job_keys = list(jobs.keys())
|
| 403 |
+
pending_keys = set(job_keys)
|
| 404 |
+
|
| 405 |
+
poll_count = 0
|
| 406 |
+
max_polls = 600 # ~10 minutes with 1s interval
|
| 407 |
+
|
| 408 |
+
while pending_keys and poll_count < max_polls:
|
| 409 |
+
for key in list(pending_keys):
|
| 410 |
+
try:
|
| 411 |
+
job = jobs[key]
|
| 412 |
+
status = job.status()
|
| 413 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 414 |
+
|
| 415 |
+
if status_name != job_status.get(key):
|
| 416 |
+
job_status[key] = status_name
|
| 417 |
+
_log(f"Job {key[:30]}... Status: {status_name}")
|
| 418 |
+
|
| 419 |
+
if status_name in ('DONE', 'ERROR', 'CANCELLED'):
|
| 420 |
+
pending_keys.discard(key)
|
| 421 |
+
completed_jobs += 1
|
| 422 |
+
# Progress: 55-85% based on completion
|
| 423 |
+
completion_pct = 55 + (completed_jobs / total_jobs) * 30
|
| 424 |
+
_progress(completion_pct, f"Completed {completed_jobs}/{total_jobs} jobs")
|
| 425 |
+
|
| 426 |
+
except Exception as e:
|
| 427 |
+
pass
|
| 428 |
+
|
| 429 |
+
if pending_keys:
|
| 430 |
+
# Show indeterminate progress while waiting
|
| 431 |
+
queued_count = sum(1 for s in job_status.values() if s in ('QUEUED', 'VALIDATING', 'INITIALIZING'))
|
| 432 |
+
running_count = sum(1 for s in job_status.values() if s == 'RUNNING')
|
| 433 |
+
|
| 434 |
+
if running_count > 0:
|
| 435 |
+
# Slowly increment while running (55-85 range)
|
| 436 |
+
run_progress = 55 + min(30, poll_count * 0.1)
|
| 437 |
+
_progress(run_progress, f"Running... ({running_count} active, {queued_count} queued)")
|
| 438 |
+
elif queued_count > 0:
|
| 439 |
+
queue_progress = 55 + min(10, poll_count * 0.05)
|
| 440 |
+
_progress(queue_progress, f"Queued... (waiting {poll_count}s)")
|
| 441 |
+
|
| 442 |
+
time_module.sleep(1)
|
| 443 |
+
poll_count += 1
|
| 444 |
+
|
| 445 |
+
_log("All jobs completed. Retrieving results...")
|
| 446 |
+
_progress(87, "Retrieving results...")
|
| 447 |
+
|
| 448 |
#########################################################################################################
|
| 449 |
results = {}
|
| 450 |
+
for idx, (key, job) in enumerate(jobs.items()):
|
| 451 |
res = job.result()[0]
|
| 452 |
z_exp = res.data.evs.item() # expectation value
|
| 453 |
results[key] = np.sqrt((1 - z_exp) / 2)
|
| 454 |
+
|
| 455 |
+
# Progress: 87-90% for result retrieval
|
| 456 |
+
result_pct = 87 + ((idx + 1) / total_jobs) * 3
|
| 457 |
+
_progress(result_pct, f"Retrieved result {idx+1}/{total_jobs}")
|
| 458 |
+
|
| 459 |
+
_progress(90, "All results retrieved")
|
| 460 |
#########################################################################################
|
| 461 |
return results
|
| 462 |
|
|
|
|
| 695 |
except Exception:
|
| 696 |
pass
|
| 697 |
|
| 698 |
+
results = get_absolute_field_values(all_circuits, shots, pm_optimization_level, simulation, platform, progress_callback=progress_callback, print_callback=print_callback)
|
| 699 |
|
| 700 |
# --- Step 3: Result Processing (90-100%) ---
|
| 701 |
_log("Step 3: Result Processing")
|