|
12 | 12 |
|
13 | 13 | from __future__ import annotations
|
14 | 14 |
|
15 |
| -from typing import List, Tuple |
16 |
| - |
17 | 15 | from qiskit.circuit import QuantumCircuit
|
18 |
| -from qiskit.providers.backend import Backend |
19 |
| -from qiskit.qasm import pi |
20 |
| - |
21 |
| -import numpy as np |
| 16 | +from qiskit.converters import circuit_to_dag |
| 17 | +from qiskit.transpiler import Target |
| 18 | +from qiskit.transpiler.passes.layout.vf2_utils import ( |
| 19 | + build_average_error_map, |
| 20 | + build_interaction_graph, |
| 21 | + score_layout, |
| 22 | +) |
22 | 23 |
|
23 | 24 |
|
24 |
| -def cost_func_scaled_cr( |
25 |
| - circ: QuantumCircuit, |
26 |
| - layouts: List[List[int]], |
27 |
| - backend: Backend, |
28 |
| -) -> List[Tuple[List[int], float]]: |
29 |
| - """ |
30 |
| - A custom cost function that includes T1 and T2 computed during idle periods, |
31 |
| - for an already transpiled and scheduled circuit circ |
| 25 | +def avg_error_score(qc_isa: QuantumCircuit, target: Target) -> float: |
| 26 | + """Calculate average error score using vf2 utils |
32 | 27 |
|
33 |
| - Parameters: |
34 |
| - circ (QuantumCircuit): scheduled circuit of interest |
35 |
| - backend (IBMQBackend): An IBM Quantum backend instance |
| 28 | + Args: |
| 29 | + qc_isa (QuantumCircuit): transpiled circuit |
| 30 | + target (Target): backend target |
36 | 31 |
|
37 | 32 | Returns:
|
38 |
| - float: error |
| 33 | + float: average error score determined by average error map |
39 | 34 | """
|
40 |
| - out = [] |
41 |
| - props = backend.properties() |
42 |
| - inst_sched_map = backend.defaults().instruction_schedule_map |
43 |
| - dt = backend.configuration().dt |
44 |
| - num_qubits = backend.configuration().num_qubits |
45 |
| - t1s = [props.qubit_property(qq, "T1")[0] for qq in range(num_qubits)] |
46 |
| - t2s = [props.qubit_property(qq, "T2")[0] for qq in range(num_qubits)] |
47 |
| - |
48 |
| - error = 0.0 |
49 |
| - fid = 1.0 |
50 |
| - touched = set() |
51 |
| - for layout in layouts: |
52 |
| - for item in circ.data: |
53 |
| - if item[0].num_qubits == 2: |
54 |
| - q0 = circ.find_bit(item[1][0]).index |
55 |
| - q1 = circ.find_bit(item[1][1]).index |
56 |
| - if inst_sched_map.has("cx", qubits=[q0, q1]): |
57 |
| - basis_2q_error = props.gate_error("cx", [q0, q1]) |
58 |
| - elif inst_sched_map.has("ecr", qubits=[q0, q1]): |
59 |
| - basis_2q_error = props.gate_error("ecr", [q0, q1]) |
60 |
| - elif inst_sched_map.has("ecr", qubits=[q1, q0]): |
61 |
| - basis_2q_error = props.gate_error("ecr", [q1, q0]) |
62 |
| - else: |
63 |
| - print( |
64 |
| - f"{backend.name} missing 2Q gate between qubit pair ({q0}, {q1})" |
65 |
| - ) |
66 |
| - |
67 |
| - if item[0].name == "cx": |
68 |
| - fid *= 1 - basis_2q_error |
69 |
| - touched.add(q0) |
70 |
| - touched.add(q1) |
71 |
| - |
72 |
| - # if it is a scaled pulse derived from cx |
73 |
| - elif item[0].name == "rzx": |
74 |
| - cr_error = np.abs(float(item[0].params[0]) / (pi / 2)) * basis_2q_error |
75 |
| - |
76 |
| - # assumes control qubit is actually control for cr |
77 |
| - echo_error = props.gate_error("x", q0) |
78 |
| - |
79 |
| - fid *= 1 - max(cr_error, 2 * echo_error) |
80 |
| - elif item[0].name == "secr": |
81 |
| - cr_error = ( |
82 |
| - np.abs((float(item[0].params[0])) / (pi / 2)) * basis_2q_error |
83 |
| - ) |
84 |
| - |
85 |
| - # assumes control qubit is actually control for cr |
86 |
| - echo_error = props.gate_error("x", q0) |
87 |
| - |
88 |
| - fid *= 1 - max(cr_error, echo_error) |
89 |
| - |
90 |
| - elif item[0].name in ["sx", "x"]: |
91 |
| - q0 = circ.find_bit(item[1][0]).index |
92 |
| - fid *= 1 - props.gate_error(item[0].name, q0) |
93 |
| - touched.add(q0) |
94 |
| - |
95 |
| - # if it is a dynamical decoupling pulse |
96 |
| - elif item[0].name in ["xp", "xm", "y", "yp", "ym"]: |
97 |
| - q0 = circ.find_bit(item[1][0]).index |
98 |
| - fid *= 1 - props.gate_error("x", q0) |
99 |
| - touched.add(q0) |
100 |
| - |
101 |
| - elif item[0].name == "measure": |
102 |
| - q0 = circ.find_bit(item[1][0]).index |
103 |
| - fid *= 1 - props.readout_error(q0) |
104 |
| - touched.add(q0) |
105 |
| - |
106 |
| - elif item[0].name == "delay": |
107 |
| - q0 = circ.find_bit(item[1][0]).index |
108 |
| - # Ignore delays that occur before gates |
109 |
| - # This assumes you are in ground state and errors |
110 |
| - # do not occur. |
111 |
| - if q0 in touched: |
112 |
| - time = item[0].duration * dt |
113 |
| - fid *= 1 - idle_error(time, t1s[q0], t2s[q0]) |
114 |
| - |
115 |
| - error = 1 - fid |
116 |
| - out.append((layout, error)) |
117 |
| - return out |
118 |
| - |
119 |
| - |
120 |
| -def idle_error( |
121 |
| - time: float, |
122 |
| - t1: float, |
123 |
| - t2: float, |
124 |
| -) -> float: |
125 |
| - """Compute the approx. idle error from T1 and T2 |
126 |
| - Parameters: |
127 |
| - time (float): Delay time in sec |
128 |
| - t1 (float): T1 time in sec |
129 |
| - t2, (float): T2 time in sec |
130 |
| - Returns: |
131 |
| - float: Idle error |
132 |
| - """ |
133 |
| - t2 = min(t1, t2) |
134 |
| - rate1 = 1 / t1 |
135 |
| - rate2 = 1 / t2 |
136 |
| - p_reset = 1 - np.exp(-time * rate1) |
137 |
| - p_z = (1 - p_reset) * (1 - np.exp(-time * (rate2 - rate1))) / 2 |
138 |
| - return p_z + p_reset |
139 |
| - |
140 |
| - |
141 |
| -def cost_func_ecr( |
142 |
| - circ: QuantumCircuit, |
143 |
| - layouts: List[List[int]], |
144 |
| - backend: Backend, |
145 |
| -) -> List[Tuple[List[int], float]]: |
146 |
| - """ |
147 |
| - A custom cost function that includes ECR gates in either direction |
148 |
| -
|
149 |
| - Parameters: |
150 |
| - circ (QuantumCircuit): circuit of interest |
151 |
| - layouts (list of lists): List of specified layouts |
152 |
| - backend (IBMQBackend): An IBM Quantum backend instance |
153 |
| -
|
154 |
| - Returns: |
155 |
| - list: Tuples of layout and cost |
156 |
| - """ |
157 |
| - out = [] |
158 |
| - inst_sched_map = backend.defaults().instruction_schedule_map |
159 |
| - props = backend.properties() |
160 |
| - for layout in layouts: |
161 |
| - error = 0.0 |
162 |
| - fid = 1.0 |
163 |
| - touched = set() |
164 |
| - for item in circ.data: |
165 |
| - if item[0].name == "ecr": |
166 |
| - q0 = layout[circ.find_bit(item[1][0]).index] |
167 |
| - q1 = layout[circ.find_bit(item[1][1]).index] |
168 |
| - if inst_sched_map.has("ecr", [q0, q1]): |
169 |
| - fid *= 1 - props.gate_error("ecr", [q0, q1]) |
170 |
| - elif inst_sched_map.has("ecr", [q1, q0]): |
171 |
| - fid *= 1 - props.gate_error("ecr", [q1, q0]) |
172 |
| - else: |
173 |
| - print( |
174 |
| - f"{backend.name} does not support {item[0].name} \ |
175 |
| - for qubit pair ({q0}, {q1})" |
176 |
| - ) |
177 |
| - touched.add(q0) |
178 |
| - touched.add(q1) |
179 |
| - |
180 |
| - elif item[0].name in ["sx", "x"]: |
181 |
| - q0 = layout[circ.find_bit(item[1][0]).index] |
182 |
| - fid *= 1 - props.gate_error(item[0].name, q0) |
183 |
| - touched.add(q0) |
184 |
| - |
185 |
| - elif item[0].name == "measure": |
186 |
| - q0 = layout[circ.find_bit(item[1][0]).index] |
187 |
| - fid *= 1 - props.readout_error(q0) |
188 |
| - touched.add(q0) |
189 |
| - |
190 |
| - error = 1 - fid |
191 |
| - out.append((layout, error)) |
192 |
| - return out |
| 35 | + init_layout = qc_isa.layout.final_index_layout() |
| 36 | + dag = circuit_to_dag(qc_isa) |
| 37 | + |
| 38 | + layout = {idx: init_layout[idx] for idx in range(len(init_layout))} |
| 39 | + avg_error_map = build_average_error_map(target, None, None) |
| 40 | + im_graph, im_graph_node_map, reverse_im_graph_node_map, _ = build_interaction_graph( |
| 41 | + dag, strict_direction=False |
| 42 | + ) |
| 43 | + return score_layout( |
| 44 | + avg_error_map, layout, im_graph_node_map, reverse_im_graph_node_map, im_graph |
| 45 | + ) |
0 commit comments