jezerjojo commited on
Commit
fcea2ca
·
1 Parent(s): 5d45402

Added code for running QLBM on IonQ backend and flag qubit mitigation without midcircuit measurements

Browse files
Files changed (2) hide show
  1. qlbm/qlbm_sample_app.py +120 -9
  2. qlbm/visualize_counts.py +12 -6
qlbm/qlbm_sample_app.py CHANGED
@@ -251,7 +251,7 @@ def stream(qc,pos_qr,dir_qr,n):
251
  qc.cp( np.pi / (2 ** m), forw_ctrl, pos_qr[i][m])
252
  qc.cp(-np.pi / (2 ** m), backw_ctrl, pos_qr[i][m])
253
 
254
- def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure=True,flag_qubits=False):
255
 
256
  ux_str,uy_str,uz_str=None,None,None
257
  if type(ux)==str:
@@ -277,14 +277,23 @@ def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure
277
  for T_total in T_list:
278
  pos_qr=[QuantumRegister(n) for _ in range(dim)]
279
  pos_cr=[ClassicalRegister(n) for _ in range(dim)]
280
- dir_qr=QuantumRegister(2*dim)
 
 
 
281
  dir_qr_flag=QuantumRegister(2*dim)
282
- dir_cr=[ClassicalRegister((4 if flag_qubits else 2)*dim) for _ in range(T_total)]
283
 
284
  if flag_qubits:
285
- qc=QuantumCircuit(*pos_qr,dir_qr,dir_qr_flag,*pos_cr,*dir_cr)
 
 
 
286
  else:
287
- qc=QuantumCircuit(*pos_qr,dir_qr,*pos_cr,*dir_cr)
 
 
 
288
 
289
  qc.compose(init_state_prep_circ,[qubit for qr in pos_qr for qubit in list(qr)], inplace=True)
290
 
@@ -300,7 +309,11 @@ def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure
300
 
301
  for T in list(range(T_total))[::-1]:
302
 
 
 
 
303
  prep(qc,pos_qr,dir_qr)
 
304
  if flag_qubits:
305
  for q1,q2 in zip(dir_qr,dir_qr_flag):
306
  qc.cx(q1,q2)
@@ -316,11 +329,14 @@ def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure
316
  qc.cx(q1,q2)
317
  unprep(qc,pos_qr,dir_qr)
318
 
319
- if flag_qubits:
320
  qc.measure(list(dir_qr)+list(dir_qr_flag),dir_cr[T])
321
  else:
322
  qc.measure(dir_qr,dir_cr[T])
323
 
 
 
 
324
  if uniform_bool:
325
  for i in range(dim):
326
  qc.compose(QFT(n, inverse=True, do_swaps=False), pos_qr[i], inplace=True)
@@ -328,7 +344,7 @@ def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure
328
  if measure:
329
  for i in range(dim):
330
  qc.measure(pos_qr[i],pos_cr[i])
331
-
332
  qc_list+=[qc]
333
 
334
  return qc_list
@@ -590,6 +606,101 @@ def run_sampling_hw_ibm(
590
 
591
  return job,get_job_result
592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
  from qiskit_aer import AerSimulator
595
 
@@ -983,7 +1094,7 @@ if __name__=="__main__":
983
  # show_initial_distribution(n=n, init_state_name="sin", sine_k_x=1, sine_k_y=1, sine_k_z=1)
984
 
985
  # Step 3: Run simulation - pass the pre-built circuit
986
- job, get_job_result = run_sampling_hw_ibm(
987
  n=n,
988
  ux="1",
989
  uy="1",
@@ -992,7 +1103,7 @@ if __name__=="__main__":
992
  T_list=[1,2],
993
  shots=2**19,
994
  vel_resolution=2,
995
- output_resolution=8,
996
  flag_qubits=True
997
  )
998
 
 
251
  qc.cp( np.pi / (2 ** m), forw_ctrl, pos_qr[i][m])
252
  qc.cp(-np.pi / (2 ** m), backw_ctrl, pos_qr[i][m])
253
 
254
+ def get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution=32,measure=True,flag_qubits=False,midcircuit_meas=True):
255
 
256
  ux_str,uy_str,uz_str=None,None,None
257
  if type(ux)==str:
 
277
  for T_total in T_list:
278
  pos_qr=[QuantumRegister(n) for _ in range(dim)]
279
  pos_cr=[ClassicalRegister(n) for _ in range(dim)]
280
+ if midcircuit_meas:
281
+ dir_qr=QuantumRegister(2*dim)
282
+ else:
283
+ dir_qr_list=[QuantumRegister(2*dim) for _ in range(T_total)]
284
  dir_qr_flag=QuantumRegister(2*dim)
285
+ dir_cr=[ClassicalRegister((4 if flag_qubits and midcircuit_meas else 2)*dim) for _ in range(T_total+int(flag_qubits and not midcircuit_meas))]
286
 
287
  if flag_qubits:
288
+ if midcircuit_meas:
289
+ qc=QuantumCircuit(*pos_qr,dir_qr,dir_qr_flag,*pos_cr,*dir_cr)
290
+ else:
291
+ qc=QuantumCircuit(*pos_qr,*dir_qr_list,dir_qr_flag,*pos_cr,*dir_cr)
292
  else:
293
+ if midcircuit_meas:
294
+ qc=QuantumCircuit(*pos_qr,dir_qr,*pos_cr,*dir_cr)
295
+ else:
296
+ qc=QuantumCircuit(*pos_qr,*dir_qr_list,*pos_cr,*dir_cr)
297
 
298
  qc.compose(init_state_prep_circ,[qubit for qr in pos_qr for qubit in list(qr)], inplace=True)
299
 
 
309
 
310
  for T in list(range(T_total))[::-1]:
311
 
312
+ if not midcircuit_meas:
313
+ dir_qr=dir_qr_list[T]
314
+
315
  prep(qc,pos_qr,dir_qr)
316
+
317
  if flag_qubits:
318
  for q1,q2 in zip(dir_qr,dir_qr_flag):
319
  qc.cx(q1,q2)
 
329
  qc.cx(q1,q2)
330
  unprep(qc,pos_qr,dir_qr)
331
 
332
+ if flag_qubits and midcircuit_meas:
333
  qc.measure(list(dir_qr)+list(dir_qr_flag),dir_cr[T])
334
  else:
335
  qc.measure(dir_qr,dir_cr[T])
336
 
337
+ if not midcircuit_meas and flag_qubits:
338
+ qc.measure(dir_qr_flag,dir_cr[T_total])
339
+
340
  if uniform_bool:
341
  for i in range(dim):
342
  qc.compose(QFT(n, inverse=True, do_swaps=False), pos_qr[i], inplace=True)
 
344
  if measure:
345
  for i in range(dim):
346
  qc.measure(pos_qr[i],pos_cr[i])
347
+
348
  qc_list+=[qc]
349
 
350
  return qc_list
 
606
 
607
  return job,get_job_result
608
 
609
+ from qiskit_ionq import IonQProvider
610
+
611
+ provider = IonQProvider("qDwgg6JWfESewd6hbrxLL7e6H8OU0yop")
612
+
613
+ def run_sampling_hw_ionq(
614
+ n,
615
+ ux,
616
+ uy,
617
+ uz,
618
+ init_state_prep_circ,
619
+ T_list,
620
+ shots=2**19,
621
+ vel_resolution=32,
622
+ output_resolution=40,
623
+ logger=None,
624
+ flag_qubits=True
625
+ ):
626
+ """
627
+ Run QLBM simulation on IBM quantum hardware.
628
+
629
+ Parameters
630
+ ----------
631
+ n : int
632
+ Number of qubits per spatial dimension
633
+ ux, uy, uz : callable or str
634
+ Velocity field components
635
+ init_state_prep_circ : QuantumCircuit
636
+ Pre-built initial state preparation circuit from get_named_init_state_circuit()
637
+ T_list : list[int]
638
+ List of timesteps to simulate
639
+ shots : int
640
+ Number of measurement shots (default: 2^19)
641
+ vel_resolution : int
642
+ Resolution for velocity field discretization
643
+ output_resolution : int
644
+ Grid resolution for density estimation output
645
+ logger : callable, optional
646
+ Function to log messages (e.g. print to console)
647
+
648
+ Returns
649
+ -------
650
+ job : IonQ Job
651
+ The submitted job object
652
+ get_job_result : callable
653
+ Callback function to retrieve and process results. Returns (output, fig).
654
+ """
655
+
656
+ def log(msg):
657
+ if logger:
658
+ logger(str(msg))
659
+ else:
660
+ print(msg)
661
+
662
+ # if type(ux)==str:
663
+ # ux,uy,uz=str_to_lambda(ux,uy,uz)
664
+
665
+ # # Convert string init_state_prep_circ to circuit if needed (matches original logic)
666
+ # if type(init_state_prep_circ)==str:
667
+ # init_state_prep_circ=get_named_init_state_circuit(n,init_state_prep_circ)
668
+
669
+ qc_list=get_circuit(n,ux,uy,uz,init_state_prep_circ,T_list,vel_resolution,flag_qubits=flag_qubits,midcircuit_meas=False)
670
+
671
+ backend = provider.get_backend("qpu.forte-enterprise-1")
672
+
673
+ # Create Sampler primitive bound to the backend
674
+
675
+ job = backend.run(qc_list, shots=shots)
676
+
677
+ # sampler = Sampler(mode=backend)
678
+
679
+ # # Submit job: pass a list of PUBs (we send one PUB [qc_compiled])
680
+ # job = sampler.run(qc_compiled_list, shots=shots)
681
+ log("Job submitted; waiting for result...")
682
+
683
+ def get_job_result(j):
684
+
685
+ log("Waiting for job results (this may take time)...")
686
+
687
+ output=[]
688
+
689
+ for i,T_total in enumerate(T_list):
690
+
691
+ counts = j.get_counts(i)
692
+
693
+ # Suppress verbose logging by passing None as logger
694
+ pts, counts = load_samples(counts, T_total, logger=None, flag_qubits=flag_qubits, midcircuit_meas=False)
695
+ output+=[estimate_density(pts, counts, bandwidth=0.05, grid_size=output_resolution)]
696
+
697
+ log(f"Processing complete: {len(output)} timestep(s)")
698
+ fig = plot_density_isosurface_slider(output, T_list)
699
+ return output, fig
700
+
701
+ return job,get_job_result
702
+
703
+
704
 
705
  from qiskit_aer import AerSimulator
706
 
 
1094
  # show_initial_distribution(n=n, init_state_name="sin", sine_k_x=1, sine_k_y=1, sine_k_z=1)
1095
 
1096
  # Step 3: Run simulation - pass the pre-built circuit
1097
+ job, get_job_result = run_sampling_hw_ionq(
1098
  n=n,
1099
  ux="1",
1100
  uy="1",
 
1103
  T_list=[1,2],
1104
  shots=2**19,
1105
  vel_resolution=2,
1106
+ output_resolution=16,
1107
  flag_qubits=True
1108
  )
1109
 
qlbm/visualize_counts.py CHANGED
@@ -22,7 +22,7 @@ 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, logger=None, flag_qubits=False):
26
  """
27
  Load samples from measurement counts dictionary.
28
 
@@ -51,23 +51,29 @@ def load_samples(d, T_total, logger=None, flag_qubits=False):
51
  pts = []
52
  counts = []
53
 
54
- pref_length=6*T_total
55
  if flag_qubits:
 
56
  pref_length=12*T_total
 
 
 
 
 
57
 
58
  if d is None or len(d) == 0:
59
- log("Warning: Empty counts dictionary")
60
- return np.array(pts), np.array(counts)
61
 
62
  # Debug: show sample bitstrings
63
  sample_keys = list(d.keys())[:3]
64
  log(f"Sample bitstrings (first 3): {sample_keys}")
65
  if sample_keys:
66
- log(f"Bitstring length: {len(sample_keys[0])}")
67
- log(f"Expected prefix length: {pref_length}")
68
 
69
  for bs, cnt in d.items():
70
  # Check if the direction qubits (first 6*T_total bits) are all zeros
 
71
  prefix = bs[:pref_length]
72
  expected_prefix = "0" * pref_length
73
 
 
22
  maxv = (1 << t)
23
  return ix / maxv, iy / maxv, iz / maxv
24
 
25
+ def load_samples(d, T_total, logger=None, flag_qubits=False, midcircuit_meas=True):
26
  """
27
  Load samples from measurement counts dictionary.
28
 
 
51
  pts = []
52
  counts = []
53
 
 
54
  if flag_qubits:
55
+ if midcircuit_meas:
56
  pref_length=12*T_total
57
+ else:
58
+ pref_length=6*(T_total+1)
59
+ else:
60
+ pref_length=6*T_total
61
+
62
 
63
  if d is None or len(d) == 0:
64
+ log("Warning: Empty counts dictionary")
65
+ return np.array(pts), np.array(counts)
66
 
67
  # Debug: show sample bitstrings
68
  sample_keys = list(d.keys())[:3]
69
  log(f"Sample bitstrings (first 3): {sample_keys}")
70
  if sample_keys:
71
+ log(f"Bitstring length: {len(sample_keys[0])}")
72
+ log(f"Expected prefix length: {pref_length}")
73
 
74
  for bs, cnt in d.items():
75
  # Check if the direction qubits (first 6*T_total bits) are all zeros
76
+ bs=bs.replace(" ","")
77
  prefix = bs[:pref_length]
78
  expected_prefix = "0" * pref_length
79