Spaces:
Runtime error
Runtime error
harishaseebat92 commited on
Commit ·
ae7f86e
1
Parent(s): 610d3d1
QLBM IONQ Upload Window, refined
Browse files- qlbm/visualize_counts.py +129 -7
- qlbm_embedded.py +198 -13
- utils/EBU_Quantum/with_body/base_functions_body.py +1 -1
qlbm/visualize_counts.py
CHANGED
|
@@ -34,6 +34,10 @@ def load_samples(d, T_total, logger=None, flag_qubits=False, midcircuit_meas=Tru
|
|
| 34 |
Total number of timesteps (used to determine how many direction bits to check)
|
| 35 |
logger : callable, optional
|
| 36 |
Function to log messages
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
Returns
|
| 39 |
-------
|
|
@@ -64,16 +68,130 @@ def load_samples(d, T_total, logger=None, flag_qubits=False, midcircuit_meas=Tru
|
|
| 64 |
log("Warning: Empty counts dictionary")
|
| 65 |
return np.array(pts), np.array(counts)
|
| 66 |
|
| 67 |
-
#
|
|
|
|
| 68 |
sample_keys = list(d.keys())[:3]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
log(f"Sample bitstrings (first 3): {sample_keys}")
|
| 70 |
if sample_keys:
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
log(f"Expected prefix length: {pref_length}")
|
| 73 |
|
| 74 |
-
for
|
| 75 |
-
#
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
prefix = bs[:pref_length]
|
| 78 |
expected_prefix = "0" * pref_length
|
| 79 |
|
|
@@ -83,8 +201,12 @@ def load_samples(d, T_total, logger=None, flag_qubits=False, midcircuit_meas=Tru
|
|
| 83 |
remaining_bits = bs[pref_length:]
|
| 84 |
# Check if remaining bits are divisible by 3
|
| 85 |
if len(remaining_bits) % 3 != 0:
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
x, y, z = bitstring_to_xyz(remaining_bits)
|
| 89 |
pts.append([x, y, z])
|
| 90 |
counts.append(cnt)
|
|
|
|
| 34 |
Total number of timesteps (used to determine how many direction bits to check)
|
| 35 |
logger : callable, optional
|
| 36 |
Function to log messages
|
| 37 |
+
flag_qubits : bool
|
| 38 |
+
Whether flag qubits were used in the circuit
|
| 39 |
+
midcircuit_meas : bool
|
| 40 |
+
Whether mid-circuit measurement was used (IBM uses True, IonQ uses False)
|
| 41 |
|
| 42 |
Returns
|
| 43 |
-------
|
|
|
|
| 68 |
log("Warning: Empty counts dictionary")
|
| 69 |
return np.array(pts), np.array(counts)
|
| 70 |
|
| 71 |
+
# Detect format: IonQ returns decimal strings, IBM returns binary strings
|
| 72 |
+
# Also detect hex format and UUID-like keys (which indicate wrong data structure)
|
| 73 |
sample_keys = list(d.keys())[:3]
|
| 74 |
+
|
| 75 |
+
# Check if keys look like UUIDs (job IDs) - this indicates wrong JSON structure
|
| 76 |
+
is_uuid_format = False
|
| 77 |
+
if sample_keys:
|
| 78 |
+
first_key = str(sample_keys[0])
|
| 79 |
+
# UUID pattern: contains hyphens and hex chars, length ~36
|
| 80 |
+
if '-' in first_key and len(first_key) > 30:
|
| 81 |
+
is_uuid_format = True
|
| 82 |
+
log(f"ERROR: Keys appear to be UUIDs/job IDs, not measurement bitstrings!")
|
| 83 |
+
log(f"This suggests the JSON file structure is not being parsed correctly.")
|
| 84 |
+
log(f"Expected: measurement outcomes like '0', '1', '101010', '0x1a2b'")
|
| 85 |
+
log(f"Got: {first_key}")
|
| 86 |
+
return np.array([]), np.array([])
|
| 87 |
+
|
| 88 |
+
# Check for hex format (IonQ sometimes returns hex like '0x1a2b' or just 'a1b2')
|
| 89 |
+
is_hex_format = False
|
| 90 |
+
if sample_keys and not is_uuid_format:
|
| 91 |
+
first_key = str(sample_keys[0]).replace(" ", "").lower()
|
| 92 |
+
# Check if it's hex (contains a-f and/or starts with 0x)
|
| 93 |
+
if first_key.startswith('0x'):
|
| 94 |
+
is_hex_format = True
|
| 95 |
+
elif any(c in 'abcdef' for c in first_key) and all(c in '0123456789abcdef' for c in first_key):
|
| 96 |
+
is_hex_format = True
|
| 97 |
+
|
| 98 |
+
# Check if keys look like decimal integers (short strings of digits only)
|
| 99 |
+
is_decimal_format = False
|
| 100 |
+
if sample_keys and not is_uuid_format and not is_hex_format:
|
| 101 |
+
first_key = str(sample_keys[0]).replace(" ", "")
|
| 102 |
+
# If the key is short and all digits, it's likely decimal format (IonQ)
|
| 103 |
+
# Binary strings from IBM are much longer and only contain 0s and 1s
|
| 104 |
+
if first_key.isdigit() and len(first_key) < 20:
|
| 105 |
+
# Additional check: if any key has digits other than 0 and 1, it's decimal
|
| 106 |
+
for key in sample_keys:
|
| 107 |
+
key_str = str(key).replace(" ", "")
|
| 108 |
+
if any(c not in '01' for c in key_str):
|
| 109 |
+
is_decimal_format = True
|
| 110 |
+
break
|
| 111 |
+
# Also check if length is suspiciously short for expected binary
|
| 112 |
+
if not is_decimal_format and len(first_key) < pref_length // 2:
|
| 113 |
+
is_decimal_format = True
|
| 114 |
+
|
| 115 |
+
# Compute total_bits for decimal or hex format
|
| 116 |
+
total_bits = pref_length + 9 # default minimum
|
| 117 |
+
|
| 118 |
+
if is_hex_format:
|
| 119 |
+
log(f"Detected hex format, converting to binary...")
|
| 120 |
+
# Find max value to determine bit width
|
| 121 |
+
max_val = 0
|
| 122 |
+
for k in d.keys():
|
| 123 |
+
k_str = str(k).replace(" ", "").lower()
|
| 124 |
+
if k_str.startswith('0x'):
|
| 125 |
+
k_str = k_str[2:]
|
| 126 |
+
try:
|
| 127 |
+
val = int(k_str, 16)
|
| 128 |
+
max_val = max(max_val, val)
|
| 129 |
+
except ValueError:
|
| 130 |
+
pass
|
| 131 |
+
total_bits = max(max_val.bit_length(), pref_length + 9)
|
| 132 |
+
log(f"Max value: {max_val}, using {total_bits} total bits")
|
| 133 |
+
elif is_decimal_format:
|
| 134 |
+
log(f"Detected IonQ decimal format, converting to binary...")
|
| 135 |
+
# Determine total bit width needed
|
| 136 |
+
# For IonQ without mid-circuit measurement: 6*(T_total+1) prefix + 3*n position bits
|
| 137 |
+
# We need to figure out n from the maximum value in keys
|
| 138 |
+
max_val = max(int(str(k).replace(" ", "")) for k in d.keys())
|
| 139 |
+
total_bits = max_val.bit_length()
|
| 140 |
+
# Round up to ensure we have enough bits
|
| 141 |
+
total_bits = max(total_bits, pref_length + 9) # At least 3 qubits per dimension
|
| 142 |
+
log(f"Max value: {max_val}, using {total_bits} total bits")
|
| 143 |
+
|
| 144 |
+
# Debug: show sample bitstrings
|
| 145 |
log(f"Sample bitstrings (first 3): {sample_keys}")
|
| 146 |
if sample_keys:
|
| 147 |
+
if is_hex_format:
|
| 148 |
+
# Show what they look like after conversion
|
| 149 |
+
converted = []
|
| 150 |
+
for k in sample_keys[:3]:
|
| 151 |
+
k_str = str(k).replace(" ", "").lower()
|
| 152 |
+
if k_str.startswith('0x'):
|
| 153 |
+
k_str = k_str[2:]
|
| 154 |
+
try:
|
| 155 |
+
converted.append(bin(int(k_str, 16))[2:].zfill(total_bits))
|
| 156 |
+
except ValueError:
|
| 157 |
+
converted.append(f"<invalid:{k}>")
|
| 158 |
+
log(f"Converted from hex to binary (first 3): {converted}")
|
| 159 |
+
log(f"Binary length: {len(converted[0]) if converted else 0}")
|
| 160 |
+
elif is_decimal_format:
|
| 161 |
+
# Show what they look like after conversion
|
| 162 |
+
converted = [bin(int(str(k).replace(" ", "")))[2:].zfill(total_bits) for k in sample_keys[:3]]
|
| 163 |
+
log(f"Converted to binary (first 3): {converted}")
|
| 164 |
+
log(f"Binary length: {len(converted[0]) if converted else 0}")
|
| 165 |
+
else:
|
| 166 |
+
log(f"Bitstring length: {len(str(sample_keys[0]).replace(' ', ''))}")
|
| 167 |
log(f"Expected prefix length: {pref_length}")
|
| 168 |
|
| 169 |
+
for bs_raw, cnt in d.items():
|
| 170 |
+
# Convert to binary string
|
| 171 |
+
bs_raw_str = str(bs_raw).replace(" ", "")
|
| 172 |
+
|
| 173 |
+
if is_hex_format:
|
| 174 |
+
# Convert hex to binary with proper padding
|
| 175 |
+
hex_str = bs_raw_str.lower()
|
| 176 |
+
if hex_str.startswith('0x'):
|
| 177 |
+
hex_str = hex_str[2:]
|
| 178 |
+
try:
|
| 179 |
+
decimal_val = int(hex_str, 16)
|
| 180 |
+
bs = bin(decimal_val)[2:].zfill(total_bits)
|
| 181 |
+
except ValueError:
|
| 182 |
+
continue # Skip invalid hex
|
| 183 |
+
elif is_decimal_format:
|
| 184 |
+
# Convert decimal to binary with proper padding
|
| 185 |
+
decimal_val = int(bs_raw_str)
|
| 186 |
+
bs = bin(decimal_val)[2:].zfill(total_bits)
|
| 187 |
+
else:
|
| 188 |
+
bs = bs_raw_str
|
| 189 |
+
|
| 190 |
+
# Check if the direction qubits (first pref_length bits) are all zeros
|
| 191 |
+
if len(bs) < pref_length:
|
| 192 |
+
# Pad with leading zeros if needed
|
| 193 |
+
bs = bs.zfill(pref_length + 9) # Ensure at least 3 qubits per dimension
|
| 194 |
+
|
| 195 |
prefix = bs[:pref_length]
|
| 196 |
expected_prefix = "0" * pref_length
|
| 197 |
|
|
|
|
| 201 |
remaining_bits = bs[pref_length:]
|
| 202 |
# Check if remaining bits are divisible by 3
|
| 203 |
if len(remaining_bits) % 3 != 0:
|
| 204 |
+
# Try to pad to make divisible by 3
|
| 205 |
+
pad_needed = (3 - len(remaining_bits) % 3) % 3
|
| 206 |
+
remaining_bits = "0" * pad_needed + remaining_bits
|
| 207 |
+
if len(remaining_bits) % 3 != 0:
|
| 208 |
+
log(f"Warning: Remaining bitstring length {len(remaining_bits)} not divisible by 3")
|
| 209 |
+
continue
|
| 210 |
x, y, z = bitstring_to_xyz(remaining_bits)
|
| 211 |
pts.append([x, y, z])
|
| 212 |
counts.append(cnt)
|
qlbm_embedded.py
CHANGED
|
@@ -1306,7 +1306,17 @@ def process_uploaded_job_result():
|
|
| 1306 |
# IBM PrimitiveResult structure: result is a list of PubResults
|
| 1307 |
# Each PubResult has .join_data().get_counts()
|
| 1308 |
if hasattr(result, '__iter__') and not isinstance(result, dict):
|
| 1309 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1310 |
try:
|
| 1311 |
# Try the PrimitiveResult API
|
| 1312 |
if hasattr(pub, 'join_data'):
|
|
@@ -1338,7 +1348,129 @@ def process_uploaded_job_result():
|
|
| 1338 |
flag_qubits=flag_qubits, midcircuit_meas=midcircuit_meas)
|
| 1339 |
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1340 |
else:
|
| 1341 |
-
# IonQ result structure
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1342 |
if hasattr(result, 'get_counts'):
|
| 1343 |
for i, T_total in enumerate(T_list):
|
| 1344 |
try:
|
|
@@ -1350,21 +1482,74 @@ def process_uploaded_job_result():
|
|
| 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 |
-
|
| 1355 |
-
|
| 1356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
#
|
| 1362 |
-
|
| 1363 |
-
|
| 1364 |
-
|
| 1365 |
-
|
| 1366 |
-
|
| 1367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1368 |
|
| 1369 |
if not output:
|
| 1370 |
_state.qlbm_job_upload_error = "No valid data extracted from job result. Check timesteps and file format."
|
|
|
|
| 1306 |
# IBM PrimitiveResult structure: result is a list of PubResults
|
| 1307 |
# Each PubResult has .join_data().get_counts()
|
| 1308 |
if hasattr(result, '__iter__') and not isinstance(result, dict):
|
| 1309 |
+
result_list = list(result)
|
| 1310 |
+
available_timesteps = len(result_list)
|
| 1311 |
+
|
| 1312 |
+
# Validate timestep count
|
| 1313 |
+
if len(T_list) > available_timesteps:
|
| 1314 |
+
log_to_console(f"Warning: Requested {len(T_list)} timesteps but result contains only {available_timesteps}")
|
| 1315 |
+
_state.qlbm_job_upload_error = f"Requested {len(T_list)} timesteps but result contains only {available_timesteps}. Please reduce Total Time T."
|
| 1316 |
+
_state.qlbm_job_is_processing = False
|
| 1317 |
+
return
|
| 1318 |
+
|
| 1319 |
+
for i, (T_total, pub) in enumerate(zip(T_list, result_list)):
|
| 1320 |
try:
|
| 1321 |
# Try the PrimitiveResult API
|
| 1322 |
if hasattr(pub, 'join_data'):
|
|
|
|
| 1348 |
flag_qubits=flag_qubits, midcircuit_meas=midcircuit_meas)
|
| 1349 |
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1350 |
else:
|
| 1351 |
+
# IonQ result structure - needs careful parsing
|
| 1352 |
+
# IonQ saves results as: {"job_id_1": {"decimal_int": probability, ...}, "job_id_2": {...}, ...}
|
| 1353 |
+
# Where:
|
| 1354 |
+
# - Top-level keys are job IDs (UUIDs like "06945432-f399-796d-8000-...")
|
| 1355 |
+
# - Each job represents one timestep/circuit
|
| 1356 |
+
# - Values are dicts with decimal integer keys (measurement outcomes)
|
| 1357 |
+
# - Values in those dicts are probabilities (floats 0-1), NOT raw counts
|
| 1358 |
+
|
| 1359 |
+
def is_uuid_like(s):
|
| 1360 |
+
"""Check if string looks like a UUID (contains hyphens, long)."""
|
| 1361 |
+
return isinstance(s, str) and '-' in s and len(s) > 30
|
| 1362 |
+
|
| 1363 |
+
def is_counts_dict(d):
|
| 1364 |
+
"""Check if dict looks like a counts/probabilities dict (numeric string keys)."""
|
| 1365 |
+
if not isinstance(d, dict) or len(d) == 0:
|
| 1366 |
+
return False
|
| 1367 |
+
sample_keys = list(d.keys())[:5]
|
| 1368 |
+
# Keys should be numeric strings (decimal integers)
|
| 1369 |
+
looks_like_counts = all(
|
| 1370 |
+
k.replace(' ', '').isdigit() or
|
| 1371 |
+
(k.replace(' ', '').startswith('-') and k.replace(' ', '')[1:].isdigit())
|
| 1372 |
+
for k in sample_keys
|
| 1373 |
+
)
|
| 1374 |
+
if not looks_like_counts:
|
| 1375 |
+
return False
|
| 1376 |
+
# Values should be numeric (int counts or float probabilities)
|
| 1377 |
+
sample_vals = [d[k] for k in sample_keys]
|
| 1378 |
+
return all(isinstance(v, (int, float)) for v in sample_vals)
|
| 1379 |
+
|
| 1380 |
+
def probabilities_to_counts(prob_dict, num_shots=16384):
|
| 1381 |
+
"""Convert probability dict to counts dict by multiplying by num_shots."""
|
| 1382 |
+
# Check if values are already counts (integers or floats > 1)
|
| 1383 |
+
sample_vals = list(prob_dict.values())[:10]
|
| 1384 |
+
max_val = max(sample_vals) if sample_vals else 0
|
| 1385 |
+
|
| 1386 |
+
if max_val > 1:
|
| 1387 |
+
# Already counts (int or float > 1)
|
| 1388 |
+
return {k: int(v) for k, v in prob_dict.items()}
|
| 1389 |
+
else:
|
| 1390 |
+
# Probabilities (0-1), convert to counts
|
| 1391 |
+
return {k: int(v * num_shots) for k, v in prob_dict.items() if int(v * num_shots) > 0}
|
| 1392 |
+
|
| 1393 |
+
def extract_ionq_counts_from_job_ids(data, num_shots=16384):
|
| 1394 |
+
"""
|
| 1395 |
+
Extract counts dicts from IonQ format where top-level keys are job IDs.
|
| 1396 |
+
Returns list of counts dicts, one per job/timestep.
|
| 1397 |
+
"""
|
| 1398 |
+
if not isinstance(data, dict):
|
| 1399 |
+
return None
|
| 1400 |
+
|
| 1401 |
+
# Check if top-level keys are job IDs (UUIDs)
|
| 1402 |
+
top_keys = list(data.keys())
|
| 1403 |
+
if not top_keys:
|
| 1404 |
+
return None
|
| 1405 |
+
|
| 1406 |
+
# If all/most keys are UUID-like, this is the job ID format
|
| 1407 |
+
uuid_keys = [k for k in top_keys if is_uuid_like(k)]
|
| 1408 |
+
if len(uuid_keys) == len(top_keys):
|
| 1409 |
+
# All keys are job IDs - extract counts from each
|
| 1410 |
+
counts_list = []
|
| 1411 |
+
for job_id in top_keys:
|
| 1412 |
+
job_data = data[job_id]
|
| 1413 |
+
if is_counts_dict(job_data):
|
| 1414 |
+
# Convert probabilities to counts
|
| 1415 |
+
counts = probabilities_to_counts(job_data, num_shots)
|
| 1416 |
+
counts_list.append(counts)
|
| 1417 |
+
return counts_list if counts_list else None
|
| 1418 |
+
|
| 1419 |
+
return None
|
| 1420 |
+
|
| 1421 |
+
def extract_ionq_counts(data):
|
| 1422 |
+
"""Recursively find a single counts dict in IonQ result structure."""
|
| 1423 |
+
if not isinstance(data, dict):
|
| 1424 |
+
return None
|
| 1425 |
+
|
| 1426 |
+
# Check if this is a counts dict directly
|
| 1427 |
+
if is_counts_dict(data):
|
| 1428 |
+
return probabilities_to_counts(data)
|
| 1429 |
+
|
| 1430 |
+
# Check for 'counts' key
|
| 1431 |
+
if 'counts' in data:
|
| 1432 |
+
return extract_ionq_counts(data['counts'])
|
| 1433 |
+
|
| 1434 |
+
# Check for 'data' key
|
| 1435 |
+
if 'data' in data:
|
| 1436 |
+
return extract_ionq_counts(data['data'])
|
| 1437 |
+
|
| 1438 |
+
# Check for 'results' key
|
| 1439 |
+
if 'results' in data:
|
| 1440 |
+
return extract_ionq_counts(data['results'])
|
| 1441 |
+
|
| 1442 |
+
return None
|
| 1443 |
+
|
| 1444 |
+
def extract_ionq_counts_list(data):
|
| 1445 |
+
"""Extract list of counts dicts for multiple timesteps."""
|
| 1446 |
+
if isinstance(data, list):
|
| 1447 |
+
counts_list = []
|
| 1448 |
+
for item in data:
|
| 1449 |
+
counts = extract_ionq_counts(item) if isinstance(item, dict) else item
|
| 1450 |
+
if counts:
|
| 1451 |
+
counts_list.append(counts)
|
| 1452 |
+
return counts_list if counts_list else None
|
| 1453 |
+
return None
|
| 1454 |
+
|
| 1455 |
+
# Debug: show top-level structure
|
| 1456 |
+
if isinstance(result, dict):
|
| 1457 |
+
top_keys = list(result.keys())[:5]
|
| 1458 |
+
log_to_console(f"IonQ result top-level keys: {top_keys}")
|
| 1459 |
+
uuid_count = sum(1 for k in result.keys() if is_uuid_like(k))
|
| 1460 |
+
log_to_console(f" UUID-like keys: {uuid_count}/{len(result)}")
|
| 1461 |
+
for key in top_keys[:2]:
|
| 1462 |
+
val = result[key]
|
| 1463 |
+
if isinstance(val, dict):
|
| 1464 |
+
val_keys = list(val.keys())[:5]
|
| 1465 |
+
val_vals = [val[k] for k in val_keys]
|
| 1466 |
+
log_to_console(f" '{key[:20]}...' contains dict with {len(val)} entries")
|
| 1467 |
+
log_to_console(f" Sample keys: {val_keys}")
|
| 1468 |
+
log_to_console(f" Sample values: {val_vals}")
|
| 1469 |
+
elif isinstance(val, list):
|
| 1470 |
+
log_to_console(f" '{key}' is list with {len(val)} items")
|
| 1471 |
+
else:
|
| 1472 |
+
log_to_console(f" '{key}' = {type(val).__name__}")
|
| 1473 |
+
|
| 1474 |
if hasattr(result, 'get_counts'):
|
| 1475 |
for i, T_total in enumerate(T_list):
|
| 1476 |
try:
|
|
|
|
| 1482 |
except Exception as e:
|
| 1483 |
log_to_console(f"Error processing timestep {i}: {e}")
|
| 1484 |
elif isinstance(result, list):
|
| 1485 |
+
# List of counts dicts - validate length
|
| 1486 |
+
if len(T_list) > len(result):
|
| 1487 |
+
log_to_console(f"Warning: Requested {len(T_list)} timesteps but result contains only {len(result)}")
|
| 1488 |
+
_state.qlbm_job_upload_error = f"Requested {len(T_list)} timesteps but result contains only {len(result)}. Please reduce Total Time T."
|
| 1489 |
+
_state.qlbm_job_is_processing = False
|
| 1490 |
+
return
|
| 1491 |
+
|
| 1492 |
+
for i, (T_total, item) in enumerate(zip(T_list, result)):
|
| 1493 |
+
counts = extract_ionq_counts(item) if isinstance(item, dict) else item
|
| 1494 |
+
if counts and isinstance(counts, dict):
|
| 1495 |
+
log_to_console(f"Processing timestep T={T_total}: {len(counts)} unique bitstrings")
|
| 1496 |
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1497 |
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1498 |
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1499 |
+
else:
|
| 1500 |
+
log_to_console(f"Could not extract counts for timestep T={T_total}")
|
| 1501 |
elif isinstance(result, dict):
|
| 1502 |
+
# First: Try to detect IonQ format with job ID keys
|
| 1503 |
+
job_id_counts_list = extract_ionq_counts_from_job_ids(result)
|
| 1504 |
+
|
| 1505 |
+
if job_id_counts_list and len(job_id_counts_list) > 0:
|
| 1506 |
+
# IonQ job ID format - multiple jobs/timesteps
|
| 1507 |
+
log_to_console(f"Detected IonQ job ID format with {len(job_id_counts_list)} jobs")
|
| 1508 |
+
|
| 1509 |
+
if len(T_list) > len(job_id_counts_list):
|
| 1510 |
+
log_to_console(f"Warning: Requested {len(T_list)} timesteps but result contains only {len(job_id_counts_list)} jobs")
|
| 1511 |
+
_state.qlbm_job_upload_error = f"Requested {len(T_list)} timesteps but result contains only {len(job_id_counts_list)} jobs. Please reduce Total Time T."
|
| 1512 |
+
_state.qlbm_job_is_processing = False
|
| 1513 |
+
return
|
| 1514 |
+
|
| 1515 |
+
for i, (T_total, counts) in enumerate(zip(T_list, job_id_counts_list)):
|
| 1516 |
+
log_to_console(f"Processing timestep T={T_total}: {len(counts)} unique outcomes (converted from probabilities)")
|
| 1517 |
+
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1518 |
+
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1519 |
+
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1520 |
+
else:
|
| 1521 |
+
# Try to extract counts from nested structure
|
| 1522 |
+
counts = extract_ionq_counts(result)
|
| 1523 |
+
counts_list = extract_ionq_counts_list(result.get('results', result.get('data', [])))
|
| 1524 |
+
|
| 1525 |
+
if counts_list and len(counts_list) > 0:
|
| 1526 |
+
# Multiple timesteps in result
|
| 1527 |
+
if len(T_list) > len(counts_list):
|
| 1528 |
+
log_to_console(f"Warning: Requested {len(T_list)} timesteps but result contains only {len(counts_list)}")
|
| 1529 |
+
_state.qlbm_job_upload_error = f"Requested {len(T_list)} timesteps but result contains only {len(counts_list)}. Please reduce Total Time T."
|
| 1530 |
+
_state.qlbm_job_is_processing = False
|
| 1531 |
+
return
|
| 1532 |
+
|
| 1533 |
+
for i, (T_total, c) in enumerate(zip(T_list, counts_list)):
|
| 1534 |
+
log_to_console(f"Processing timestep T={T_total}: {len(c)} unique bitstrings")
|
| 1535 |
+
pts, cnts = load_samples(c, T_total, logger=log_to_console,
|
| 1536 |
+
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1537 |
+
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1538 |
+
elif counts:
|
| 1539 |
+
# Single counts dict - same data for all timesteps (unusual but handle it)
|
| 1540 |
+
log_to_console(f"Found single counts dict with {len(counts)} entries")
|
| 1541 |
+
for T_total in T_list:
|
| 1542 |
+
log_to_console(f"Processing timestep T={T_total}")
|
| 1543 |
+
pts, cnts = load_samples(counts, T_total, logger=log_to_console,
|
| 1544 |
+
flag_qubits=flag_qubits, midcircuit_meas=False)
|
| 1545 |
+
output.append(estimate_density(pts, cnts, bandwidth=0.05, grid_size=output_resolution))
|
| 1546 |
+
else:
|
| 1547 |
+
# Could not find counts - show structure for debugging
|
| 1548 |
+
log_to_console("ERROR: Could not find counts data in IonQ result structure")
|
| 1549 |
+
log_to_console(f"Result keys: {list(result.keys())}")
|
| 1550 |
+
_state.qlbm_job_upload_error = "Could not find counts data in uploaded file. Check file format."
|
| 1551 |
+
_state.qlbm_job_is_processing = False
|
| 1552 |
+
return
|
| 1553 |
|
| 1554 |
if not output:
|
| 1555 |
_state.qlbm_job_upload_error = "No valid data extracted from job result. Check timesteps and file format."
|
utils/EBU_Quantum/with_body/base_functions_body.py
CHANGED
|
@@ -602,7 +602,7 @@ def check_gridpoint(grid_point, X_Holed, Y_Holed):
|
|
| 602 |
|
| 603 |
# Check for hole
|
| 604 |
if np.isnan(x) or np.isnan(y):
|
| 605 |
-
warnings.warn(f"Warning: Grid point ({i}, {j}) lies inside the hole (NaN).")
|
| 606 |
return False
|
| 607 |
|
| 608 |
return True
|
|
|
|
| 602 |
|
| 603 |
# Check for hole
|
| 604 |
if np.isnan(x) or np.isnan(y):
|
| 605 |
+
# warnings.warn(f"Warning: Grid point ({i}, {j}) lies inside the hole (NaN).")
|
| 606 |
return False
|
| 607 |
|
| 608 |
return True
|