def add_delay_to_dag(self, duration, dag, qargs, cargs): if self.delay_quantum: number_of_delays = int(duration / self.delay_quantum) for ii in range(number_of_delays): dag.apply_operation_back(Delay(self.delay_quantum), qargs, cargs) else: dag.apply_operation_back(Delay(duration), qargs, cargs)
def test_add_delay_on_single_qubit_to_circuit(self): qc = QuantumCircuit(1) qc.h(0) qc.delay(100, 0) qc.delay(200, [0]) qc.delay(300, qc.qubits[0]) self.assertEqual(qc.data[1], (Delay(duration=100), qc.qubits, [])) self.assertEqual(qc.data[2], (Delay(duration=200), qc.qubits, [])) self.assertEqual(qc.data[3], (Delay(duration=300), qc.qubits, []))
def setUp(self) -> None: """Setup.""" super().setUp() self.qubit = list(qiskit.QuantumRegister(1))[0] self.u1 = types.ScheduledGate(t0=100, operand=library.U1Gate(0), duration=0, bits=[self.qubit], bit_position=0) self.u3 = types.ScheduledGate(t0=100, operand=library.U3Gate(0, 0, 0), duration=20, bits=[self.qubit], bit_position=0) self.delay = types.ScheduledGate(t0=100, operand=Delay(20), duration=20, bits=[self.qubit], bit_position=0) style = stylesheet.QiskitTimelineStyle() self.formatter = style.formatter
def pad_with_delays(qubits: List[int], until, unit) -> None: """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``.""" for q in qubits: if qubit_time_available[q] < until: idle_duration = until - qubit_time_available[q] new_dag.apply_operation_back(Delay(idle_duration, unit), [q])
def test_insert_dd_ghz(self): """Test DD gates are inserted in correct spots. ┌───┐ ┌────────────────┐ ┌───┐ » q_0: ──────┤ H ├─────────■──┤ Delay(100[dt]) ├──────┤ X ├──────» ┌─────┴───┴─────┐ ┌─┴─┐└────────────────┘┌─────┴───┴─────┐» q_1: ┤ Delay(50[dt]) ├─┤ X ├────────■─────────┤ Delay(50[dt]) ├» ├───────────────┴┐└───┘ ┌─┴─┐ └───────────────┘» q_2: ┤ Delay(750[dt]) ├───────────┤ X ├───────────────■────────» ├────────────────┤ └───┘ ┌─┴─┐ » q_3: ┤ Delay(950[dt]) ├─────────────────────────────┤ X ├──────» └────────────────┘ └───┘ » « ┌────────────────┐ ┌───┐ ┌────────────────┐ «q_0: ┤ Delay(200[dt]) ├──────┤ X ├───────┤ Delay(100[dt]) ├───────────────── « └─────┬───┬──────┘┌─────┴───┴──────┐└─────┬───┬──────┘┌───────────────┐ «q_1: ──────┤ X ├───────┤ Delay(100[dt]) ├──────┤ X ├───────┤ Delay(50[dt]) ├ « └───┘ └────────────────┘ └───┘ └───────────────┘ «q_2: ─────────────────────────────────────────────────────────────────────── « «q_3: ─────────────────────────────────────────────────────────────────────── « """ dd_sequence = [XGate(), XGate()] pm = PassManager([ ALAPScheduleAnalysis(self.durations), PadDynamicalDecoupling(self.durations, dd_sequence), ]) ghz4_dd = pm.run(self.ghz4) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(750), [2], front=True) expected = expected.compose(Delay(950), [3], front=True) expected = expected.compose(Delay(100), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(200), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(100), [0]) expected = expected.compose(Delay(50), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(100), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(50), [1]) self.assertEqual(ghz4_dd, expected)
def test_insert_dd_ghz_one_qubit(self): """Test DD gates are inserted on only one qubit. ┌───┐ ┌────────────────┐ ┌───┐ » q_0: ──────┤ H ├─────────■──┤ Delay(100[dt]) ├──────┤ X ├───────» ┌─────┴───┴─────┐ ┌─┴─┐└────────────────┘┌─────┴───┴──────┐» q_1: ┤ Delay(50[dt]) ├─┤ X ├────────■─────────┤ Delay(300[dt]) ├» ├───────────────┴┐└───┘ ┌─┴─┐ └────────────────┘» q_2: ┤ Delay(750[dt]) ├───────────┤ X ├───────────────■─────────» ├────────────────┤ └───┘ ┌─┴─┐ » q_3: ┤ Delay(950[dt]) ├─────────────────────────────┤ X ├───────» └────────────────┘ └───┘ » meas: 4/═══════════════════════════════════════════════════════════» » « ┌────────────────┐┌───┐┌────────────────┐ ░ ┌─┐ « q_0: ┤ Delay(200[dt]) ├┤ X ├┤ Delay(100[dt]) ├─░─┤M├───────── « └────────────────┘└───┘└────────────────┘ ░ └╥┘┌─┐ « q_1: ──────────────────────────────────────────░──╫─┤M├────── « ░ ║ └╥┘┌─┐ « q_2: ──────────────────────────────────────────░──╫──╫─┤M├─── « ░ ║ ║ └╥┘┌─┐ « q_3: ──────────────────────────────────────────░──╫──╫──╫─┤M├ « ░ ║ ║ ║ └╥┘ «meas: 4/═════════════════════════════════════════════╩══╩══╩══╩═ « 0 1 2 3 """ dd_sequence = [XGate(), XGate()] pm = PassManager( [ ALAPSchedule(self.durations), DynamicalDecoupling(self.durations, dd_sequence, qubits=[0]), ] ) ghz4_dd = pm.run(self.ghz4.measure_all(inplace=False)) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(750), [2], front=True) expected = expected.compose(Delay(950), [3], front=True) expected = expected.compose(Delay(100), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(200), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(100), [0]) expected = expected.compose(Delay(300), [1]) expected.measure_all() self.assertEqual(ghz4_dd, expected)
def test_interleaving_delay(self): """Test delay instruction can be interleaved.""" # See qiskit-experiments/#727 for details interleaved_element = Delay(10, unit="us") exp = rb.InterleavedRB( interleaved_element, qubits=[0], lengths=[1], num_samples=1, ) # Not raises an error _, int_circ = exp.circuits() # barrier, clifford, barrier, "delay", barrier, ... self.assertEqual(int_circ.data[3][0], interleaved_element)
def _deprecate_id_instruction( self, circuits: Union[QasmQobj, PulseQobj, QuantumCircuit, Schedule, List[Union[QuantumCircuit, Schedule]]] ) -> None: """Raise a DeprecationWarning if any circuit contains an 'id' instruction. Additionally, if 'delay' is a 'supported_instruction', replace each 'id' instruction (in-place) with the equivalent ('sx'-length) 'delay' instruction. Args: circuits: The individual or list of :class:`~qiskit.circuits.QuantumCircuit` or :class:`~qiskit.pulse.Schedule` objects passed to :meth:`IBMQBackend.run()<IBMQBackend.run>`. Modified in-place. Returns: None """ if isinstance(circuits, PulseQobj): return id_support = 'id' in getattr(self.configuration(), 'basis_gates', []) delay_support = 'delay' in getattr(self.configuration(), 'supported_instructions', []) if not delay_support: return if isinstance(circuits, QasmQobj): circuit_has_id = any(instr.name == 'id' for experiment in circuits.experiments for instr in experiment.instructions) else: if not isinstance(circuits, List): circuits = [circuits] circuit_has_id = any(instr.name == 'id' for circuit in circuits if isinstance(circuit, QuantumCircuit) for instr, qargs, cargs in circuit.data) if not circuit_has_id: return if not self.id_warning_issued: if id_support and delay_support: warnings.warn( "Support for the 'id' instruction has been deprecated " "from IBM hardware backends. Any 'id' instructions " "will be replaced with their equivalent 'delay' instruction. " "Please use the 'delay' instruction instead.", DeprecationWarning, stacklevel=4) else: warnings.warn( "Support for the 'id' instruction has been removed " "from IBM hardware backends. Any 'id' instructions " "will be replaced with their equivalent 'delay' instruction. " "Please use the 'delay' instruction instead.", DeprecationWarning, stacklevel=4) self.id_warning_issued = True dt_in_s = self.configuration().dt if isinstance(circuits, QasmQobj): for experiment in circuits.experiments: for instr in experiment.instructions: if instr.name == 'id': sx_duration = self.properties().gate_length( 'sx', instr.qubits[0]) sx_duration_in_dt = duration_in_dt( sx_duration, dt_in_s) instr.name = 'delay' instr.params = [sx_duration_in_dt] else: for circuit in circuits: if isinstance(circuit, Schedule): continue for idx, (instr, qargs, cargs) in enumerate(circuit.data): if instr.name == 'id': sx_duration = self.properties().gate_length( 'sx', qargs[0].index) sx_duration_in_dt = duration_in_dt( sx_duration, dt_in_s) delay_instr = Delay(sx_duration_in_dt) circuit.data[idx] = (delay_instr, qargs, cargs)
def run(self, dag): """Run the ASAPSchedule pass on `dag`. Args: dag (DAGCircuit): DAG to schedule. Returns: DAGCircuit: A scheduled DAG. Raises: TranspilerError: if the circuit is not mapped on physical qubits. TranspilerError: if conditional bit is added to non-supported instruction. """ if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None: raise TranspilerError( "ASAP schedule runs on physical circuits only") time_unit = self.property_set["time_unit"] new_dag = DAGCircuit() for qreg in dag.qregs.values(): new_dag.add_qreg(qreg) for creg in dag.cregs.values(): new_dag.add_creg(creg) idle_after = {q: 0 for q in dag.qubits + dag.clbits} bit_indices = {q: index for index, q in enumerate(dag.qubits)} for node in dag.topological_op_nodes(): op_duration = self._get_node_duration(node, bit_indices, dag) # compute t0, t1: instruction interval, note that # t0: start time of instruction # t1: end time of instruction if isinstance(node.op, self.CONDITIONAL_SUPPORTED): t0q = max(idle_after[q] for q in node.qargs) if node.op.condition_bits: # conditional is bit tricky due to conditional_latency t0c = max(idle_after[bit] for bit in node.op.condition_bits) if t0q > t0c: # This is situation something like below # # |t0q # Q ▒▒▒▒▒▒▒▒▒░░ # C ▒▒▒░░░░░░░░ # |t0c # # In this case, you can insert readout access before tq0 # # |t0q # Q ▒▒▒▒▒▒▒▒▒▒▒ # C ▒▒▒░░░▒▒░░░ # |t0q - conditional_latency # t0c = max(t0q - self.conditional_latency, t0c) t1c = t0c + self.conditional_latency for bit in node.op.condition_bits: # Lock clbit until state is read idle_after[bit] = t1c # It starts after register read access t0 = max(t0q, t1c) else: t0 = t0q t1 = t0 + op_duration else: if node.op.condition_bits: raise TranspilerError( f"Conditional instruction {node.op.name} is not supported in ASAP scheduler." ) if isinstance(node.op, Measure): # measure instruction handling is bit tricky due to clbit_write_latency t0q = max(idle_after[q] for q in node.qargs) t0c = max(idle_after[c] for c in node.cargs) # Assume following case (t0c > t0q) # # |t0q # Q ▒▒▒▒░░░░░░░░░░░░ # C ▒▒▒▒▒▒▒▒░░░░░░░░ # |t0c # # In this case, there is no actual clbit access until clbit_write_latency. # The node t0 can be push backward by this amount. # # |t0q' = t0c - clbit_write_latency # Q ▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒ # C ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ # |t0c' = t0c # # rather than naively doing # # |t0q' = t0c # Q ▒▒▒▒░░░░▒▒▒▒▒▒▒▒ # C ▒▒▒▒▒▒▒▒░░░▒▒▒▒▒ # |t0c' = t0c + clbit_write_latency # t0 = max(t0q, t0c - self.clbit_write_latency) t1 = t0 + op_duration for clbit in node.cargs: idle_after[clbit] = t1 else: # It happens to be directives such as barrier t0 = max(idle_after[bit] for bit in node.qargs + node.cargs) t1 = t0 + op_duration # Add delay to qubit wire for bit in node.qargs: delta = t0 - idle_after[bit] if delta > 0 and isinstance(bit, Qubit): new_dag.apply_operation_back(Delay(delta, time_unit), [bit], []) idle_after[bit] = t1 new_dag.apply_operation_back(node.op, node.qargs, node.cargs) circuit_duration = max(idle_after.values()) for bit, after in idle_after.items(): delta = circuit_duration - after if not (delta > 0 and isinstance(bit, Qubit)): continue new_dag.apply_operation_back(Delay(delta, time_unit), [bit], []) new_dag.name = dag.name new_dag.metadata = dag.metadata new_dag.calibrations = dag.calibrations # set circuit duration and unit to indicate it is scheduled new_dag.duration = circuit_duration new_dag.unit = time_unit return new_dag
def _deprecate_id_instruction( self, circuits: Union[QuantumCircuit, Schedule, List[Union[QuantumCircuit, Schedule]]], ) -> Union[QuantumCircuit, Schedule, List[Union[QuantumCircuit, Schedule]]]: """Raise a DeprecationWarning if any circuit contains an 'id' instruction. Additionally, if 'delay' is a 'supported_instruction', replace each 'id' instruction (in-place) with the equivalent ('sx'-length) 'delay' instruction. Args: circuits: The individual or list of :class:`~qiskit.circuits.QuantumCircuit` or :class:`~qiskit.pulse.Schedule` objects passed to :meth:`IBMBackend.run()<IBMBackend.run>`. Modified in-place. Returns: A modified copy of the original circuit where 'id' instructions are replaced with 'delay' instructions. A copy is used so the original circuit is not modified. If there are no 'id' instructions or 'delay' is not supported, return the original circuit. """ id_support = "id" in getattr(self.configuration(), "basis_gates", []) delay_support = "delay" in getattr(self.configuration(), "supported_instructions", []) if not delay_support: return circuits if not isinstance(circuits, List): circuits = [circuits] circuit_has_id = any(instr.name == "id" for circuit in circuits if isinstance(circuit, QuantumCircuit) for instr, qargs, cargs in circuit.data) if not circuit_has_id: return circuits if not self.id_warning_issued: if id_support and delay_support: warnings.warn( "Support for the 'id' instruction has been deprecated " "from IBM hardware backends. Any 'id' instructions " "will be replaced with their equivalent 'delay' instruction. " "Please use the 'delay' instruction instead.", DeprecationWarning, stacklevel=4, ) else: warnings.warn( "Support for the 'id' instruction has been removed " "from IBM hardware backends. Any 'id' instructions " "will be replaced with their equivalent 'delay' instruction. " "Please use the 'delay' instruction instead.", DeprecationWarning, stacklevel=4, ) self.id_warning_issued = True dt_in_s = self.configuration().dt circuits_copy = copy.deepcopy(circuits) for circuit in circuits_copy: if isinstance(circuit, Schedule): continue for idx, (instr, qargs, cargs) in enumerate(circuit.data): if instr.name == "id": sx_duration = self.properties().gate_length( "sx", qargs[0].index) sx_duration_in_dt = duration_in_dt(sx_duration, dt_in_s) delay_instr = Delay(sx_duration_in_dt) circuit.data[idx] = (delay_instr, qargs, cargs) return circuits_copy
def test_insert_dd_ghz_xy4_with_alignment(self): """Test DD with pulse alignment constraints. ┌───┐ ┌───────────────┐ ┌───┐ ┌───────────────┐» q_0: ──────┤ H ├─────────■──┤ Delay(40[dt]) ├──────┤ X ├──────┤ Delay(70[dt]) ├» ┌─────┴───┴─────┐ ┌─┴─┐└───────────────┘┌─────┴───┴─────┐└─────┬───┬─────┘» q_1: ┤ Delay(50[dt]) ├─┤ X ├────────■────────┤ Delay(20[dt]) ├──────┤ X ├──────» ├───────────────┴┐└───┘ ┌─┴─┐ └───────────────┘ └───┘ » q_2: ┤ Delay(750[dt]) ├───────────┤ X ├──────────────■─────────────────────────» ├────────────────┤ └───┘ ┌─┴─┐ » q_3: ┤ Delay(950[dt]) ├────────────────────────────┤ X ├───────────────────────» └────────────────┘ └───┘ » « ┌───┐ ┌───────────────┐ ┌───┐ ┌───────────────┐» «q_0: ──────┤ Y ├──────┤ Delay(70[dt]) ├──────┤ X ├──────┤ Delay(70[dt]) ├» « ┌─────┴───┴─────┐└─────┬───┬─────┘┌─────┴───┴─────┐└─────┬───┬─────┘» «q_1: ┤ Delay(20[dt]) ├──────┤ Y ├──────┤ Delay(20[dt]) ├──────┤ X ├──────» « └───────────────┘ └───┘ └───────────────┘ └───┘ » «q_2: ────────────────────────────────────────────────────────────────────» « » «q_3: ────────────────────────────────────────────────────────────────────» « » « ┌───┐ ┌───────────────┐ «q_0: ──────┤ Y ├──────┤ Delay(50[dt]) ├───────────────── « ┌─────┴───┴─────┐└─────┬───┬─────┘┌───────────────┐ «q_1: ┤ Delay(20[dt]) ├──────┤ Y ├──────┤ Delay(20[dt]) ├ « └───────────────┘ └───┘ └───────────────┘ «q_2: ─────────────────────────────────────────────────── « «q_3: ─────────────────────────────────────────────────── « """ dd_sequence = [XGate(), YGate(), XGate(), YGate()] pm = PassManager([ ALAPScheduleAnalysis(self.durations), PadDynamicalDecoupling( self.durations, dd_sequence, pulse_alignment=10, extra_slack_distribution="edges", ), ]) ghz4_dd = pm.run(self.ghz4) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(750), [2], front=True) expected = expected.compose(Delay(950), [3], front=True) expected = expected.compose(Delay(40), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(70), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(70), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(70), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(50), [0]) expected = expected.compose(Delay(20), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(20), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(20), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(20), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(20), [1]) self.assertEqual(ghz4_dd, expected)
def test_insert_ghz_uhrig(self): """Test custom spacing (following Uhrig DD [1]). [1] Uhrig, G. "Keeping a quantum bit alive by optimized π-pulse sequences." Physical Review Letters 98.10 (2007): 100504. ┌───┐ ┌──────────────┐ ┌───┐ ┌──────────────┐┌───┐» q_0: ──────┤ H ├─────────■──┤ Delay(3[dt]) ├──────┤ X ├───────┤ Delay(8[dt]) ├┤ X ├» ┌─────┴───┴─────┐ ┌─┴─┐└──────────────┘┌─────┴───┴──────┐└──────────────┘└───┘» q_1: ┤ Delay(50[dt]) ├─┤ X ├───────■────────┤ Delay(300[dt]) ├─────────────────────» ├───────────────┴┐└───┘ ┌─┴─┐ └────────────────┘ » q_2: ┤ Delay(750[dt]) ├──────────┤ X ├──────────────■──────────────────────────────» ├────────────────┤ └───┘ ┌─┴─┐ » q_3: ┤ Delay(950[dt]) ├───────────────────────────┤ X ├────────────────────────────» └────────────────┘ └───┘ » « ┌───────────────┐┌───┐┌───────────────┐┌───┐┌───────────────┐┌───┐┌───────────────┐» «q_0: ┤ Delay(13[dt]) ├┤ X ├┤ Delay(16[dt]) ├┤ X ├┤ Delay(20[dt]) ├┤ X ├┤ Delay(16[dt]) ├» « └───────────────┘└───┘└───────────────┘└───┘└───────────────┘└───┘└───────────────┘» «q_1: ───────────────────────────────────────────────────────────────────────────────────» « » «q_2: ───────────────────────────────────────────────────────────────────────────────────» « » «q_3: ───────────────────────────────────────────────────────────────────────────────────» « » « ┌───┐┌───────────────┐┌───┐┌──────────────┐┌───┐┌──────────────┐ «q_0: ┤ X ├┤ Delay(13[dt]) ├┤ X ├┤ Delay(8[dt]) ├┤ X ├┤ Delay(3[dt]) ├ « └───┘└───────────────┘└───┘└──────────────┘└───┘└──────────────┘ «q_1: ──────────────────────────────────────────────────────────────── « «q_2: ──────────────────────────────────────────────────────────────── « «q_3: ──────────────────────────────────────────────────────────────── « """ n = 8 dd_sequence = [XGate()] * n # uhrig specifies the location of the k'th pulse def uhrig(k): return np.sin(np.pi * (k + 1) / (2 * n + 2))**2 # convert that to spacing between pulses (whatever finite duration pulses have) spacing = [] for k in range(n): spacing.append(uhrig(k) - sum(spacing)) spacing.append(1 - sum(spacing)) pm = PassManager([ ALAPScheduleAnalysis(self.durations), PadDynamicalDecoupling(self.durations, dd_sequence, qubits=[0], spacing=spacing), ]) ghz4_dd = pm.run(self.ghz4) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(750), [2], front=True) expected = expected.compose(Delay(950), [3], front=True) expected = expected.compose(Delay(3), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(8), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(13), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(16), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(20), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(16), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(13), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(8), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(3), [0]) expected = expected.compose(Delay(300), [1]) self.assertEqual(ghz4_dd, expected)
def test_insert_dd_ghz_xy4(self): """Test XY4 sequence of DD gates. ┌───┐ ┌───────────────┐ ┌───┐ ┌───────────────┐» q_0: ──────┤ H ├─────────■──┤ Delay(37[dt]) ├──────┤ X ├──────┤ Delay(75[dt]) ├» ┌─────┴───┴─────┐ ┌─┴─┐└───────────────┘┌─────┴───┴─────┐└─────┬───┬─────┘» q_1: ┤ Delay(50[dt]) ├─┤ X ├────────■────────┤ Delay(12[dt]) ├──────┤ X ├──────» ├───────────────┴┐└───┘ ┌─┴─┐ └───────────────┘ └───┘ » q_2: ┤ Delay(750[dt]) ├───────────┤ X ├──────────────■─────────────────────────» ├────────────────┤ └───┘ ┌─┴─┐ » q_3: ┤ Delay(950[dt]) ├────────────────────────────┤ X ├───────────────────────» └────────────────┘ └───┘ » « ┌───┐ ┌───────────────┐ ┌───┐ ┌───────────────┐» «q_0: ──────┤ Y ├──────┤ Delay(76[dt]) ├──────┤ X ├──────┤ Delay(75[dt]) ├» « ┌─────┴───┴─────┐└─────┬───┬─────┘┌─────┴───┴─────┐└─────┬───┬─────┘» «q_1: ┤ Delay(25[dt]) ├──────┤ Y ├──────┤ Delay(26[dt]) ├──────┤ X ├──────» « └───────────────┘ └───┘ └───────────────┘ └───┘ » «q_2: ────────────────────────────────────────────────────────────────────» « » «q_3: ────────────────────────────────────────────────────────────────────» « » « ┌───┐ ┌───────────────┐ «q_0: ──────┤ Y ├──────┤ Delay(37[dt]) ├───────────────── « ┌─────┴───┴─────┐└─────┬───┬─────┘┌───────────────┐ «q_1: ┤ Delay(25[dt]) ├──────┤ Y ├──────┤ Delay(12[dt]) ├ « └───────────────┘ └───┘ └───────────────┘ «q_2: ─────────────────────────────────────────────────── « «q_3: ─────────────────────────────────────────────────── """ dd_sequence = [XGate(), YGate(), XGate(), YGate()] pm = PassManager([ ALAPScheduleAnalysis(self.durations), PadDynamicalDecoupling(self.durations, dd_sequence), ]) ghz4_dd = pm.run(self.ghz4) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(750), [2], front=True) expected = expected.compose(Delay(950), [3], front=True) expected = expected.compose(Delay(37), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(75), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(76), [0]) expected = expected.compose(XGate(), [0]) expected = expected.compose(Delay(75), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(37), [0]) expected = expected.compose(Delay(12), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(25), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(26), [1]) expected = expected.compose(XGate(), [1]) expected = expected.compose(Delay(25), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(12), [1]) self.assertEqual(ghz4_dd, expected)
def test_insert_dd_ghz_everywhere(self): """Test DD gates even on initial idle spots. ┌───┐ ┌────────────────┐┌───┐┌────────────────┐┌───┐» q_0: ──────┤ H ├─────────■──┤ Delay(100[dt]) ├┤ Y ├┤ Delay(200[dt]) ├┤ Y ├» ┌─────┴───┴─────┐ ┌─┴─┐└────────────────┘└───┘└────────────────┘└───┘» q_1: ┤ Delay(50[dt]) ├─┤ X ├───────────────────────────────────────────■──» ├───────────────┴┐├───┤┌────────────────┐┌───┐┌────────────────┐┌─┴─┐» q_2: ┤ Delay(162[dt]) ├┤ Y ├┤ Delay(326[dt]) ├┤ Y ├┤ Delay(162[dt]) ├┤ X ├» ├────────────────┤├───┤├────────────────┤├───┤├────────────────┤└───┘» q_3: ┤ Delay(212[dt]) ├┤ Y ├┤ Delay(426[dt]) ├┤ Y ├┤ Delay(212[dt]) ├─────» └────────────────┘└───┘└────────────────┘└───┘└────────────────┘ » « ┌────────────────┐ «q_0: ┤ Delay(100[dt]) ├───────────────────────────────────────────── « ├───────────────┬┘┌───┐┌────────────────┐┌───┐┌───────────────┐ «q_1: ┤ Delay(50[dt]) ├─┤ Y ├┤ Delay(100[dt]) ├┤ Y ├┤ Delay(50[dt]) ├ « └───────────────┘ └───┘└────────────────┘└───┘└───────────────┘ «q_2: ────────■────────────────────────────────────────────────────── « ┌─┴─┐ «q_3: ──────┤ X ├──────────────────────────────────────────────────── « └───┘ """ dd_sequence = [YGate(), YGate()] pm = PassManager([ ALAPScheduleAnalysis(self.durations), PadDynamicalDecoupling(self.durations, dd_sequence, skip_reset_qubits=False), ]) ghz4_dd = pm.run(self.ghz4) expected = self.ghz4.copy() expected = expected.compose(Delay(50), [1], front=True) expected = expected.compose(Delay(162), [2], front=True) expected = expected.compose(YGate(), [2], front=True) expected = expected.compose(Delay(326), [2], front=True) expected = expected.compose(YGate(), [2], front=True) expected = expected.compose(Delay(162), [2], front=True) expected = expected.compose(Delay(212), [3], front=True) expected = expected.compose(YGate(), [3], front=True) expected = expected.compose(Delay(426), [3], front=True) expected = expected.compose(YGate(), [3], front=True) expected = expected.compose(Delay(212), [3], front=True) expected = expected.compose(Delay(100), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(200), [0]) expected = expected.compose(YGate(), [0]) expected = expected.compose(Delay(100), [0]) expected = expected.compose(Delay(50), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(100), [1]) expected = expected.compose(YGate(), [1]) expected = expected.compose(Delay(50), [1]) self.assertEqual(ghz4_dd, expected)
def test_can_get_unbounded_duration_without_unit_conversion(self): param = Parameter("t") parameterized_delay = Delay(param, "dt") actual = InstructionDurations().get(parameterized_delay, 0) self.assertEqual(actual, param)
def test_to_matrix_return_identity_matrix(self): actual = Delay(100).to_matrix() expected = np.array([[1, 0], [0, 1]], dtype=complex) self.assertTrue(np.array_equal(actual, expected))
def run(self, dag): """Run the ALAPSchedule pass on `dag`. Args: dag (DAGCircuit): DAG to schedule. Returns: DAGCircuit: A scheduled DAG. Raises: TranspilerError: if the circuit is not mapped on physical qubits. TranspilerError: if conditional bit is added to non-supported instruction. """ if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None: raise TranspilerError( "ALAP schedule runs on physical circuits only") time_unit = self.property_set["time_unit"] new_dag = DAGCircuit() for qreg in dag.qregs.values(): new_dag.add_qreg(qreg) for creg in dag.cregs.values(): new_dag.add_creg(creg) idle_before = {q: 0 for q in dag.qubits + dag.clbits} bit_indices = {bit: index for index, bit in enumerate(dag.qubits)} for node in reversed(list(dag.topological_op_nodes())): op_duration = self._get_node_duration(node, bit_indices, dag) # compute t0, t1: instruction interval, note that # t0: start time of instruction # t1: end time of instruction # since this is alap scheduling, node is scheduled in reversed topological ordering # and nodes are packed from the very end of the circuit. # the physical meaning of t0 and t1 is flipped here. if isinstance(node.op, self.CONDITIONAL_SUPPORTED): t0q = max(idle_before[q] for q in node.qargs) if node.op.condition_bits: # conditional is bit tricky due to conditional_latency t0c = max(idle_before[c] for c in node.op.condition_bits) # Assume following case (t0c > t0q): # # |t0q # Q ░░░░░░░░░░░░░▒▒▒ # C ░░░░░░░░▒▒▒▒▒▒▒▒ # |t0c # # In this case, there is no actual clbit read before gate. # # |t0q' = t0c - conditional_latency # Q ░░░░░░░░▒▒▒░░▒▒▒ # C ░░░░░░▒▒▒▒▒▒▒▒▒▒ # |t1c' = t0c + conditional_latency # # rather than naively doing # # |t1q' = t0c + duration # Q ░░░░░▒▒▒░░░░░▒▒▒ # C ░░▒▒░░░░▒▒▒▒▒▒▒▒ # |t1c' = t0c + duration + conditional_latency # t0 = max(t0q, t0c - op_duration) t1 = t0 + op_duration for clbit in node.op.condition_bits: idle_before[clbit] = t1 + self.conditional_latency else: t0 = t0q t1 = t0 + op_duration else: if node.op.condition_bits: raise TranspilerError( f"Conditional instruction {node.op.name} is not supported in ALAP scheduler." ) if isinstance(node.op, Measure): # clbit time is always right (alap) justified t0 = max(idle_before[bit] for bit in node.qargs + node.cargs) t1 = t0 + op_duration # # |t1 = t0 + duration # Q ░░░░░▒▒▒▒▒▒▒▒▒▒▒ # C ░░░░░░░░░▒▒▒▒▒▒▒ # |t0 + (duration - clbit_write_latency) # for clbit in node.cargs: idle_before[clbit] = t0 + (op_duration - self.clbit_write_latency) else: # It happens to be directives such as barrier t0 = max(idle_before[bit] for bit in node.qargs + node.cargs) t1 = t0 + op_duration for bit in node.qargs: delta = t0 - idle_before[bit] if delta > 0: new_dag.apply_operation_front(Delay(delta, time_unit), [bit], []) idle_before[bit] = t1 new_dag.apply_operation_front(node.op, node.qargs, node.cargs) circuit_duration = max(idle_before.values()) for bit, before in idle_before.items(): delta = circuit_duration - before if not (delta > 0 and isinstance(bit, Qubit)): continue new_dag.apply_operation_front(Delay(delta, time_unit), [bit], []) new_dag.name = dag.name new_dag.metadata = dag.metadata new_dag.calibrations = dag.calibrations # set circuit duration and unit to indicate it is scheduled new_dag.duration = circuit_duration new_dag.unit = time_unit return new_dag
def test_can_append_parameterized_delay(self): dur = Parameter("t") qc = QuantumCircuit(1) qc.delay(dur) self.assertEqual(qc.data[0].operation, Delay(dur))
def test_can_accept_parameterized_duration(self): dur = Parameter("t") self.assertEqual(Delay(dur).duration, dur)
def test_fail_if_get_unbounded_duration_with_unit_conversion_when_dt_is_not_provided( self): param = Parameter("t") parameterized_delay = Delay(param, "s") with self.assertRaises(TranspilerError): InstructionDurations().get(parameterized_delay, 0)
def test_can_get_unbounded_duration_with_unit_conversion_when_dt_is_provided( self): param = Parameter("t") parameterized_delay = Delay(param, "s") actual = InstructionDurations(dt=100).get(parameterized_delay, 0) self.assertEqual(actual, param / 100)