diff --git a/qiskit_experiments/framework/composite/parallel_experiment.py b/qiskit_experiments/framework/composite/parallel_experiment.py index bf384cda96..67f398c90e 100644 --- a/qiskit_experiments/framework/composite/parallel_experiment.py +++ b/qiskit_experiments/framework/composite/parallel_experiment.py @@ -16,7 +16,7 @@ import numpy as np from qiskit import QuantumCircuit, ClassicalRegister -from qiskit.circuit import Clbit +from qiskit.circuit import Clbit, Delay from qiskit.providers.backend import Backend from qiskit_experiments.exceptions import QiskitError from .composite_experiment import CompositeExperiment, BaseExperiment @@ -92,12 +92,39 @@ def _combined_circuits(self, device_layout: bool) -> List[QuantumCircuit]: else: num_qubits = 1 + max(self.physical_qubits) - joint_circuits = [] - sub_qubits = 0 + # Transpile component circuits + transpiled_circuits = [] + + # Max duration for all components that will be combined into a single circuit + max_durations = {} + duration_unit = None + scheduling_method = getattr(self.transpile_options, "scheduling_method", None) + + # Find max durations of subcircuits for exp_idx, sub_exp in enumerate(self._experiments): # Generate transpiled subcircuits sub_circuits = sub_exp._transpiled_circuits() - + transpiled_circuits.append(sub_circuits) + if scheduling_method is not None: + for circ_idx, sub_circ in enumerate(sub_circuits): + if sub_circ.duration is not None: + if duration_unit is None: + duration_unit = sub_circ.unit + if circ_idx not in max_durations: + max_durations[circ_idx] = 0 + max_durations[circ_idx] = max(sub_circ.duration, max_durations[circ_idx]) + if duration_unit != sub_circ.unit: + raise QiskitError( + "Scheduled component experiments are scheduled with" + " different time units." + ) + + # Combine circuits + joint_circuits = [] + sub_qubits = 0 + for exp_idx, (sub_circuits, sub_exp) in enumerate( + zip(transpiled_circuits, self._experiments) + ): # Qubit remapping for non-transpiled circuits if not device_layout: qubits = list(range(sub_qubits, sub_qubits + sub_exp.num_qubits)) @@ -136,6 +163,18 @@ def _combined_circuits(self, device_layout: bool) -> List[QuantumCircuit]: # Apply transpiled subcircuit # Note that this assumes the circuit was not expanded to use # any qubits outside the specified physical qubits + circ_duration = max_durations.get(circ_idx) + pad_time = None + if scheduling_method and sub_circ.duration and circ_duration: + pad_time = abs(circ_duration - sub_circ.duration) + + # If scheduling method is alap prepend shorter sub-circuits with delays + if scheduling_method == "alap" and pad_time: + for i in sub_exp.physical_qubits: + circuit._append( + Delay(pad_time, unit=duration_unit), [circuit.qubits[i]], [] + ) + for inst, qargs, cargs in sub_circ.data: mapped_cargs = [sub_cargs[sub_circ.find_bit(i).index] for i in cargs] try: @@ -158,6 +197,18 @@ def _combined_circuits(self, device_layout: bool) -> List[QuantumCircuit]: ) from ex circuit._append(inst, mapped_qargs, mapped_cargs) + # If scheduling method is alap append shorter sub-circuits with delays + if scheduling_method == "asap" and pad_time: + for i in sub_exp.physical_qubits: + circuit._append( + Delay(pad_time, unit=duration_unit), [circuit.qubits[i]], [] + ) + + # Update duration of circuit + if scheduling_method and circ_duration: + circuit.duration = circ_duration + circuit.unit = duration_unit + # Add subcircuit metadata circuit.metadata["composite_index"].append(exp_idx) circuit.metadata["composite_metadata"].append(sub_circ.metadata) diff --git a/releasenotes/notes/parallel-scheduling-03e2eca55f8d8ff2.yaml b/releasenotes/notes/parallel-scheduling-03e2eca55f8d8ff2.yaml new file mode 100644 index 0000000000..83f9ee1c66 --- /dev/null +++ b/releasenotes/notes/parallel-scheduling-03e2eca55f8d8ff2.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixes :class:`.ParallelExperiment` handling of `scheduling_method` + transpile option for `"asap"` and `"alap"` scheduling to correctly + pad the component experiment subcircuits so that the combined circuit + has a fixed duration on the experiments physical qubits. diff --git a/test/library/characterization/test_t1.py b/test/library/characterization/test_t1.py index fefab78329..16a46a8336 100644 --- a/test/library/characterization/test_t1.py +++ b/test/library/characterization/test_t1.py @@ -351,14 +351,14 @@ def test_t1_parallel_exp_transpile(self): self.assertEqual(circ.num_qubits, 2) op_counts = circ.count_ops() self.assertEqual(op_counts.get("rx"), 2) - self.assertEqual(op_counts.get("delay"), 2) + self.assertEqual(op_counts.get("delay"), 3) tcircs = parexp._transpiled_circuits() for circ in tcircs: self.assertEqual(circ.num_qubits, num_qubits) op_counts = circ.count_ops() self.assertEqual(op_counts.get("rx"), 2) - self.assertEqual(op_counts.get("delay"), 2) + self.assertEqual(op_counts.get("delay"), 3) def test_experiment_config(self): """Test converting to and from config works"""