Skip to content

Commit 604fccc

Browse files
Transpile parallel experiment sub circuits (#680)
* Update ParallelExperiment to transpile sub experimetns Parallel Experiment will now transpile sub experiments according to their individual transpile options before combining them. The combined circuits will then be transpiled again according to any options set in the parallel experiment transpile options. * recursively set composite exp transpile options Update CompositeExperiment.set_transpile_options to recursively set transpile options for each of the component experiments so that these options are not ignored for batch experiments.
1 parent f226f6c commit 604fccc

File tree

5 files changed

+105
-14
lines changed

5 files changed

+105
-14
lines changed

qiskit_experiments/framework/composite/composite_experiment.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ def __init__(
6969
def circuits(self):
7070
pass
7171

72+
def set_transpile_options(self, **fields):
73+
super().set_transpile_options(**fields)
74+
# Recursively set transpile options of component experiments
75+
for exp in self._experiments:
76+
exp.set_transpile_options(**fields)
77+
7278
@property
7379
def num_experiments(self):
7480
"""Return the number of sub experiments"""

qiskit_experiments/framework/composite/parallel_experiment.py

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from qiskit import QuantumCircuit, ClassicalRegister
1818
from qiskit.providers.backend import Backend
19+
from qiskit_experiments.exceptions import QiskitError
1920
from .composite_experiment import CompositeExperiment, BaseExperiment
2021
from .composite_analysis import CompositeAnalysis
2122

@@ -63,20 +64,27 @@ def circuits(self):
6364

6465
sub_circuits = []
6566
sub_qubits = []
67+
sub_maps = []
6668
sub_size = []
6769
num_qubits = 0
6870

6971
# Generate data for combination
70-
for expr in self._experiments:
72+
for sub_exp in self._experiments:
73+
74+
# Generate transpiled subcircuits
75+
circuits = sub_exp._transpiled_circuits()
76+
7177
# Add subcircuits
72-
circs = expr.circuits()
73-
sub_circuits.append(circs)
74-
sub_size.append(len(circs))
78+
sub_circuits.append(circuits)
79+
sub_size.append(len(circuits))
7580

76-
# Add sub qubits
77-
qubits = list(range(num_qubits, num_qubits + expr.num_qubits))
81+
# Sub experiment logical qubits in the combined circuits full qubits
82+
qubits = list(range(num_qubits, num_qubits + sub_exp.num_qubits))
7883
sub_qubits.append(qubits)
79-
num_qubits += expr.num_qubits
84+
# Construct mapping for the sub-experiments logical qubits to physical qubits
85+
# in the full combined circuits
86+
sub_maps.append({q: qubits[i] for i, q in enumerate(sub_exp.physical_qubits)})
87+
num_qubits += sub_exp.num_qubits
8088

8189
# Generate empty joint circuits
8290
num_circuits = max(sub_size)
@@ -97,9 +105,37 @@ def circuits(self):
97105
sub_circ = sub_circuits[exp_idx][circ_idx]
98106
num_clbits = circuit.num_clbits
99107
qubits = sub_qubits[exp_idx]
108+
qargs_map = sub_maps[exp_idx]
100109
clbits = list(range(num_clbits, num_clbits + sub_circ.num_clbits))
101110
circuit.add_register(ClassicalRegister(sub_circ.num_clbits))
102-
circuit.append(sub_circ, qubits, clbits)
111+
112+
# Apply transpiled subcircuit
113+
# Note that this assumes the circuit was not expanded to use
114+
# any qubits outside the specified physical qubits
115+
for inst, qargs, cargs in sub_circ.data:
116+
try:
117+
mapped_qargs = [
118+
circuit.qubits[qargs_map[sub_circ.find_bit(i).index]] for i in qargs
119+
]
120+
except KeyError as ex:
121+
# Instruction is outside physical qubits for the component
122+
# experiment.
123+
# This could legitimately happen if the subcircuit was
124+
# explicitly scheduled during transpilation which would
125+
# insert delays on all auxillary device qubits.
126+
# We skip delay instructions to allow for this.
127+
if inst.name == "delay":
128+
continue
129+
raise QiskitError(
130+
"Component experiment has been transpiled outside of the "
131+
"allowed physical qubits for that component. Check the "
132+
"experiment is valid on the backends coupling map."
133+
) from ex
134+
mapped_cargs = [
135+
circuit.clbits[clbits[sub_circ.find_bit(i).index]] for i in cargs
136+
]
137+
circuit._append(inst, mapped_qargs, mapped_cargs)
138+
103139
# Add subcircuit metadata
104140
circuit.metadata["composite_index"].append(exp_idx)
105141
circuit.metadata["composite_metadata"].append(sub_circ.metadata)
@@ -114,6 +150,6 @@ def circuits(self):
114150
)
115151

116152
# Add joint circuit to returned list
117-
joint_circuits.append(circuit.decompose())
153+
joint_circuits.append(circuit)
118154

119155
return joint_circuits

releasenotes/notes/batch-transpile-b3c5e739d2eead63.yaml

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
upgrade:
3+
- |
4+
The component experiment circuits of :class:`.ParallelExperiment` and
5+
:class:`.BatchExperiment` are now explicitly transpiled using the
6+
respective component experiments
7+
:meth:`~.BaseExperiment.transpile_options` before being combined into
8+
the composite circuits returned by the :class:`.BaseExperiment.circuits`
9+
method.
10+
11+
Any transpile options set directly on the :class:`.ParallelExperiment`
12+
or :class:`.BatchExperiment` will also be applied as a transpile option
13+
to each component experiment.
14+
- |
15+
The circuits returned by the :meth:`.ParallelExperiment.circuits` method
16+
of parallel circuits will now always be the combined circuits circuits
17+
of the transpiled circuits of the individual component experiments
18+
transpiled with that experiments transpile options.

test/test_t1.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,42 @@ def test_t1_low_quality(self):
191191
result = res[1]
192192
self.assertEqual(result.quality, "bad")
193193

194+
def test_t1_parallel_exp_transpile(self):
195+
"""Test parallel transpile options for T1 experiment"""
196+
num_qubits = 5
197+
instruction_durations = []
198+
for i in range(num_qubits):
199+
instruction_durations += [
200+
("rx", [i], (i + 1) * 10, "ns"),
201+
("measure", [i], (i + 1) * 1000, "ns"),
202+
]
203+
coupling_map = [[i - 1, i] for i in range(1, num_qubits)]
204+
basis_gates = ["rx", "delay"]
205+
206+
exp1 = T1(1, delays=[50e-9, 100e-9, 160e-9])
207+
exp2 = T1(3, delays=[40e-9, 80e-9, 190e-9])
208+
parexp = ParallelExperiment([exp1, exp2])
209+
parexp.set_transpile_options(
210+
basis_gates=basis_gates,
211+
instruction_durations=instruction_durations,
212+
coupling_map=coupling_map,
213+
scheduling_method="alap",
214+
)
215+
216+
circs = parexp.circuits()
217+
for circ in circs:
218+
self.assertEqual(circ.num_qubits, 2)
219+
op_counts = circ.count_ops()
220+
self.assertEqual(op_counts.get("rx"), 2)
221+
self.assertEqual(op_counts.get("delay"), 2)
222+
223+
tcircs = parexp._transpiled_circuits()
224+
for circ in tcircs:
225+
self.assertEqual(circ.num_qubits, num_qubits)
226+
op_counts = circ.count_ops()
227+
self.assertEqual(op_counts.get("rx"), 2)
228+
self.assertGreater(op_counts.get("delay"), num_qubits - 1)
229+
194230
def test_experiment_config(self):
195231
"""Test converting to and from config works"""
196232
exp = T1(0, [1, 2, 3, 4, 5])

0 commit comments

Comments
 (0)