Spaces:
Runtime error
Runtime error
harishaseebat92 commited on
Commit ·
b112afa
1
Parent(s): f4fc516
QLBM :IBM Jobs and Removed JSON file
Browse files- qlbm_embedded.py +237 -137
- requirements.txt +0 -0
qlbm_embedded.py
CHANGED
|
@@ -313,18 +313,16 @@ def init_state():
|
|
| 313 |
"qlbm_qiskit_backend_available": _QISKIT_BACKEND_AVAILABLE,
|
| 314 |
"qlbm_qiskit_fig": None, # Stores the Plotly figure for Qiskit results
|
| 315 |
|
| 316 |
-
# Job
|
| 317 |
-
"
|
| 318 |
-
"
|
| 319 |
-
"
|
| 320 |
-
"qlbm_job_upload_success": "", # Success message for upload
|
| 321 |
-
"qlbm_job_platform": "IonQ", # Platform: IonQ only now
|
| 322 |
"qlbm_job_id": "", # Job ID text field for direct entry
|
| 323 |
"qlbm_job_total_time": 3, # Total time T (generates T_list = [1..T])
|
| 324 |
"qlbm_job_output_resolution": 40, # Grid resolution for density estimation
|
| 325 |
-
"qlbm_job_is_processing": False, # True when processing
|
| 326 |
"qlbm_job_flag_qubits": True, # Whether flag qubits were used
|
| 327 |
-
"qlbm_job_midcircuit_meas": False, # IonQ uses
|
| 328 |
})
|
| 329 |
_initialized = True
|
| 330 |
|
|
@@ -1200,12 +1198,12 @@ def _run_qiskit_simulation(progress_callback=None):
|
|
| 1200 |
# --- Job Result Upload Processing ---
|
| 1201 |
def process_uploaded_job_result():
|
| 1202 |
"""
|
| 1203 |
-
Process an IonQ job by retrieving it directly using the Job ID.
|
| 1204 |
|
| 1205 |
This function:
|
| 1206 |
1. Takes the Job ID from user input (or extracts from uploaded filename)
|
| 1207 |
-
2. Connects to IonQ and retrieves the job
|
| 1208 |
-
3.
|
| 1209 |
4. Calls load_samples/estimate_density for each timestep
|
| 1210 |
5. Generates the slider figure using plot_density_isosurface_slider
|
| 1211 |
"""
|
|
@@ -1220,41 +1218,28 @@ def process_uploaded_job_result():
|
|
| 1220 |
log_to_console("Error: visualize_counts module not available")
|
| 1221 |
return
|
| 1222 |
|
| 1223 |
-
# Get job ID
|
| 1224 |
job_id = None
|
| 1225 |
|
| 1226 |
-
# First try the text field
|
| 1227 |
if _state.qlbm_job_id and str(_state.qlbm_job_id).strip():
|
| 1228 |
job_id = str(_state.qlbm_job_id).strip()
|
| 1229 |
# Remove .json extension if present
|
| 1230 |
if job_id.endswith(".json"):
|
| 1231 |
job_id = job_id[:-5]
|
| 1232 |
log_to_console(f"Using Job ID from text field: {job_id}")
|
| 1233 |
-
else:
|
| 1234 |
-
# Fallback: try to get from uploaded file name
|
| 1235 |
-
uploaded = _state.qlbm_job_upload
|
| 1236 |
-
if uploaded:
|
| 1237 |
-
file_data = uploaded[0] if isinstance(uploaded, list) else uploaded
|
| 1238 |
-
filename = file_data.get("name", "") if isinstance(file_data, dict) else ""
|
| 1239 |
-
if filename:
|
| 1240 |
-
# Extract job ID from filename (remove .json and any extra text)
|
| 1241 |
-
job_id = filename.replace(".json", "").strip()
|
| 1242 |
-
# Handle filenames like "019b368e-6e22-7525-8512-fd16e0503673 (1).json"
|
| 1243 |
-
# Remove any trailing parenthetical like " (1)"
|
| 1244 |
-
import re
|
| 1245 |
-
job_id = re.sub(r'\s*\(\d+\)\s*$', '', job_id)
|
| 1246 |
-
log_to_console(f"Extracted Job ID from filename: {job_id}")
|
| 1247 |
|
| 1248 |
if not job_id:
|
| 1249 |
-
_state.qlbm_job_upload_error = "No Job ID provided. Please enter a Job ID
|
| 1250 |
return
|
| 1251 |
|
|
|
|
|
|
|
|
|
|
| 1252 |
# Reset messages
|
| 1253 |
_state.qlbm_job_upload_error = ""
|
| 1254 |
_state.qlbm_job_upload_success = ""
|
| 1255 |
_state.qlbm_job_is_processing = True
|
| 1256 |
-
|
| 1257 |
-
log_to_console(f"Processing IonQ Job ID: {job_id}")
|
| 1258 |
|
| 1259 |
try:
|
| 1260 |
# Parse timesteps from user input
|
|
@@ -1272,102 +1257,219 @@ def process_uploaded_job_result():
|
|
| 1272 |
|
| 1273 |
# Get processing parameters
|
| 1274 |
output_resolution = int(_state.qlbm_job_output_resolution or 40)
|
| 1275 |
-
# Fixed parameters for IonQ (same as run_sampling_hw_ionq)
|
| 1276 |
-
flag_qubits = True
|
| 1277 |
-
midcircuit_meas = False # IonQ uses midcircuit_meas=False
|
| 1278 |
-
|
| 1279 |
-
log_to_console(f"Resolution: {output_resolution}, Flag qubits: {flag_qubits}, Midcircuit meas: {midcircuit_meas}")
|
| 1280 |
-
|
| 1281 |
-
# === Connect to IonQ and retrieve the job ===
|
| 1282 |
-
log_to_console("Connecting to IonQ...")
|
| 1283 |
-
|
| 1284 |
-
try:
|
| 1285 |
-
from qiskit_ionq import IonQProvider
|
| 1286 |
-
except ImportError:
|
| 1287 |
-
_state.qlbm_job_upload_error = "qiskit_ionq package not available. Please install it."
|
| 1288 |
-
_state.qlbm_job_is_processing = False
|
| 1289 |
-
log_to_console("Error: qiskit_ionq not installed")
|
| 1290 |
-
return
|
| 1291 |
-
|
| 1292 |
-
# Get API token from environment (same pattern as run_sampling_hw_ionq)
|
| 1293 |
-
ionq_token = os.environ.get("API_KEY_IONQ_QLBM") or os.environ.get("IONQ_API_TOKEN")
|
| 1294 |
-
if not ionq_token:
|
| 1295 |
-
_state.qlbm_job_upload_error = "IonQ API token not found. Set API_KEY_IONQ_QLBM environment variable."
|
| 1296 |
-
_state.qlbm_job_is_processing = False
|
| 1297 |
-
log_to_console("Error: IonQ API token not found in environment")
|
| 1298 |
-
return
|
| 1299 |
|
| 1300 |
-
#
|
| 1301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1302 |
|
| 1303 |
-
|
| 1304 |
-
provider = IonQProvider()
|
| 1305 |
-
backend = provider.get_backend("qpu.forte-enterprise-1")
|
| 1306 |
-
backend_name = backend.name if isinstance(backend.name, str) else backend.name()
|
| 1307 |
-
log_to_console(f"Connected to IonQ backend: {backend_name}")
|
| 1308 |
|
| 1309 |
-
|
| 1310 |
-
log_to_console(f"Retrieving job: {job_id}")
|
| 1311 |
-
try:
|
| 1312 |
-
job = backend.retrieve_job(job_id)
|
| 1313 |
-
except Exception as e:
|
| 1314 |
-
_state.qlbm_job_upload_error = f"Failed to retrieve job: {e}"
|
| 1315 |
-
_state.qlbm_job_is_processing = False
|
| 1316 |
-
log_to_console(f"Error retrieving job: {e}")
|
| 1317 |
-
return
|
| 1318 |
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
| 1322 |
-
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 1323 |
-
log_to_console(f"Job status: {status_name}")
|
| 1324 |
|
| 1325 |
-
|
| 1326 |
-
|
|
|
|
|
|
|
| 1327 |
_state.qlbm_job_is_processing = False
|
|
|
|
| 1328 |
return
|
| 1329 |
-
|
| 1330 |
-
|
| 1331 |
-
|
| 1332 |
-
|
| 1333 |
-
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
|
|
|
|
|
|
| 1337 |
try:
|
| 1338 |
-
|
| 1339 |
-
|
| 1340 |
-
|
| 1341 |
-
|
| 1342 |
-
log_to_console(f" Retrieved {len(counts)} unique bitstrings")
|
| 1343 |
-
|
| 1344 |
-
# Debug: show a few sample bitstrings
|
| 1345 |
-
sample_count = 0
|
| 1346 |
-
for bs, cnt in counts.items():
|
| 1347 |
-
if sample_count < 3:
|
| 1348 |
-
log_to_console(f" Sample: {bs} (count={cnt})")
|
| 1349 |
-
sample_count += 1
|
| 1350 |
-
|
| 1351 |
-
# Process samples (same as run_sampling_hw_ionq)
|
| 1352 |
-
pts, processed_counts = load_samples(
|
| 1353 |
-
counts, T_total,
|
| 1354 |
-
logger=log_to_console,
|
| 1355 |
-
flag_qubits=flag_qubits,
|
| 1356 |
-
midcircuit_meas=midcircuit_meas
|
| 1357 |
)
|
| 1358 |
-
log_to_console(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1359 |
|
| 1360 |
-
|
| 1361 |
-
|
| 1362 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1363 |
|
| 1364 |
-
|
| 1365 |
-
|
| 1366 |
-
|
|
|
|
| 1367 |
except Exception as e:
|
| 1368 |
-
log_to_console(f"
|
| 1369 |
-
|
| 1370 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1371 |
|
| 1372 |
if not output:
|
| 1373 |
_state.qlbm_job_upload_error = "No valid data extracted from job. Check timesteps parameter."
|
|
@@ -1383,7 +1485,7 @@ def process_uploaded_job_result():
|
|
| 1383 |
_state.qlbm_qiskit_mode = True
|
| 1384 |
_state.qlbm_qiskit_fig = fig
|
| 1385 |
_state.qlbm_simulation_has_run = True
|
| 1386 |
-
_state.qlbm_job_upload_success = f"✓ Successfully processed {len(output)} timestep(s) from job {job_id}"
|
| 1387 |
|
| 1388 |
# Update the Plotly figure widget
|
| 1389 |
if hasattr(_ctrl, "qlbm_qiskit_result_update"):
|
|
@@ -2393,16 +2495,32 @@ def _build_control_panels(plotter):
|
|
| 2393 |
|
| 2394 |
# --- Job Result Upload Section ---
|
| 2395 |
vuetify3.VDivider(classes="my-3")
|
| 2396 |
-
html.Div("
|
| 2397 |
-
html.Div("
|
| 2398 |
classes="text-caption text-medium-emphasis mb-2")
|
| 2399 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2400 |
# Job ID input
|
| 2401 |
with vuetify3.VTooltip(location="top"):
|
| 2402 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 2403 |
vuetify3.VTextField(
|
| 2404 |
v_bind="props",
|
| 2405 |
-
label="
|
| 2406 |
v_model=("qlbm_job_id", ""),
|
| 2407 |
density="compact",
|
| 2408 |
hide_details=True,
|
|
@@ -2411,7 +2529,7 @@ def _build_control_panels(plotter):
|
|
| 2411 |
placeholder="e.g., 019b368e-6e22-7525-8512-fd16e0503673",
|
| 2412 |
prepend_icon="mdi-identifier",
|
| 2413 |
)
|
| 2414 |
-
html.Span("Enter the
|
| 2415 |
|
| 2416 |
# Output resolution and Total Time in a row
|
| 2417 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|
|
@@ -2455,24 +2573,6 @@ def _build_control_panels(plotter):
|
|
| 2455 |
classes="mb-2",
|
| 2456 |
)
|
| 2457 |
|
| 2458 |
-
# Or use file upload (fallback)
|
| 2459 |
-
with vuetify3.VExpansionPanels(variant="accordion", classes="mt-2"):
|
| 2460 |
-
with vuetify3.VExpansionPanel():
|
| 2461 |
-
vuetify3.VExpansionPanelTitle(children=["Or extract Job ID from JSON filename"])
|
| 2462 |
-
with vuetify3.VExpansionPanelText():
|
| 2463 |
-
vuetify3.VFileInput(
|
| 2464 |
-
label="Upload JSON (for Job ID extraction)",
|
| 2465 |
-
v_model=("qlbm_job_upload", None),
|
| 2466 |
-
accept=".json",
|
| 2467 |
-
prepend_icon="mdi-file-upload",
|
| 2468 |
-
density="compact",
|
| 2469 |
-
hide_details=True,
|
| 2470 |
-
color="primary",
|
| 2471 |
-
show_size=True,
|
| 2472 |
-
clearable=True,
|
| 2473 |
-
hint="The Job ID will be extracted from the filename",
|
| 2474 |
-
)
|
| 2475 |
-
|
| 2476 |
# Success message
|
| 2477 |
vuetify3.VAlert(
|
| 2478 |
v_if="qlbm_job_upload_success",
|
|
|
|
| 313 |
"qlbm_qiskit_backend_available": _QISKIT_BACKEND_AVAILABLE,
|
| 314 |
"qlbm_qiskit_fig": None, # Stores the Plotly figure for Qiskit results
|
| 315 |
|
| 316 |
+
# Job retrieval state (for loading previously saved QPU job results)
|
| 317 |
+
"qlbm_job_upload_error": "", # Error message for retrieval
|
| 318 |
+
"qlbm_job_upload_success": "", # Success message for retrieval
|
| 319 |
+
"qlbm_job_platform": "IonQ", # Platform: IonQ or IBM
|
|
|
|
|
|
|
| 320 |
"qlbm_job_id": "", # Job ID text field for direct entry
|
| 321 |
"qlbm_job_total_time": 3, # Total time T (generates T_list = [1..T])
|
| 322 |
"qlbm_job_output_resolution": 40, # Grid resolution for density estimation
|
| 323 |
+
"qlbm_job_is_processing": False, # True when processing job
|
| 324 |
"qlbm_job_flag_qubits": True, # Whether flag qubits were used
|
| 325 |
+
"qlbm_job_midcircuit_meas": False, # IonQ uses False, IBM uses True
|
| 326 |
})
|
| 327 |
_initialized = True
|
| 328 |
|
|
|
|
| 1198 |
# --- Job Result Upload Processing ---
|
| 1199 |
def process_uploaded_job_result():
|
| 1200 |
"""
|
| 1201 |
+
Process an IBM or IonQ job by retrieving it directly using the Job ID.
|
| 1202 |
|
| 1203 |
This function:
|
| 1204 |
1. Takes the Job ID from user input (or extracts from uploaded filename)
|
| 1205 |
+
2. Connects to IBM/IonQ based on platform selection and retrieves the job
|
| 1206 |
+
3. Processes the job results (IBM: job.result(), IonQ: job.get_counts(i))
|
| 1207 |
4. Calls load_samples/estimate_density for each timestep
|
| 1208 |
5. Generates the slider figure using plot_density_isosurface_slider
|
| 1209 |
"""
|
|
|
|
| 1218 |
log_to_console("Error: visualize_counts module not available")
|
| 1219 |
return
|
| 1220 |
|
| 1221 |
+
# Get job ID from text field
|
| 1222 |
job_id = None
|
| 1223 |
|
|
|
|
| 1224 |
if _state.qlbm_job_id and str(_state.qlbm_job_id).strip():
|
| 1225 |
job_id = str(_state.qlbm_job_id).strip()
|
| 1226 |
# Remove .json extension if present
|
| 1227 |
if job_id.endswith(".json"):
|
| 1228 |
job_id = job_id[:-5]
|
| 1229 |
log_to_console(f"Using Job ID from text field: {job_id}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1230 |
|
| 1231 |
if not job_id:
|
| 1232 |
+
_state.qlbm_job_upload_error = "No Job ID provided. Please enter a Job ID."
|
| 1233 |
return
|
| 1234 |
|
| 1235 |
+
# Get platform selection
|
| 1236 |
+
platform = _state.qlbm_job_platform or "IonQ"
|
| 1237 |
+
|
| 1238 |
# Reset messages
|
| 1239 |
_state.qlbm_job_upload_error = ""
|
| 1240 |
_state.qlbm_job_upload_success = ""
|
| 1241 |
_state.qlbm_job_is_processing = True
|
| 1242 |
+
log_to_console(f"Processing {platform} Job ID: {job_id}")
|
|
|
|
| 1243 |
|
| 1244 |
try:
|
| 1245 |
# Parse timesteps from user input
|
|
|
|
| 1257 |
|
| 1258 |
# Get processing parameters
|
| 1259 |
output_resolution = int(_state.qlbm_job_output_resolution or 40)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
|
| 1261 |
+
# Platform-specific parameters
|
| 1262 |
+
if platform == "IBM":
|
| 1263 |
+
flag_qubits = True
|
| 1264 |
+
midcircuit_meas = True # IBM uses midcircuit_meas=True
|
| 1265 |
+
else: # IonQ
|
| 1266 |
+
flag_qubits = True
|
| 1267 |
+
midcircuit_meas = False # IonQ uses midcircuit_meas=False
|
| 1268 |
|
| 1269 |
+
log_to_console(f"Platform: {platform}, Resolution: {output_resolution}, Flag qubits: {flag_qubits}, Midcircuit meas: {midcircuit_meas}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1270 |
|
| 1271 |
+
output = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1272 |
|
| 1273 |
+
if platform == "IBM":
|
| 1274 |
+
# === IBM Job Retrieval ===
|
| 1275 |
+
log_to_console("Connecting to IBM Quantum...")
|
|
|
|
|
|
|
| 1276 |
|
| 1277 |
+
try:
|
| 1278 |
+
from qiskit_ibm_runtime import QiskitRuntimeService
|
| 1279 |
+
except ImportError:
|
| 1280 |
+
_state.qlbm_job_upload_error = "qiskit_ibm_runtime package not available. Please install it."
|
| 1281 |
_state.qlbm_job_is_processing = False
|
| 1282 |
+
log_to_console("Error: qiskit_ibm_runtime not installed")
|
| 1283 |
return
|
| 1284 |
+
|
| 1285 |
+
# Get API token from environment
|
| 1286 |
+
ibm_token = os.environ.get("API_KEY_IBM_QLBM")
|
| 1287 |
+
if not ibm_token:
|
| 1288 |
+
_state.qlbm_job_upload_error = "IBM API token not found. Set API_KEY_IBM_QLBM environment variable."
|
| 1289 |
+
_state.qlbm_job_is_processing = False
|
| 1290 |
+
log_to_console("Error: IBM API token not found in environment")
|
| 1291 |
+
return
|
| 1292 |
+
|
| 1293 |
+
# Set up IBM service (same as run_sampling_hw_ibm)
|
| 1294 |
try:
|
| 1295 |
+
service = QiskitRuntimeService(
|
| 1296 |
+
channel="ibm_cloud",
|
| 1297 |
+
token=ibm_token,
|
| 1298 |
+
instance="crn:v1:bluemix:public:quantum-computing:us-east:a/15157e4350c04a9dab51b8b8a4a93c86:e29afd91-64bf-4a82-8dbf-731e6c213595::",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1299 |
)
|
| 1300 |
+
log_to_console("Connected to IBM Quantum service")
|
| 1301 |
+
except Exception as e:
|
| 1302 |
+
_state.qlbm_job_upload_error = f"Failed to connect to IBM Quantum: {e}"
|
| 1303 |
+
_state.qlbm_job_is_processing = False
|
| 1304 |
+
log_to_console(f"Error connecting to IBM: {e}")
|
| 1305 |
+
return
|
| 1306 |
+
|
| 1307 |
+
# Retrieve the job
|
| 1308 |
+
log_to_console(f"Retrieving IBM job: {job_id}")
|
| 1309 |
+
try:
|
| 1310 |
+
job = service.job(job_id)
|
| 1311 |
+
except Exception as e:
|
| 1312 |
+
_state.qlbm_job_upload_error = f"Failed to retrieve IBM job: {e}"
|
| 1313 |
+
_state.qlbm_job_is_processing = False
|
| 1314 |
+
log_to_console(f"Error retrieving job: {e}")
|
| 1315 |
+
return
|
| 1316 |
+
|
| 1317 |
+
# Check job status
|
| 1318 |
+
try:
|
| 1319 |
+
status = job.status()
|
| 1320 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 1321 |
+
log_to_console(f"Job status: {status_name}")
|
| 1322 |
|
| 1323 |
+
if status_name not in ('DONE', 'COMPLETED'):
|
| 1324 |
+
_state.qlbm_job_upload_error = f"Job is not complete. Current status: {status_name}"
|
| 1325 |
+
_state.qlbm_job_is_processing = False
|
| 1326 |
+
return
|
| 1327 |
+
except Exception as e:
|
| 1328 |
+
log_to_console(f"Warning: Could not check job status: {e}")
|
| 1329 |
+
|
| 1330 |
+
# Get results (same as run_sampling_hw_ibm)
|
| 1331 |
+
log_to_console("Retrieving IBM job results...")
|
| 1332 |
+
try:
|
| 1333 |
+
result = job.result()
|
| 1334 |
+
log_to_console("Results retrieved successfully")
|
| 1335 |
+
except Exception as e:
|
| 1336 |
+
_state.qlbm_job_upload_error = f"Failed to get job results: {e}"
|
| 1337 |
+
_state.qlbm_job_is_processing = False
|
| 1338 |
+
log_to_console(f"Error getting results: {e}")
|
| 1339 |
+
return
|
| 1340 |
+
|
| 1341 |
+
# Process results (same pattern as run_sampling_hw_ibm)
|
| 1342 |
+
log_to_console("Processing IBM job results...")
|
| 1343 |
+
|
| 1344 |
+
for idx, (T_total, pub) in enumerate(zip(T_list, result)):
|
| 1345 |
+
try:
|
| 1346 |
+
log_to_console(f"Processing timestep T={T_total} (circuit {idx})...")
|
| 1347 |
+
|
| 1348 |
+
# Get counts (same as run_sampling_hw_ibm)
|
| 1349 |
+
try:
|
| 1350 |
+
joined = pub.join_data()
|
| 1351 |
+
counts = joined.get_counts()
|
| 1352 |
+
except Exception as e:
|
| 1353 |
+
log_to_console(f"Error retrieving counts for T={T_total}: {e}")
|
| 1354 |
+
continue
|
| 1355 |
+
|
| 1356 |
+
log_to_console(f" Retrieved {len(counts)} unique bitstrings")
|
| 1357 |
+
|
| 1358 |
+
# Debug: show a few sample bitstrings
|
| 1359 |
+
sample_count = 0
|
| 1360 |
+
for bs, cnt in counts.items():
|
| 1361 |
+
if sample_count < 3:
|
| 1362 |
+
log_to_console(f" Sample: {bs} (count={cnt})")
|
| 1363 |
+
sample_count += 1
|
| 1364 |
+
|
| 1365 |
+
# Process samples (same as run_sampling_hw_ibm)
|
| 1366 |
+
pts, processed_counts = load_samples(
|
| 1367 |
+
counts, T_total,
|
| 1368 |
+
logger=log_to_console,
|
| 1369 |
+
flag_qubits=flag_qubits,
|
| 1370 |
+
midcircuit_meas=midcircuit_meas
|
| 1371 |
+
)
|
| 1372 |
+
log_to_console(f" load_samples returned {len(pts)} valid sample points")
|
| 1373 |
+
|
| 1374 |
+
# Estimate density
|
| 1375 |
+
density = estimate_density(pts, processed_counts, bandwidth=0.05, grid_size=output_resolution)
|
| 1376 |
+
output.append(density)
|
| 1377 |
+
|
| 1378 |
+
except Exception as e:
|
| 1379 |
+
log_to_console(f"Error processing timestep {idx}: {e}")
|
| 1380 |
+
import traceback
|
| 1381 |
+
log_to_console(traceback.format_exc())
|
| 1382 |
+
|
| 1383 |
+
else:
|
| 1384 |
+
# === IonQ Job Retrieval ===
|
| 1385 |
+
log_to_console("Connecting to IonQ...")
|
| 1386 |
+
|
| 1387 |
+
try:
|
| 1388 |
+
from qiskit_ionq import IonQProvider
|
| 1389 |
+
except ImportError:
|
| 1390 |
+
_state.qlbm_job_upload_error = "qiskit_ionq package not available. Please install it."
|
| 1391 |
+
_state.qlbm_job_is_processing = False
|
| 1392 |
+
log_to_console("Error: qiskit_ionq not installed")
|
| 1393 |
+
return
|
| 1394 |
+
|
| 1395 |
+
# Get API token from environment (same pattern as run_sampling_hw_ionq)
|
| 1396 |
+
ionq_token = os.environ.get("API_KEY_IONQ_QLBM") or os.environ.get("IONQ_API_TOKEN")
|
| 1397 |
+
if not ionq_token:
|
| 1398 |
+
_state.qlbm_job_upload_error = "IonQ API token not found. Set API_KEY_IONQ_QLBM environment variable."
|
| 1399 |
+
_state.qlbm_job_is_processing = False
|
| 1400 |
+
log_to_console("Error: IonQ API token not found in environment")
|
| 1401 |
+
return
|
| 1402 |
+
|
| 1403 |
+
# Set the IONQ_API_TOKEN env var so IonQProvider() can find it (same as run_sampling_hw_ionq)
|
| 1404 |
+
os.environ.setdefault("IONQ_API_TOKEN", ionq_token)
|
| 1405 |
+
|
| 1406 |
+
# Set up the IonQ provider and backend (IonQProvider reads from IONQ_API_TOKEN env var)
|
| 1407 |
+
provider = IonQProvider()
|
| 1408 |
+
backend = provider.get_backend("qpu.forte-enterprise-1")
|
| 1409 |
+
backend_name = backend.name if isinstance(backend.name, str) else backend.name()
|
| 1410 |
+
log_to_console(f"Connected to IonQ backend: {backend_name}")
|
| 1411 |
+
|
| 1412 |
+
# Retrieve the job
|
| 1413 |
+
log_to_console(f"Retrieving IonQ job: {job_id}")
|
| 1414 |
+
try:
|
| 1415 |
+
job = backend.retrieve_job(job_id)
|
| 1416 |
+
except Exception as e:
|
| 1417 |
+
_state.qlbm_job_upload_error = f"Failed to retrieve IonQ job: {e}"
|
| 1418 |
+
_state.qlbm_job_is_processing = False
|
| 1419 |
+
log_to_console(f"Error retrieving job: {e}")
|
| 1420 |
+
return
|
| 1421 |
+
|
| 1422 |
+
# Check job status
|
| 1423 |
+
try:
|
| 1424 |
+
status = job.status()
|
| 1425 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 1426 |
+
log_to_console(f"Job status: {status_name}")
|
| 1427 |
|
| 1428 |
+
if status_name not in ('DONE', 'COMPLETED'):
|
| 1429 |
+
_state.qlbm_job_upload_error = f"Job is not complete. Current status: {status_name}"
|
| 1430 |
+
_state.qlbm_job_is_processing = False
|
| 1431 |
+
return
|
| 1432 |
except Exception as e:
|
| 1433 |
+
log_to_console(f"Warning: Could not check job status: {e}")
|
| 1434 |
+
|
| 1435 |
+
# Process results (same as run_sampling_hw_ionq)
|
| 1436 |
+
log_to_console("Processing IonQ job results...")
|
| 1437 |
+
|
| 1438 |
+
for i, T_total in enumerate(T_list):
|
| 1439 |
+
try:
|
| 1440 |
+
log_to_console(f"Processing timestep T={T_total} (circuit {i})...")
|
| 1441 |
+
|
| 1442 |
+
# Get counts directly from job (same as run_sampling_hw_ionq)
|
| 1443 |
+
counts = job.get_counts(i)
|
| 1444 |
+
log_to_console(f" Retrieved {len(counts)} unique bitstrings")
|
| 1445 |
+
|
| 1446 |
+
# Debug: show a few sample bitstrings
|
| 1447 |
+
sample_count = 0
|
| 1448 |
+
for bs, cnt in counts.items():
|
| 1449 |
+
if sample_count < 3:
|
| 1450 |
+
log_to_console(f" Sample: {bs} (count={cnt})")
|
| 1451 |
+
sample_count += 1
|
| 1452 |
+
|
| 1453 |
+
# Process samples (same as run_sampling_hw_ionq)
|
| 1454 |
+
pts, processed_counts = load_samples(
|
| 1455 |
+
counts, T_total,
|
| 1456 |
+
logger=log_to_console,
|
| 1457 |
+
flag_qubits=flag_qubits,
|
| 1458 |
+
midcircuit_meas=midcircuit_meas
|
| 1459 |
+
)
|
| 1460 |
+
log_to_console(f" load_samples returned {len(pts)} valid sample points")
|
| 1461 |
+
|
| 1462 |
+
# Estimate density
|
| 1463 |
+
density = estimate_density(pts, processed_counts, bandwidth=0.05, grid_size=output_resolution)
|
| 1464 |
+
output.append(density)
|
| 1465 |
+
|
| 1466 |
+
except IndexError:
|
| 1467 |
+
log_to_console(f"Warning: No data found for timestep T={T_total} (circuit {i})")
|
| 1468 |
+
break
|
| 1469 |
+
except Exception as e:
|
| 1470 |
+
log_to_console(f"Error processing timestep {i}: {e}")
|
| 1471 |
+
import traceback
|
| 1472 |
+
log_to_console(traceback.format_exc())
|
| 1473 |
|
| 1474 |
if not output:
|
| 1475 |
_state.qlbm_job_upload_error = "No valid data extracted from job. Check timesteps parameter."
|
|
|
|
| 1485 |
_state.qlbm_qiskit_mode = True
|
| 1486 |
_state.qlbm_qiskit_fig = fig
|
| 1487 |
_state.qlbm_simulation_has_run = True
|
| 1488 |
+
_state.qlbm_job_upload_success = f"✓ Successfully processed {len(output)} timestep(s) from {platform} job {job_id}"
|
| 1489 |
|
| 1490 |
# Update the Plotly figure widget
|
| 1491 |
if hasattr(_ctrl, "qlbm_qiskit_result_update"):
|
|
|
|
| 2495 |
|
| 2496 |
# --- Job Result Upload Section ---
|
| 2497 |
vuetify3.VDivider(classes="my-3")
|
| 2498 |
+
html.Div("Upload Results", classes="text-subtitle-2 font-weight-bold text-primary mb-2")
|
| 2499 |
+
html.Div("Retrieve completed job results from IBM or IonQ using the Job ID",
|
| 2500 |
classes="text-caption text-medium-emphasis mb-2")
|
| 2501 |
|
| 2502 |
+
# Platform selector
|
| 2503 |
+
with vuetify3.VTooltip(location="top"):
|
| 2504 |
+
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 2505 |
+
vuetify3.VSelect(
|
| 2506 |
+
v_bind="props",
|
| 2507 |
+
label="Platform",
|
| 2508 |
+
v_model=("qlbm_job_platform", "IonQ"),
|
| 2509 |
+
items=("['IBM', 'IonQ']",),
|
| 2510 |
+
density="compact",
|
| 2511 |
+
hide_details=True,
|
| 2512 |
+
color="primary",
|
| 2513 |
+
classes="mb-2",
|
| 2514 |
+
prepend_icon="mdi-chip",
|
| 2515 |
+
)
|
| 2516 |
+
html.Span("Select the quantum hardware provider (IBM or IonQ)")
|
| 2517 |
+
|
| 2518 |
# Job ID input
|
| 2519 |
with vuetify3.VTooltip(location="top"):
|
| 2520 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 2521 |
vuetify3.VTextField(
|
| 2522 |
v_bind="props",
|
| 2523 |
+
label="Job ID",
|
| 2524 |
v_model=("qlbm_job_id", ""),
|
| 2525 |
density="compact",
|
| 2526 |
hide_details=True,
|
|
|
|
| 2529 |
placeholder="e.g., 019b368e-6e22-7525-8512-fd16e0503673",
|
| 2530 |
prepend_icon="mdi-identifier",
|
| 2531 |
)
|
| 2532 |
+
html.Span("Enter the Job ID (UUID format from IBM or IonQ)")
|
| 2533 |
|
| 2534 |
# Output resolution and Total Time in a row
|
| 2535 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|
|
|
|
| 2573 |
classes="mb-2",
|
| 2574 |
)
|
| 2575 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2576 |
# Success message
|
| 2577 |
vuetify3.VAlert(
|
| 2578 |
v_if="qlbm_job_upload_success",
|
requirements.txt
CHANGED
|
Binary files a/requirements.txt and b/requirements.txt differ
|
|
|