Spaces:
Paused
Paused
Commit
·
fd3d804
1
Parent(s):
c37820f
QLBM : IBM QPU version integrated
Browse files- qlbm/qlbm_sample_app.py +15 -4
- qlbm/visualize_counts.py +85 -9
qlbm/qlbm_sample_app.py
CHANGED
|
@@ -390,13 +390,16 @@ def get_named_init_state_circuit(
|
|
| 390 |
# Configurable Gaussian distribution
|
| 391 |
# f(x,y,z) = exp(-((x-cx)^2 + (y-cy)^2 + (z-cz)^2) / (2*sigma^2))
|
| 392 |
|
| 393 |
-
# Default centers to 0.5 (middle of domain)
|
| 394 |
cx = float(gauss_cx) if gauss_cx is not None else 0.5
|
| 395 |
cy = float(gauss_cy) if gauss_cy is not None else 0.5
|
| 396 |
cz = float(gauss_cz) if gauss_cz is not None else 0.5
|
| 397 |
|
| 398 |
-
# Default sigma to 0
|
| 399 |
-
|
|
|
|
|
|
|
|
|
|
| 400 |
|
| 401 |
coords = np.arange(N) / N # Normalized [0, 1)
|
| 402 |
|
|
@@ -486,6 +489,10 @@ def run_sampling_hw_ibm(
|
|
| 486 |
if type(ux)==str:
|
| 487 |
ux,uy,uz=str_to_lambda(ux,uy,uz)
|
| 488 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution)
|
| 490 |
|
| 491 |
pm_optimization_level = 3
|
|
@@ -541,7 +548,7 @@ def run_sampling_hw_ibm(
|
|
| 541 |
joined_counts = None
|
| 542 |
|
| 543 |
|
| 544 |
-
pts, counts = load_samples(joined_counts,T_total)
|
| 545 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 546 |
|
| 547 |
fig = plot_density_isosurface_slider(output, T_list)
|
|
@@ -589,6 +596,10 @@ def run_sampling_sim(
|
|
| 589 |
if type(ux)==str:
|
| 590 |
ux,uy,uz=str_to_lambda(ux,uy,uz)
|
| 591 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 592 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,measure=False)
|
| 593 |
backend = AerSimulator(method = 'statevector')
|
| 594 |
output=[]
|
|
|
|
| 390 |
# Configurable Gaussian distribution
|
| 391 |
# f(x,y,z) = exp(-((x-cx)^2 + (y-cy)^2 + (z-cz)^2) / (2*sigma^2))
|
| 392 |
|
| 393 |
+
# Default centers to 0.5 (middle of domain) - matches original mu=0.5
|
| 394 |
cx = float(gauss_cx) if gauss_cx is not None else 0.5
|
| 395 |
cy = float(gauss_cy) if gauss_cy is not None else 0.5
|
| 396 |
cz = float(gauss_cz) if gauss_cz is not None else 0.5
|
| 397 |
|
| 398 |
+
# Default sigma to 1.0 to match original sig=1 behavior
|
| 399 |
+
# Original formula: exp(-((x - mu) / sig)^2 / 2) with sig=1
|
| 400 |
+
# Our formula: exp(-((x - cx)^2) / (2 * sigma^2))
|
| 401 |
+
# For equivalence: sigma = 1.0 (they are the same formula)
|
| 402 |
+
sigma = float(gauss_sigma) if gauss_sigma is not None else 1.0
|
| 403 |
|
| 404 |
coords = np.arange(N) / N # Normalized [0, 1)
|
| 405 |
|
|
|
|
| 489 |
if type(ux)==str:
|
| 490 |
ux,uy,uz=str_to_lambda(ux,uy,uz)
|
| 491 |
|
| 492 |
+
# Convert string init_state_prep_circ to circuit if needed (matches original logic)
|
| 493 |
+
if type(init_state_prep_circ)==str:
|
| 494 |
+
init_state_prep_circ=get_named_init_state_circuit(n,init_state_prep_circ)
|
| 495 |
+
|
| 496 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution)
|
| 497 |
|
| 498 |
pm_optimization_level = 3
|
|
|
|
| 548 |
joined_counts = None
|
| 549 |
|
| 550 |
|
| 551 |
+
pts, counts = load_samples(joined_counts, T_total, logger=log)
|
| 552 |
output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
|
| 553 |
|
| 554 |
fig = plot_density_isosurface_slider(output, T_list)
|
|
|
|
| 596 |
if type(ux)==str:
|
| 597 |
ux,uy,uz=str_to_lambda(ux,uy,uz)
|
| 598 |
|
| 599 |
+
# Convert string init_state_prep_circ to circuit if needed (matches original logic)
|
| 600 |
+
if type(init_state_prep_circ)==str:
|
| 601 |
+
init_state_prep_circ=get_named_init_state_circuit(n,init_state_prep_circ)
|
| 602 |
+
|
| 603 |
qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,measure=False)
|
| 604 |
backend = AerSimulator(method = 'statevector')
|
| 605 |
output=[]
|
qlbm/visualize_counts.py
CHANGED
|
@@ -22,22 +22,73 @@ def bitstring_to_xyz(bs):
|
|
| 22 |
maxv = (1 << t)
|
| 23 |
return ix / maxv, iy / maxv, iz / maxv
|
| 24 |
|
| 25 |
-
def load_samples(d,T_total):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
pts = []
|
| 27 |
counts = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
for bs, cnt in d.items():
|
| 29 |
-
if
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
continue
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
pts.append([x, y, z])
|
| 34 |
counts.append(cnt)
|
|
|
|
| 35 |
pts = np.array(pts)
|
| 36 |
counts = np.array(counts)
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
return pts, counts
|
| 42 |
|
| 43 |
def estimate_density(pts, counts, bandwidth=0.05, grid_size=64):
|
|
@@ -46,12 +97,37 @@ def estimate_density(pts, counts, bandwidth=0.05, grid_size=64):
|
|
| 46 |
Returns (grid_x, grid_y, grid_z, grid_density) where
|
| 47 |
grid_x, etc. are 3D mesh arrays, and grid_density has same shape.
|
| 48 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
# Expand points by weights: we can replicate points (if counts small),
|
| 50 |
# or directly use weights in log-likelihood calculation.
|
| 51 |
-
# sklearn
|
| 52 |
# but you can approximate by replication or by customizing the KDE.
|
| 53 |
# Here, for simplicity, replicate (careful of explosion):
|
| 54 |
pts_rep = np.repeat(pts, counts.astype(int), axis=0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
kde = KernelDensity(bandwidth=bandwidth, kernel='gaussian')
|
| 56 |
kde.fit(pts_rep)
|
| 57 |
|
|
|
|
| 22 |
maxv = (1 << t)
|
| 23 |
return ix / maxv, iy / maxv, iz / maxv
|
| 24 |
|
| 25 |
+
def load_samples(d, T_total, logger=None):
|
| 26 |
+
"""
|
| 27 |
+
Load samples from measurement counts dictionary.
|
| 28 |
+
|
| 29 |
+
Parameters
|
| 30 |
+
----------
|
| 31 |
+
d : dict
|
| 32 |
+
Dictionary mapping bitstrings to counts
|
| 33 |
+
T_total : int
|
| 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 |
+
-------
|
| 40 |
+
pts : ndarray
|
| 41 |
+
Array of (x, y, z) points
|
| 42 |
+
counts : ndarray
|
| 43 |
+
Array of counts for each point
|
| 44 |
+
"""
|
| 45 |
+
def log(msg):
|
| 46 |
+
if logger:
|
| 47 |
+
logger(str(msg))
|
| 48 |
+
else:
|
| 49 |
+
print(msg)
|
| 50 |
+
|
| 51 |
pts = []
|
| 52 |
counts = []
|
| 53 |
+
|
| 54 |
+
if d is None or len(d) == 0:
|
| 55 |
+
log("Warning: Empty counts dictionary")
|
| 56 |
+
return np.array(pts), np.array(counts)
|
| 57 |
+
|
| 58 |
+
# Debug: show sample bitstrings
|
| 59 |
+
sample_keys = list(d.keys())[:3]
|
| 60 |
+
log(f"Sample bitstrings (first 3): {sample_keys}")
|
| 61 |
+
if sample_keys:
|
| 62 |
+
log(f"Bitstring length: {len(sample_keys[0])}")
|
| 63 |
+
log(f"Expected prefix length (6*T_total): {6*T_total}")
|
| 64 |
+
|
| 65 |
for bs, cnt in d.items():
|
| 66 |
+
# Check if the direction qubits (first 6*T_total bits) are all zeros
|
| 67 |
+
prefix = bs[:6*T_total]
|
| 68 |
+
expected_prefix = "0" * 6*T_total
|
| 69 |
+
|
| 70 |
+
if prefix == expected_prefix:
|
| 71 |
+
if cnt < 0:
|
| 72 |
continue
|
| 73 |
+
remaining_bits = bs[6*T_total:]
|
| 74 |
+
# Check if remaining bits are divisible by 3
|
| 75 |
+
if len(remaining_bits) % 3 != 0:
|
| 76 |
+
log(f"Warning: Remaining bitstring length {len(remaining_bits)} not divisible by 3")
|
| 77 |
+
continue
|
| 78 |
+
x, y, z = bitstring_to_xyz(remaining_bits)
|
| 79 |
pts.append([x, y, z])
|
| 80 |
counts.append(cnt)
|
| 81 |
+
|
| 82 |
pts = np.array(pts)
|
| 83 |
counts = np.array(counts)
|
| 84 |
+
log(f"Number of valid counts: {np.sum(counts)}")
|
| 85 |
+
log(f"Number of valid bitstrings: {len(counts)}")
|
| 86 |
+
|
| 87 |
+
if len(counts) == 0:
|
| 88 |
+
log("Warning: No bitstrings matched the zero-prefix filter. This may indicate:")
|
| 89 |
+
log(" - All measurement outcomes had non-zero direction qubits")
|
| 90 |
+
log(" - Bitstring format mismatch")
|
| 91 |
+
|
| 92 |
return pts, counts
|
| 93 |
|
| 94 |
def estimate_density(pts, counts, bandwidth=0.05, grid_size=64):
|
|
|
|
| 97 |
Returns (grid_x, grid_y, grid_z, grid_density) where
|
| 98 |
grid_x, etc. are 3D mesh arrays, and grid_density has same shape.
|
| 99 |
"""
|
| 100 |
+
# Handle empty input
|
| 101 |
+
if len(pts) == 0 or len(counts) == 0 or np.sum(counts) == 0:
|
| 102 |
+
print("Warning: No valid samples to estimate density. Returning uniform distribution.")
|
| 103 |
+
mins = [0, 0, 0]
|
| 104 |
+
maxs = [1, 1, 1]
|
| 105 |
+
xs = np.linspace(mins[0], maxs[0], grid_size)
|
| 106 |
+
ys = np.linspace(mins[1], maxs[1], grid_size)
|
| 107 |
+
zs = np.linspace(mins[2], maxs[2], grid_size)
|
| 108 |
+
xx, yy, zz = np.meshgrid(xs, ys, zs, indexing='ij')
|
| 109 |
+
dens = np.ones(xx.shape) * 0.001 # Small uniform density
|
| 110 |
+
return xx, yy, zz, dens
|
| 111 |
+
|
| 112 |
# Expand points by weights: we can replicate points (if counts small),
|
| 113 |
# or directly use weights in log-likelihood calculation.
|
| 114 |
+
# sklearn's KernelDensity doesn't support sample weights in .fit,
|
| 115 |
# but you can approximate by replication or by customizing the KDE.
|
| 116 |
# Here, for simplicity, replicate (careful of explosion):
|
| 117 |
pts_rep = np.repeat(pts, counts.astype(int), axis=0)
|
| 118 |
+
|
| 119 |
+
# Handle case where all counts are zero after conversion
|
| 120 |
+
if len(pts_rep) == 0:
|
| 121 |
+
print("Warning: No points after replication. Returning uniform distribution.")
|
| 122 |
+
mins = [0, 0, 0]
|
| 123 |
+
maxs = [1, 1, 1]
|
| 124 |
+
xs = np.linspace(mins[0], maxs[0], grid_size)
|
| 125 |
+
ys = np.linspace(mins[1], maxs[1], grid_size)
|
| 126 |
+
zs = np.linspace(mins[2], maxs[2], grid_size)
|
| 127 |
+
xx, yy, zz = np.meshgrid(xs, ys, zs, indexing='ij')
|
| 128 |
+
dens = np.ones(xx.shape) * 0.001 # Small uniform density
|
| 129 |
+
return xx, yy, zz, dens
|
| 130 |
+
|
| 131 |
kde = KernelDensity(bandwidth=bandwidth, kernel='gaussian')
|
| 132 |
kde.fit(pts_rep)
|
| 133 |
|