Skip to content

Commit 0d5416a

Browse files
authored
Merge pull request #117 from nbronn/bug/mapomatic-upgrade
removed mapomatic dependency
2 parents 19c6eea + 0aa9df2 commit 0d5416a

10 files changed

+162
-521
lines changed

Diff for: pyproject.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ classifiers = [
2424
requires-python = ">=3.8"
2525

2626
dependencies = [
27-
"mapomatic==0.9",
28-
"qiskit==0.46.0",
27+
"qiskit==1.1.1",
2928
"qiskit-aer",
3029
"qiskit-ibm-runtime",
3130
]

Diff for: qiskit_research/utils/convenience.py

+14-15
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
from qiskit.circuit import QuantumCircuit
1616
from qiskit.circuit import Gate
1717
from qiskit.circuit.library import XGate
18-
from qiskit.providers.backend import Backend
19-
from qiskit.transpiler import PassManager
18+
from qiskit.providers import Backend
19+
from qiskit.transpiler import PassManager, Target
2020
from qiskit.transpiler.passes.scheduling import ALAPScheduleAnalysis
2121
from qiskit.transpiler.passes.scheduling.scheduling.base_scheduler import BaseScheduler
2222

@@ -37,7 +37,7 @@
3737

3838
def add_dynamical_decoupling(
3939
circuits: Union[QuantumCircuit, List[QuantumCircuit], List[List[QuantumCircuit]]],
40-
backend: Backend,
40+
target: Target,
4141
dd_str: str,
4242
scheduler: BaseScheduler = ALAPScheduleAnalysis,
4343
add_pulse_cals: bool = False,
@@ -47,12 +47,12 @@ def add_dynamical_decoupling(
4747
"""Add dynamical decoupling sequences and calibrations to circuits.
4848
4949
Adds dynamical decoupling sequences and the calibrations necessary
50-
to run them on an IBM backend.
50+
to run them on an IBM backend target.
5151
5252
Args:
5353
circuits (Union[QuantumCircuit, List[QuantumCircuit], List[List[QuantumCircuit]]]):
5454
input QuantumCircuit or sequences thereof.
55-
backend (Backend): Backend to run on; gate timing is required for this method.
55+
target (Target): Backend to run on; gate timing is required for this method.
5656
dd_str (str): String describing DD sequence to use.
5757
scheduler (BaseScheduler, optional): Scheduler, defaults to ALAPScheduleAnalysis.
5858
add_pulse_cals (bool, optional): Add Pulse calibrations for non-basis
@@ -67,23 +67,22 @@ def add_dynamical_decoupling(
6767
single or sequence type of QuantumCircuit, shceduled with DD sequences
6868
inserted into idle times.
6969
"""
70-
7170
pass_manager = PassManager(
7271
list(
7372
dynamical_decoupling_passes(
74-
backend, dd_str, scheduler, urdd_pulse_num=urdd_pulse_num
73+
target, dd_str, scheduler, urdd_pulse_num=urdd_pulse_num
7574
)
7675
)
7776
)
7877
if isinstance(circuits, QuantumCircuit) or isinstance(circuits[0], QuantumCircuit):
7978
circuits_dd = pass_manager.run(circuits)
8079
if add_pulse_cals:
81-
add_pulse_calibrations(circuits_dd, backend, pulse_method=pulse_method)
80+
add_pulse_calibrations(circuits_dd, target, pulse_method=pulse_method)
8281
else:
8382
circuits_dd = [pass_manager.run(circs) for circs in circuits]
8483
if add_pulse_cals:
8584
for circs_dd in circuits_dd:
86-
add_pulse_calibrations(circs_dd, backend, pulse_method=pulse_method)
85+
add_pulse_calibrations(circs_dd, target, pulse_method=pulse_method)
8786

8887
return circuits_dd
8988

@@ -169,7 +168,7 @@ def add_pauli_twirls(
169168
@overload
170169
def scale_cr_pulses(
171170
circuits: List[QuantumCircuit],
172-
backend: Backend,
171+
target: Target,
173172
unroll_rzx_to_ecr: Optional[bool] = True,
174173
force_zz_matches: Optional[bool] = True,
175174
param_bind: Optional[dict] = None,
@@ -179,7 +178,7 @@ def scale_cr_pulses(
179178
@overload
180179
def scale_cr_pulses(
181180
circuits: QuantumCircuit,
182-
backend: Backend,
181+
target: Target,
183182
unroll_rzx_to_ecr: Optional[bool] = True,
184183
force_zz_matches: Optional[bool] = True,
185184
param_bind: Optional[dict] = None,
@@ -188,7 +187,7 @@ def scale_cr_pulses(
188187

189188
def scale_cr_pulses(
190189
circuits,
191-
backend,
190+
target,
192191
unroll_rzx_to_ecr: Optional[bool] = True,
193192
force_zz_matches: Optional[bool] = True,
194193
param_bind: Optional[dict] = None,
@@ -204,7 +203,7 @@ def scale_cr_pulses(
204203
pass_manager = PassManager(
205204
list(
206205
cr_scaling_passes(
207-
backend,
206+
target,
208207
templates,
209208
unroll_rzx_to_ecr=unroll_rzx_to_ecr,
210209
force_zz_matches=force_zz_matches,
@@ -217,15 +216,15 @@ def scale_cr_pulses(
217216

218217
def attach_cr_pulses(
219218
circuits: Union[QuantumCircuit, List[QuantumCircuit]],
220-
backend: Backend,
219+
target: Target,
221220
param_bind: dict,
222221
) -> Union[QuantumCircuit, List[QuantumCircuit]]:
223222
"""
224223
Scale circuits using Pulse scaling technique from
225224
http://arxiv.org/abs/2012.11660. Binds parameters
226225
in param_bind and attaches pulse gates.
227226
"""
228-
pass_manager = PassManager(list(pulse_attaching_passes(backend, param_bind)))
227+
pass_manager = PassManager(list(pulse_attaching_passes(target, param_bind)))
229228
return pass_manager.run(circuits)
230229

231230

Diff for: qiskit_research/utils/cost_funcs.py

+24-171
Original file line numberDiff line numberDiff line change
@@ -12,181 +12,34 @@
1212

1313
from __future__ import annotations
1414

15-
from typing import List, Tuple
16-
1715
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+
)
2223

2324

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
3227
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
3631
3732
Returns:
38-
float: error
33+
float: average error score determined by average error map
3934
"""
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

Comments
 (0)