Spaces:
Sleeping
Sleeping
harishaseebat92
commited on
Commit
·
f4fc516
1
Parent(s):
610d3d1
QLBM IONQ Upload Window
Browse files- qlbm_embedded.py +186 -184
qlbm_embedded.py
CHANGED
|
@@ -314,16 +314,17 @@ def init_state():
|
|
| 314 |
"qlbm_qiskit_fig": None, # Stores the Plotly figure for Qiskit results
|
| 315 |
|
| 316 |
# Job upload state (for loading previously saved QPU job results)
|
| 317 |
-
"qlbm_job_upload": None, # File upload content
|
| 318 |
"qlbm_job_upload_filename": "", # Display the uploaded filename
|
| 319 |
"qlbm_job_upload_error": "", # Error message for upload
|
| 320 |
"qlbm_job_upload_success": "", # Success message for upload
|
| 321 |
-
"qlbm_job_platform": "
|
|
|
|
| 322 |
"qlbm_job_total_time": 3, # Total time T (generates T_list = [1..T])
|
| 323 |
"qlbm_job_output_resolution": 40, # Grid resolution for density estimation
|
| 324 |
"qlbm_job_is_processing": False, # True when processing uploaded job
|
| 325 |
"qlbm_job_flag_qubits": True, # Whether flag qubits were used
|
| 326 |
-
"qlbm_job_midcircuit_meas":
|
| 327 |
})
|
| 328 |
_initialized = True
|
| 329 |
|
|
@@ -1199,13 +1200,14 @@ def _run_qiskit_simulation(progress_callback=None):
|
|
| 1199 |
# --- Job Result Upload Processing ---
|
| 1200 |
def process_uploaded_job_result():
|
| 1201 |
"""
|
| 1202 |
-
Process an
|
| 1203 |
|
| 1204 |
This function:
|
| 1205 |
-
1.
|
| 1206 |
-
2.
|
| 1207 |
-
3. Calls
|
| 1208 |
-
4.
|
|
|
|
| 1209 |
"""
|
| 1210 |
global simulation_data_frames, simulation_times, current_grid_object
|
| 1211 |
|
|
@@ -1218,45 +1220,44 @@ def process_uploaded_job_result():
|
|
| 1218 |
log_to_console("Error: visualize_counts module not available")
|
| 1219 |
return
|
| 1220 |
|
| 1221 |
-
#
|
| 1222 |
-
|
| 1223 |
-
|
| 1224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1225 |
return
|
| 1226 |
|
| 1227 |
# Reset messages
|
| 1228 |
_state.qlbm_job_upload_error = ""
|
| 1229 |
_state.qlbm_job_upload_success = ""
|
| 1230 |
_state.qlbm_job_is_processing = True
|
| 1231 |
-
|
|
|
|
| 1232 |
|
| 1233 |
try:
|
| 1234 |
-
# Handle list or single file
|
| 1235 |
-
file_data = uploaded[0] if isinstance(uploaded, list) else uploaded
|
| 1236 |
-
|
| 1237 |
-
# Get filename for display
|
| 1238 |
-
filename = file_data.get("name", "unknown.json") if isinstance(file_data, dict) else "unknown.json"
|
| 1239 |
-
_state.qlbm_job_upload_filename = filename
|
| 1240 |
-
log_to_console(f"Processing file: {filename}")
|
| 1241 |
-
|
| 1242 |
-
# Decode content - handle both bytes and string (base64 data URI)
|
| 1243 |
-
content = file_data.get("content", "") if isinstance(file_data, dict) else ""
|
| 1244 |
-
|
| 1245 |
-
# Check if content is bytes or string
|
| 1246 |
-
if isinstance(content, bytes):
|
| 1247 |
-
# Content is already raw bytes, decode directly
|
| 1248 |
-
json_str = content.decode("utf-8")
|
| 1249 |
-
log_to_console("Content received as raw bytes")
|
| 1250 |
-
else:
|
| 1251 |
-
# Content is a string, possibly base64 data URI
|
| 1252 |
-
if content.startswith("data:"):
|
| 1253 |
-
content = content.split(",", 1)[1] # Remove data URI prefix
|
| 1254 |
-
raw_bytes = base64.b64decode(content)
|
| 1255 |
-
json_str = raw_bytes.decode("utf-8")
|
| 1256 |
-
log_to_console("Content decoded from base64 string")
|
| 1257 |
-
|
| 1258 |
# Parse timesteps from user input
|
| 1259 |
-
# User provides Total Time T, we generate T_list = [1, 2, ..., T]
|
| 1260 |
try:
|
| 1261 |
total_time = int(_state.qlbm_job_total_time or 3)
|
| 1262 |
if total_time < 1:
|
|
@@ -1270,117 +1271,119 @@ def process_uploaded_job_result():
|
|
| 1270 |
log_to_console(f"Timesteps to process: {T_list}")
|
| 1271 |
|
| 1272 |
# Get processing parameters
|
| 1273 |
-
platform = _state.qlbm_job_platform or "IBM"
|
| 1274 |
output_resolution = int(_state.qlbm_job_output_resolution or 40)
|
| 1275 |
-
#
|
| 1276 |
flag_qubits = True
|
| 1277 |
-
midcircuit_meas =
|
| 1278 |
|
| 1279 |
-
log_to_console(f"
|
| 1280 |
|
| 1281 |
-
#
|
| 1282 |
-
|
| 1283 |
-
|
| 1284 |
-
|
| 1285 |
-
|
| 1286 |
-
|
| 1287 |
-
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
-
|
|
|
|
|
|
|
| 1300 |
|
| 1301 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1302 |
output = []
|
| 1303 |
|
| 1304 |
-
|
| 1305 |
-
|
| 1306 |
-
|
| 1307 |
-
|
| 1308 |
-
|
| 1309 |
-
|
| 1310 |
-
|
| 1311 |
-
|
| 1312 |
-
|
| 1313 |
-
|
| 1314 |
-
|
| 1315 |
-
|
| 1316 |
-
|
| 1317 |
-
|
| 1318 |
-
|
| 1319 |
-
|
| 1320 |
-
|
| 1321 |
-
|
| 1322 |
-
|
| 1323 |
-
|
| 1324 |
-
|
| 1325 |
-
|
| 1326 |
-
|
| 1327 |
-
|
| 1328 |
-
|
| 1329 |
-
|
| 1330 |
-
|
| 1331 |
-
|
| 1332 |
-
|
| 1333 |
-
|
| 1334 |
-
|
| 1335 |
-
|
| 1336 |
-
|
| 1337 |
-
|
| 1338 |
-
|
| 1339 |
-
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1340 |
-
else:
|
| 1341 |
-
# IonQ result structure: use get_counts(i) or direct counts dict
|
| 1342 |
-
if hasattr(result, 'get_counts'):
|
| 1343 |
-
for i, T_total in enumerate(T_list):
|
| 1344 |
-
try:
|
| 1345 |
-
counts = result.get_counts(i)
|
| 1346 |
-
log_to_console(f"Processing timestep T={T_total}: {len(counts)} unique bitstrings")
|
| 1347 |
-
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1348 |
-
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1349 |
-
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1350 |
-
except Exception as e:
|
| 1351 |
-
log_to_console(f"Error processing timestep {i}: {e}")
|
| 1352 |
-
elif isinstance(result, list):
|
| 1353 |
-
# List of counts dicts
|
| 1354 |
-
for i, (T_total, counts) in enumerate(zip(T_list, result)):
|
| 1355 |
-
if isinstance(counts, dict):
|
| 1356 |
-
log_to_console(f"Processing timestep T={T_total}")
|
| 1357 |
-
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1358 |
-
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1359 |
-
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1360 |
-
elif isinstance(result, dict):
|
| 1361 |
-
# Single counts dict
|
| 1362 |
-
counts = result.get('counts', result)
|
| 1363 |
-
for T_total in T_list:
|
| 1364 |
-
log_to_console(f"Processing timestep T={T_total}")
|
| 1365 |
-
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1366 |
-
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1367 |
-
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1368 |
|
| 1369 |
if not output:
|
| 1370 |
-
_state.qlbm_job_upload_error = "No valid data extracted from job
|
| 1371 |
_state.qlbm_job_is_processing = False
|
| 1372 |
return
|
| 1373 |
|
| 1374 |
log_to_console(f"Processed {len(output)} timestep(s) successfully")
|
| 1375 |
|
| 1376 |
# Generate the Plotly figure
|
| 1377 |
-
fig = plot_density_isosurface_slider(output, T_list)
|
| 1378 |
|
| 1379 |
# Update state to show results
|
| 1380 |
_state.qlbm_qiskit_mode = True
|
| 1381 |
_state.qlbm_qiskit_fig = fig
|
| 1382 |
_state.qlbm_simulation_has_run = True
|
| 1383 |
-
_state.qlbm_job_upload_success = f"✓ Successfully processed {len(output)} timestep(s) from {
|
| 1384 |
|
| 1385 |
# Update the Plotly figure widget
|
| 1386 |
if hasattr(_ctrl, "qlbm_qiskit_result_update"):
|
|
@@ -1388,11 +1391,8 @@ def process_uploaded_job_result():
|
|
| 1388 |
|
| 1389 |
log_to_console(f"Results ready! {len(output)} frames generated.")
|
| 1390 |
|
| 1391 |
-
except json.JSONDecodeError as e:
|
| 1392 |
-
_state.qlbm_job_upload_error = f"Invalid JSON file: {e}"
|
| 1393 |
-
log_to_console(f"JSON decode error: {e}")
|
| 1394 |
except Exception as e:
|
| 1395 |
-
_state.qlbm_job_upload_error = f"Error processing job
|
| 1396 |
log_to_console(f"Processing error: {e}")
|
| 1397 |
import traceback
|
| 1398 |
log_to_console(traceback.format_exc())
|
|
@@ -2393,25 +2393,41 @@ 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 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2401 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 2402 |
with vuetify3.VCol(cols=6):
|
| 2403 |
with vuetify3.VTooltip(location="top"):
|
| 2404 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 2405 |
-
vuetify3.
|
| 2406 |
v_bind="props",
|
| 2407 |
-
label="
|
| 2408 |
-
v_model=("
|
| 2409 |
-
|
| 2410 |
density="compact",
|
| 2411 |
hide_details=True,
|
| 2412 |
color="primary",
|
| 2413 |
)
|
| 2414 |
-
html.Span("
|
| 2415 |
with vuetify3.VCol(cols=6):
|
| 2416 |
with vuetify3.VTooltip(location="top"):
|
| 2417 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
|
@@ -2426,50 +2442,36 @@ def _build_control_panels(plotter):
|
|
| 2426 |
)
|
| 2427 |
html.Span("Resolution for 3D visualization. Should be <= Grid Size (2^n).")
|
| 2428 |
|
| 2429 |
-
#
|
| 2430 |
-
|
| 2431 |
-
|
| 2432 |
-
|
| 2433 |
-
|
| 2434 |
-
|
| 2435 |
-
|
| 2436 |
-
|
| 2437 |
-
|
| 2438 |
-
|
| 2439 |
-
|
| 2440 |
-
|
| 2441 |
-
hint="Enter the total time T used when running the job",
|
| 2442 |
-
)
|
| 2443 |
-
html.Span("Total number of time steps in the uploaded job")
|
| 2444 |
-
|
| 2445 |
-
# Advanced options (flag qubits, mid-circuit measurement) - HIDDEN
|
| 2446 |
-
# Defaulting to True for both as per user request
|
| 2447 |
|
| 2448 |
-
#
|
| 2449 |
-
with vuetify3.
|
| 2450 |
-
with vuetify3.
|
| 2451 |
-
vuetify3.
|
| 2452 |
-
|
| 2453 |
-
|
| 2454 |
-
|
| 2455 |
-
|
| 2456 |
-
|
| 2457 |
-
|
| 2458 |
-
|
| 2459 |
-
|
| 2460 |
-
|
| 2461 |
-
|
| 2462 |
-
|
| 2463 |
-
|
| 2464 |
-
|
| 2465 |
-
color="secondary",
|
| 2466 |
-
variant="tonal",
|
| 2467 |
-
block=True,
|
| 2468 |
-
disabled=("!qlbm_job_upload || qlbm_job_is_processing", True),
|
| 2469 |
-
loading=("qlbm_job_is_processing", False),
|
| 2470 |
-
click=process_uploaded_job_result,
|
| 2471 |
-
prepend_icon="mdi-chart-box-outline",
|
| 2472 |
-
)
|
| 2473 |
|
| 2474 |
# Success message
|
| 2475 |
vuetify3.VAlert(
|
|
|
|
| 314 |
"qlbm_qiskit_fig": None, # Stores the Plotly figure for Qiskit results
|
| 315 |
|
| 316 |
# Job upload state (for loading previously saved QPU job results)
|
| 317 |
+
"qlbm_job_upload": None, # File upload content (optional, for extracting job ID from filename)
|
| 318 |
"qlbm_job_upload_filename": "", # Display the uploaded filename
|
| 319 |
"qlbm_job_upload_error": "", # Error message for upload
|
| 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 uploaded job
|
| 326 |
"qlbm_job_flag_qubits": True, # Whether flag qubits were used
|
| 327 |
+
"qlbm_job_midcircuit_meas": False, # IonQ uses midcircuit_meas=False
|
| 328 |
})
|
| 329 |
_initialized = True
|
| 330 |
|
|
|
|
| 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. Calls job.get_counts(i) for each timestep (same as run_sampling_hw_ionq)
|
| 1209 |
+
4. Calls load_samples/estimate_density for each timestep
|
| 1210 |
+
5. Generates the slider figure using plot_density_isosurface_slider
|
| 1211 |
"""
|
| 1212 |
global simulation_data_frames, simulation_times, current_grid_object
|
| 1213 |
|
|
|
|
| 1220 |
log_to_console("Error: visualize_counts module not available")
|
| 1221 |
return
|
| 1222 |
|
| 1223 |
+
# Get job ID - either from text field or from uploaded filename
|
| 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 or upload a JSON file."
|
| 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 |
+
_state.qlbm_job_upload_filename = job_id
|
| 1257 |
+
log_to_console(f"Processing IonQ Job ID: {job_id}")
|
| 1258 |
|
| 1259 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
# Parse timesteps from user input
|
|
|
|
| 1261 |
try:
|
| 1262 |
total_time = int(_state.qlbm_job_total_time or 3)
|
| 1263 |
if total_time < 1:
|
|
|
|
| 1271 |
log_to_console(f"Timesteps to process: {T_list}")
|
| 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 |
+
# Set the IONQ_API_TOKEN env var so IonQProvider() can find it (same as run_sampling_hw_ionq)
|
| 1301 |
+
os.environ.setdefault("IONQ_API_TOKEN", ionq_token)
|
| 1302 |
|
| 1303 |
+
# Set up the IonQ provider and backend (IonQProvider reads from IONQ_API_TOKEN env var)
|
| 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 |
+
# Retrieve the job
|
| 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 |
+
# Check job status
|
| 1320 |
+
try:
|
| 1321 |
+
status = job.status()
|
| 1322 |
+
status_name = status.name if hasattr(status, 'name') else str(status)
|
| 1323 |
+
log_to_console(f"Job status: {status_name}")
|
| 1324 |
+
|
| 1325 |
+
if status_name not in ('DONE', 'COMPLETED'):
|
| 1326 |
+
_state.qlbm_job_upload_error = f"Job is not complete. Current status: {status_name}"
|
| 1327 |
+
_state.qlbm_job_is_processing = False
|
| 1328 |
+
return
|
| 1329 |
+
except Exception as e:
|
| 1330 |
+
log_to_console(f"Warning: Could not check job status: {e}")
|
| 1331 |
+
|
| 1332 |
+
# === Process results (same as run_sampling_hw_ionq) ===
|
| 1333 |
+
log_to_console("Processing job results...")
|
| 1334 |
output = []
|
| 1335 |
|
| 1336 |
+
for i, T_total in enumerate(T_list):
|
| 1337 |
+
try:
|
| 1338 |
+
log_to_console(f"Processing timestep T={T_total} (circuit {i})...")
|
| 1339 |
+
|
| 1340 |
+
# Get counts directly from job (same as run_sampling_hw_ionq)
|
| 1341 |
+
counts = job.get_counts(i)
|
| 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(f" load_samples returned {len(pts)} valid sample points")
|
| 1359 |
+
|
| 1360 |
+
# Estimate density
|
| 1361 |
+
density = estimate_density(pts, processed_counts, bandwidth=0.05, grid_size=output_resolution)
|
| 1362 |
+
output.append(density)
|
| 1363 |
+
|
| 1364 |
+
except IndexError:
|
| 1365 |
+
log_to_console(f"Warning: No data found for timestep T={T_total} (circuit {i})")
|
| 1366 |
+
break
|
| 1367 |
+
except Exception as e:
|
| 1368 |
+
log_to_console(f"Error processing timestep {i}: {e}")
|
| 1369 |
+
import traceback
|
| 1370 |
+
log_to_console(traceback.format_exc())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1371 |
|
| 1372 |
if not output:
|
| 1373 |
+
_state.qlbm_job_upload_error = "No valid data extracted from job. Check timesteps parameter."
|
| 1374 |
_state.qlbm_job_is_processing = False
|
| 1375 |
return
|
| 1376 |
|
| 1377 |
log_to_console(f"Processed {len(output)} timestep(s) successfully")
|
| 1378 |
|
| 1379 |
# Generate the Plotly figure
|
| 1380 |
+
fig = plot_density_isosurface_slider(output, T_list[:len(output)])
|
| 1381 |
|
| 1382 |
# Update state to show results
|
| 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"):
|
|
|
|
| 1391 |
|
| 1392 |
log_to_console(f"Results ready! {len(output)} frames generated.")
|
| 1393 |
|
|
|
|
|
|
|
|
|
|
| 1394 |
except Exception as e:
|
| 1395 |
+
_state.qlbm_job_upload_error = f"Error processing job: {e}"
|
| 1396 |
log_to_console(f"Processing error: {e}")
|
| 1397 |
import traceback
|
| 1398 |
log_to_console(traceback.format_exc())
|
|
|
|
| 2393 |
|
| 2394 |
# --- Job Result Upload Section ---
|
| 2395 |
vuetify3.VDivider(classes="my-3")
|
| 2396 |
+
html.Div("Retrieve IonQ Job Results", classes="text-subtitle-2 font-weight-bold text-primary mb-2")
|
| 2397 |
+
html.Div("Enter the Job ID to retrieve results directly from IonQ",
|
| 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="IonQ Job ID",
|
| 2406 |
+
v_model=("qlbm_job_id", ""),
|
| 2407 |
+
density="compact",
|
| 2408 |
+
hide_details=True,
|
| 2409 |
+
color="primary",
|
| 2410 |
+
classes="mb-2",
|
| 2411 |
+
placeholder="e.g., 019b368e-6e22-7525-8512-fd16e0503673",
|
| 2412 |
+
prepend_icon="mdi-identifier",
|
| 2413 |
+
)
|
| 2414 |
+
html.Span("Enter the IonQ Job ID (UUID format)")
|
| 2415 |
+
|
| 2416 |
+
# Output resolution and Total Time in a row
|
| 2417 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 2418 |
with vuetify3.VCol(cols=6):
|
| 2419 |
with vuetify3.VTooltip(location="top"):
|
| 2420 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
| 2421 |
+
vuetify3.VTextField(
|
| 2422 |
v_bind="props",
|
| 2423 |
+
label="Total Time",
|
| 2424 |
+
v_model=("qlbm_job_total_time", 3),
|
| 2425 |
+
type="number",
|
| 2426 |
density="compact",
|
| 2427 |
hide_details=True,
|
| 2428 |
color="primary",
|
| 2429 |
)
|
| 2430 |
+
html.Span("Total number of time steps (T) used when running the job")
|
| 2431 |
with vuetify3.VCol(cols=6):
|
| 2432 |
with vuetify3.VTooltip(location="top"):
|
| 2433 |
with vuetify3.Template(v_slot_activator="{ props }"):
|
|
|
|
| 2442 |
)
|
| 2443 |
html.Span("Resolution for 3D visualization. Should be <= Grid Size (2^n).")
|
| 2444 |
|
| 2445 |
+
# Generate button
|
| 2446 |
+
vuetify3.VBtn(
|
| 2447 |
+
text="Retrieve & Generate Plot",
|
| 2448 |
+
color="secondary",
|
| 2449 |
+
variant="tonal",
|
| 2450 |
+
block=True,
|
| 2451 |
+
disabled=("!qlbm_job_id || qlbm_job_is_processing", True),
|
| 2452 |
+
loading=("qlbm_job_is_processing", False),
|
| 2453 |
+
click=process_uploaded_job_result,
|
| 2454 |
+
prepend_icon="mdi-chart-box-outline",
|
| 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(
|