Skip to content

Commit dfe577f

Browse files
committed
Merge changes for cudaq estimate_depth from develop
1 parent e4feb5d commit dfe577f

File tree

1 file changed

+136
-87
lines changed

1 file changed

+136
-87
lines changed

_common/cudaq/execute.py

Lines changed: 136 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -307,39 +307,70 @@ def execute_circuit (batched_circuit):
307307
# Get circuit metrics fom the circuit passed in
308308
def get_circuit_metrics(qc, qc_size):
309309

310-
# get resource info from cudaq
311-
resources = cudaq.estimate_resources(qc[0], *qc[1])
312-
resources_str = str(resources)
310+
# the new code is implemented in CUDA-Q ver 0.13+, if it fails fall back to old code
311+
try:
313312

314-
import re
315-
316-
# Get total gates (not needed as we use the .count() function)
317-
#total_match = re.search(r'Total # of gates:\s*(\d+)', resources_str)
318-
#total_gates = int(total_match.group(1)) if total_match else 0
319-
320-
total_gates = resources.count()
321-
322-
# the resources object returned is not a dict; need to parse the string to get 2q gates
323-
# Get all gate counts that start with 'c' (controlled/2-qubit gates)
324-
two_qubit_gates = 0
325-
for line in resources_str.split('\n'):
326-
match = re.match(r'\s*(c\w+)\s*:\s*(\d+)', line)
327-
if match:
328-
two_qubit_gates += int(match.group(2))
313+
# get resource info from cudaq
314+
resources = cudaq.estimate_resources(qc[0], *qc[1])
315+
#resources_str = str(resources)
316+
317+
#print(resources)
318+
319+
# Get total gates (not needed as we use the .count() function)
320+
# import re
321+
# total_match = re.search(r'Total # of gates:\s*(\d+)', resources_str)
322+
# total_gates = int(total_match.group(1)) if total_match else 0
323+
324+
total_gates = resources.count()
325+
326+
two_qubit_gates = 0
327+
two_qubit_gates += resources.count_controls('x', 1)
328+
two_qubit_gates += resources.count_controls('y', 1)
329+
two_qubit_gates += resources.count_controls('z', 1)
330+
two_qubit_gates += resources.count_controls('r1', 1)
331+
two_qubit_gates += resources.count_controls('rx', 1)
332+
two_qubit_gates += resources.count_controls('ry', 1)
333+
two_qubit_gates += resources.count_controls('rz', 1)
334+
335+
#print(f"... depth = {resources.count_depth()}") # doesn't exist yet
336+
#print(f"Total: {total_gates}, 2-qubit: {two_qubit_gates}")
329337

330-
# print(f"Total: {total_gates}, 2-qubit: {two_qubit_gates}")
338+
# obtain an estimate of circuit depth
339+
qc_depth = estimate_depth(qc_size, total_gates, two_qubit_gates)
340+
#print(qc_depth)
341+
342+
# this exact computation is not used, currently, as it is slow
343+
#qc_depth = compute_circuit_depth(qc[0](*qc[1]))
344+
#print(qc_depth)
345+
346+
qc_xi = two_qubit_gates / max(total_gates, 1)
347+
qc_n2q = two_qubit_gates
348+
349+
# not currently used
350+
qc_count_ops = total_gates
351+
352+
# this is used for CUDA-Q versions 0.12 or earlier
353+
except:
354+
355+
# compute depth and gate counts based on number of qubits
356+
qc_depth = 4 * pow(qc_size, 2)
331357

332-
qc_depth = estimate_depth(qc_size, total_gates, two_qubit_gates)
333-
#print(qc_depth)
334-
335-
qc_xi = two_qubit_gates / max(total_gates, 1)
336-
qc_n2q = two_qubit_gates
337-
338-
qc_count_ops = total_gates
358+
qc_xi = 0.5
339359

360+
qc_n2q = int(qc_depth * 0.75)
361+
qc_tr_depth = qc_depth
362+
qc_tr_size = qc_size
363+
qc_tr_xi = qc_xi
364+
qc_tr_n2q = qc_n2q
365+
366+
# not currently used
367+
qc_count_ops = qc_depth
368+
340369
return qc_depth, qc_size, qc_count_ops, qc_xi, qc_n2q
341370

342-
# Make estimate of circuit depth using heuristic approach
371+
372+
# Make estimate of circuit depth using heuristic approach; depth not provided by cudaq
373+
# This is somewhat simplistic, but we have not found better solution
343374
def estimate_depth(num_qubits, total_gates, two_qubit_gates):
344375
N = num_qubits
345376
K = two_qubit_gates
@@ -348,11 +379,23 @@ def estimate_depth(num_qubits, total_gates, two_qubit_gates):
348379
# Theoretical minimum (perfect packing - 2Qs and 1Qs and MZ)
349380
depth_min = math.ceil(2*K / N) + math.ceil(S / N) + 1
350381

351-
# Realistic estimate (assume 40% packing efficiency)
352-
depth_estimate = depth_min * 2.5
382+
# Theoretical maximum, sparse distribution
383+
depth_max = K + S + 1
384+
385+
# use xi factor as proxy for level of sparseness in layout (larger = sparser)
386+
qc_xi = two_qubit_gates / max(total_gates, 1)
387+
depth_range = depth_max - depth_min
388+
depth_estimate = math.ceil(depth_min + depth_range * qc_xi)
389+
390+
#print(f" ... min = {depth_min}, max = {depth_max}, depth = {depth_estimate}")
391+
392+
# Realistic estimate (assume 40% packing efficiency) (used with min only)
393+
# depth_estimate = depth_min * 2.5
353394

354395
return depth_estimate
396+
355397

398+
#########################################################################
356399

357400
# klunky way to know the last group executed
358401
last_group = None
@@ -530,73 +573,82 @@ def execute_circuit_immed (circuit: list, num_shots: int):
530573
if verbose:
531574
print(f'... execute_circuit_immed({circuit}, {num_shots})')
532575

533-
#active_circuit = copy.copy(batched_circuit)
534-
#active_circuit["launch_time"] = time.time()
535-
536-
#num_shots = batched_circuit["shots"]
537-
538-
# Initiate execution
539-
#circuit = batched_circuit["qc"]
540-
541-
# create a pseudo-job to perform metrics processing upon return
542-
job = Job()
543-
544-
# draw the circuit, but only for debugging
545-
# print(cudaq.draw(circuit[0], *circuit[1]))
546-
547-
ts = time.time()
548-
549-
# call sample() on circuit with its list of arguments
550-
if verbose: print(f"... during exec, noise model is: {noise}")
551-
if noise is None:
552-
if verbose: print("... executing without noise")
553-
result = cudaq.sample(circuit[0], *circuit[1], shots_count=num_shots)
554-
else:
555-
if verbose: print("... executing WITH noise")
556-
result = cudaq.sample(circuit[0], *circuit[1], shots_count=num_shots, noise_model=noise)
557-
558-
# control results print at benchmark level
559-
#if verbose: print(result)
576+
try:
577+
#active_circuit = copy.copy(batched_circuit)
578+
#active_circuit["launch_time"] = time.time()
560579

561-
exec_time = time.time() - ts
562-
563-
# store the result object on the job for processing in job_complete
564-
job.executor_result = result
565-
job.exec_time = exec_time
566-
567-
if verbose:
568-
print(f"... result = {len(result)} {result}")
569-
''' for debugging, a better way to see the counts, as the type of result is something Quake
570-
for key, val in result.items():
571-
print(f"... {key}:{val}")
572-
'''
573-
print(f"... register names = {result.register_names}")
574-
print(result.dump())
575-
#print(f"... register dump = {result.get_register_counts('__global__').dump()}")
576-
#result.get_register_counts("b1").dump()
577-
#print(result.get_sequential_data())
580+
#num_shots = batched_circuit["shots"]
581+
582+
# Initiate execution
583+
#circuit = batched_circuit["qc"]
584+
585+
# create a pseudo-job to perform metrics processing upon return
586+
job = Job()
587+
588+
# draw the circuit, but only for debugging
589+
# print(cudaq.draw(circuit[0], *circuit[1]))
590+
591+
ts = time.time()
592+
593+
# call sample() on circuit with its list of arguments
594+
if verbose: print(f"... during exec, noise model is: {noise}")
595+
if noise is None:
596+
if verbose: print("... executing without noise")
597+
result = cudaq.sample(circuit[0], *circuit[1], shots_count=num_shots)
598+
else:
599+
if verbose: print("... executing WITH noise")
600+
result = cudaq.sample(circuit[0], *circuit[1], shots_count=num_shots, noise_model=noise)
601+
602+
# control results print at benchmark level
603+
#if verbose: print(result)
604+
605+
exec_time = time.time() - ts
606+
607+
# store the result object on the job for processing in job_complete
608+
job.executor_result = result
609+
job.exec_time = exec_time
610+
611+
if verbose:
612+
print(f"... result = {len(result)} {result}")
613+
''' for debugging, a better way to see the counts, as the type of result is something Quake
614+
for key, val in result.items():
615+
print(f"... {key}:{val}")
616+
'''
617+
print(f"... register names = {result.register_names}")
618+
print(result.dump())
619+
#print(f"... register dump = {result.get_register_counts('__global__').dump()}")
620+
#result.get_register_counts("b1").dump()
621+
#print(result.get_sequential_data())
622+
623+
except Exception as ex:
624+
print(f"ERROR attempting to compute cicuit metrics")
625+
print(ex)
578626

579627
# put job into the active circuits with circuit info
580628
#active_circuits[job] = active_circuit
581629
#print("... active_circuit = ", str(active_circuit))
582630

583631
# ***********************************
584-
585-
# store circuit dimensional metrics
586-
# DEVNOTE: this is not accurate; it is provided so the volumetric plots show something
587-
"""
588-
# compute depth and gate counts based on number of qubits
589-
qc_size = int(active_circuit["group"])
590-
qc_depth = 4 * pow(qc_size, 2)
591-
592-
qc_xi = 0.5
632+
qc_depth, qc_size, qc_count_ops, qc_xi, qc_n2q = 0, 0, 0, 0, 0
633+
try:
634+
# number of qubits is stored in the "group" field
635+
#qc_size = int(circuit["group"])
636+
qc_size = circuit[1][0] # the first item after the kernel is alway num_qubits
637+
638+
# obtain initial circuit metrics
639+
qc_depth, qc_size, qc_count_ops, qc_xi, qc_n2q = get_circuit_metrics(circuit, qc_size)
593640

594-
qc_n2q = int(qc_depth * 0.75)
641+
except Exception as ex:
642+
print(f"ERROR attempting to compute cicuit metrics")
643+
print(ex)
644+
595645
qc_tr_depth = qc_depth
596646
qc_tr_size = qc_size
647+
qc_tr_count_ops = qc_count_ops
597648
qc_tr_xi = qc_xi
598649
qc_tr_n2q = qc_n2q
599650

651+
"""
600652
# store circuit dimensional metrics
601653
metrics.store_metric(active_circuit["group"], active_circuit["circuit"], 'depth', qc_depth)
602654
metrics.store_metric(active_circuit["group"], active_circuit["circuit"], 'size', qc_size)
@@ -607,11 +659,8 @@ def execute_circuit_immed (circuit: list, num_shots: int):
607659
metrics.store_metric(active_circuit["group"], active_circuit["circuit"], 'tr_size', qc_tr_size)
608660
metrics.store_metric(active_circuit["group"], active_circuit["circuit"], 'tr_xi', qc_tr_xi)
609661
metrics.store_metric(active_circuit["group"], active_circuit["circuit"], 'tr_n2q', qc_tr_n2q)
610-
611-
##############
612-
# Here we complete the job immediately
613-
job_complete(job)
614662
"""
663+
615664
"""
616665
# klunky way to know the last group executed
617666
#last_group = None

0 commit comments

Comments
 (0)