diff --git a/qiskit/transpiler/passes/basis/unroller.py b/qiskit/transpiler/passes/basis/unroller.py index 5027e99bd550..806b61d752c6 100644 --- a/qiskit/transpiler/passes/basis/unroller.py +++ b/qiskit/transpiler/passes/basis/unroller.py @@ -26,15 +26,19 @@ class Unroller(TransformationPass): to a desired basis, using decomposition rules defined for each instruction. """ - def __init__(self, basis): + def __init__(self, basis=None, target=None): """Unroller initializer. Args: basis (list[str] or None): Target basis names to unroll to, e.g. `['u3', 'cx']` . If None, does not unroll any gate. + target (Target): The :class:`~.Target` representing the target backend, if both + ``basis`` and this are specified then this argument will take + precedence and ``basis`` will be ignored. """ super().__init__() self.basis = basis + self.target = target def run(self, dag): """Run the Unroller pass on `dag`. @@ -49,26 +53,42 @@ def run(self, dag): Returns: DAGCircuit: output unrolled dag """ - if self.basis is None: + if self.basis is None and self.target is None: return dag + qubit_mapping = {} + if self.target is not None: + qubit_mapping = {bit: index for index, bit in enumerate(dag.qubits)} # Walk through the DAG and expand each non-basis node basic_insts = ["measure", "reset", "barrier", "snapshot", "delay"] for node in dag.op_nodes(): if getattr(node.op, "_directive", False): continue - if node.name in basic_insts: - # TODO: this is legacy behavior.Basis_insts should be removed that these - # instructions should be part of the device-reported basis. Currently, no - # backend reports "measure", for example. - continue - - if node.name in self.basis: # If already a base, ignore. - if isinstance(node.op, ControlledGate) and node.op._open_ctrl: - pass - else: + run_qubits = None + if self.target is not None: + run_qubits = tuple(qubit_mapping[x] for x in node.qargs) + if ( + self.target.instruction_supported(node.op.name, qargs=run_qubits) + or node.op.name == "barrier" + ): + print("blue") + if isinstance(node.op, ControlledGate) and node.op._open_ctrl: + pass + else: + continue + else: + if node.name in basic_insts: + # TODO: this is legacy behavior.Basis_insts should be removed that these + # instructions should be part of the device-reported basis. Currently, no + # backend reports "measure", for example. continue + if node.name in self.basis: # If already a base, ignore. + if isinstance(node.op, ControlledGate) and node.op._open_ctrl: + pass + else: + continue + if isinstance(node.op, ControlFlowOp): node.op = control_flow.map_blocks(self.run, node.op) continue @@ -87,10 +107,16 @@ def run(self, dag): # to substitute_node_with_dag if an the width of the definition is # different that the width of the node. while rule and len(rule) == 1 and len(node.qargs) == len(rule[0].qubits) == 1: - if rule[0].operation.name in self.basis: - dag.global_phase += phase - dag.substitute_node(node, rule[0].operation, inplace=True) - break + if self.target is not None: + if self.target.instruction_supported(rule[0].operation.name, run_qubits): + dag.global_phase += phase + dag.substitute_node(node, rule[0].operation, inplace=True) + break + else: + if rule[0].operation.name in self.basis: + dag.global_phase += phase + dag.substitute_node(node, rule[0].operation, inplace=True) + break try: phase += rule[0].operation.definition.global_phase rule = rule[0].operation.definition.data diff --git a/qiskit/transpiler/passes/calibration/pulse_gate.py b/qiskit/transpiler/passes/calibration/pulse_gate.py index ebcbfb93ddb4..5ed960253b62 100644 --- a/qiskit/transpiler/passes/calibration/pulse_gate.py +++ b/qiskit/transpiler/passes/calibration/pulse_gate.py @@ -20,6 +20,7 @@ ScheduleBlock, ) from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap +from qiskit.transpiler.target import Target from .base_builder import CalibrationBuilder @@ -50,15 +51,21 @@ class PulseGates(CalibrationBuilder): def __init__( self, - inst_map: InstructionScheduleMap, + inst_map: InstructionScheduleMap = None, + target: Target = None, ): """Create new pass. Args: inst_map: Instruction schedule map that user may override. + target: The :class:`~.Target` representing the target backend, if both + ``inst_map`` and this are specified then this argument will take + precedence and ``inst_map`` will be ignored. """ super().__init__() self.inst_map = inst_map + if target: + self.inst_map = target.instruction_schedule_map() def supported(self, node_op: CircuitInst, qubits: List) -> bool: """Determine if a given node supports the calibration. diff --git a/qiskit/transpiler/passes/calibration/rzx_builder.py b/qiskit/transpiler/passes/calibration/rzx_builder.py index 8b1312a37d0c..5efcf62ea4fa 100644 --- a/qiskit/transpiler/passes/calibration/rzx_builder.py +++ b/qiskit/transpiler/passes/calibration/rzx_builder.py @@ -33,6 +33,7 @@ from qiskit.pulse import builder from qiskit.pulse.filters import filter_instructions from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap +from qiskit.transpiler.target import Target from .base_builder import CalibrationBuilder from .exceptions import CalibrationNotAvailable @@ -63,6 +64,7 @@ def __init__( instruction_schedule_map: InstructionScheduleMap = None, qubit_channel_mapping: List[List[str]] = None, verbose: bool = True, + target: Target = None, ): """ Initializes a RZXGate calibration builder. @@ -73,6 +75,9 @@ def __init__( qubit_channel_mapping: The list mapping qubit indices to the list of channel names that apply on that qubit. verbose: Set True to raise a user warning when RZX schedule cannot be built. + target: The :class:`~.Target` representing the target backend, if both + ``instruction_schedule_map`` and this are specified then this argument will take + precedence and ``instruction_schedule_map`` will be ignored. Raises: QiskitError: Instruction schedule map is not provided. @@ -90,6 +95,8 @@ def __init__( self._inst_map = instruction_schedule_map self._verbose = verbose + if target: + self._inst_map = target.instruction_schedule_map() def supported(self, node_op: CircuitInst, qubits: List) -> bool: """Determine if a given node supports the calibration. diff --git a/qiskit/transpiler/passes/optimization/commutative_cancellation.py b/qiskit/transpiler/passes/optimization/commutative_cancellation.py index 7db4d37fc769..5469aef85f49 100644 --- a/qiskit/transpiler/passes/optimization/commutative_cancellation.py +++ b/qiskit/transpiler/passes/optimization/commutative_cancellation.py @@ -38,7 +38,7 @@ class CommutativeCancellation(TransformationPass): H, X, Y, Z, CX, CY, CZ """ - def __init__(self, basis_gates=None): + def __init__(self, basis_gates=None, target=None): """ CommutativeCancellation initializer. @@ -47,12 +47,17 @@ def __init__(self, basis_gates=None): ``['u3', 'cx']``. For the effects of this pass, the basis is the set intersection between the ``basis_gates`` parameter and the gates in the dag. + target (Target): The :class:`~.Target` representing the target backend, if both + ``basis_gates`` and this are specified then this argument will take + precedence and ``basis_gates`` will be ignored. """ super().__init__() if basis_gates: self.basis = set(basis_gates) else: self.basis = set() + if target is not None: + self.basis = set(target.operation_names) self._var_z_map = {"rz": RZGate, "p": PhaseGate, "u1": U1Gate} self.requires.append(CommutationAnalysis()) diff --git a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py index 844e5a6d431b..20ae08586858 100644 --- a/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py +++ b/qiskit/transpiler/passes/optimization/crosstalk_adaptive_schedule.py @@ -33,6 +33,7 @@ from itertools import chain, combinations from qiskit.transpiler.basepasses import TransformationPass +from qiskit.transpiler.target import target_to_backend_properties from qiskit.dagcircuit import DAGCircuit from qiskit.circuit.library.standard_gates import U1Gate, U2Gate, U3Gate, CXGate from qiskit.circuit import Measure @@ -49,7 +50,9 @@ class CrosstalkAdaptiveSchedule(TransformationPass): """Crosstalk mitigation through adaptive instruction scheduling.""" - def __init__(self, backend_prop, crosstalk_prop, weight_factor=0.5, measured_qubits=None): + def __init__( + self, backend_prop, crosstalk_prop, weight_factor=0.5, measured_qubits=None, target=None + ): """CrosstalkAdaptiveSchedule initializer. Args: @@ -85,6 +88,9 @@ def __init__(self, backend_prop, crosstalk_prop, weight_factor=0.5, measured_qub The arg is useful when a subsequent module such as state_tomography_circuits inserts the measure gates. If CrosstalkAdaptiveSchedule is made aware of those measurements, it is included in the optimization. + target (Target): A target representing the target backend, if both + ``backend_prop`` and this are specified then this argument will take + precedence and ``coupling_map`` will be ignored. Raises: ImportError: if unable to import z3 solver @@ -93,6 +99,8 @@ def __init__(self, backend_prop, crosstalk_prop, weight_factor=0.5, measured_qub super().__init__() self.backend_prop = backend_prop + if target is not None: + self.backend_prop = target_to_backend_properties(target) self.crosstalk_prop = crosstalk_prop self.weight_factor = weight_factor if measured_qubits is None: diff --git a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py index 5d55796df668..693a8a6ee809 100644 --- a/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py +++ b/qiskit/transpiler/passes/optimization/echo_rzx_weyl_decomposition.py @@ -33,15 +33,20 @@ class EchoRZXWeylDecomposition(TransformationPass): Each pair of RZXGates forms an echoed RZXGate. """ - def __init__(self, instruction_schedule_map): + def __init__(self, instruction_schedule_map=None, target=None): """EchoRZXWeylDecomposition pass. Args: instruction_schedule_map (InstructionScheduleMap): the mapping from circuit :class:`~.circuit.Instruction` names and arguments to :class:`.Schedule`\\ s. + target (Target): The :class:`~.Target` representing the target backend, if both + ``instruction_schedule_map`` and this are specified then this argument will take + precedence and ``instruction_schedule_map`` will be ignored. """ super().__init__() self._inst_map = instruction_schedule_map + if target is not None: + self._inst_map = target.instruction_schedule_map() def _is_native(self, qubit_pair: Tuple) -> bool: """Return the direction of the qubit pair that is native, i.e. with the shortest schedule.""" diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py index 7d226ca828b5..d01592d2e919 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py @@ -61,18 +61,20 @@ class Optimize1qGatesSimpleCommutation(TransformationPass): # NOTE: A run from `dag.collect_1q_runs` is always nonempty, so we sometimes use an empty list # to signify the absence of a run. - def __init__(self, basis=None, run_to_completion=False): + def __init__(self, basis=None, run_to_completion=False, target=None): """ Args: basis (List[str]): See also `Optimize1qGatesDecomposition`. run_to_completion (bool): If `True`, this pass retries until it is unable to do any more work. If `False`, it finds and performs one optimization, and for full optimization the user is obligated to re-call the pass until the output stabilizes. + target (Target): The :class:`~.Target` representing the target backend, if both + ``basis`` and this are specified then this argument will take + precedence and ``basis`` will be ignored. """ super().__init__() - self._basis = basis - self._optimize1q = Optimize1qGatesDecomposition(basis) + self._optimize1q = Optimize1qGatesDecomposition(basis=basis, target=target) self._run_to_completion = run_to_completion @staticmethod diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py index ed9fb425ce5a..1af789fa7361 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_decomposition.py @@ -70,7 +70,10 @@ def _resynthesize_run(self, run, qubit=None): operator = gate.op.to_matrix().dot(operator) if self._target: - qubits_tuple = (qubit,) + if qubit is not None: + qubits_tuple = (qubit,) + else: + qubits_tuple = None if qubits_tuple in self._local_decomposers_cache: decomposers = self._local_decomposers_cache[qubits_tuple] else: diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py index ad473f6983af..97a7fbf188e8 100644 --- a/qiskit/transpiler/passes/optimization/optimize_1q_gates.py +++ b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py @@ -34,7 +34,7 @@ class Optimize1qGates(TransformationPass): """Optimize chains of single-qubit u1, u2, u3 gates by combining them into a single gate.""" - def __init__(self, basis=None, eps=1e-15): + def __init__(self, basis=None, eps=1e-15, target=None): """Optimize1qGates initializer. Args: @@ -42,10 +42,14 @@ def __init__(self, basis=None, eps=1e-15): of this pass, the basis is the set intersection between the `basis` parameter and the set `{'u1','u2','u3', 'u', 'p'}`. eps (float): EPS to check against + target (Target): The :class:`~.Target` representing the target backend, if both + ``basis`` and this are specified then this argument will take + precedence and ``basis`` will be ignored. """ super().__init__() - self.basis = basis if basis else ["u1", "u2", "u3"] + self.basis = set(basis) if basis else {"u1", "u2", "u3"} self.eps = eps + self.target = target def run(self, dag): """Run the Optimize1qGates pass on `dag`. @@ -62,12 +66,24 @@ def run(self, dag): use_u = "u" in self.basis use_p = "p" in self.basis runs = dag.collect_runs(["u1", "u2", "u3", "u", "p"]) + qubit_mapping = {} + if self.target is not None: + qubit_mapping = {bit: index for index, bit in enumerate(dag.qubits)} runs = _split_runs_on_parameters(runs) for run in runs: - if use_p: - right_name = "p" + run_qubits = None + if self.target is not None: + run_qubits = tuple(qubit_mapping[x] for x in run[0].qargs) + + if self.target.instruction_supported("p", run_qubits): + right_name = "p" + else: + right_name = "u1" else: - right_name = "u1" + if use_p: + right_name = "p" + else: + right_name = "u1" right_parameters = (0, 0, 0) # (theta, phi, lambda) right_global_phase = 0 for current_node in run: @@ -256,16 +272,30 @@ def run(self, dag): ): right_name = "nop" - if right_name == "u2" and "u2" not in self.basis: - if use_u: - right_name = "u" - else: - right_name = "u3" - if right_name in ("u1", "p") and right_name not in self.basis: - if use_u: - right_name = "u" - else: - right_name = "u3" + if self.target is not None: + if right_name == "u2" and not self.target.instruction_supported("u2", run_qubits): + if self.target.instruction_supported("u", run_qubits): + right_name = "u" + else: + right_name = "u3" + if right_name in ("u1", "p") and not self.target.instruction_supported( + right_name, run_qubits + ): + if self.target.instruction_supported("u", run_qubits): + right_name = "u" + else: + right_name = "u3" + else: + if right_name == "u2" and "u2" not in self.basis: + if use_u: + right_name = "u" + else: + right_name = "u3" + if right_name in ("u1", "p") and right_name not in self.basis: + if use_u: + right_name = "u" + else: + right_name = "u3" new_op = Gate(name="", num_qubits=1, params=[]) if right_name == "u1": diff --git a/qiskit/transpiler/passes/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/base_scheduler.py index 29c88d21811a..bc96aacc2d57 100644 --- a/qiskit/transpiler/passes/scheduling/base_scheduler.py +++ b/qiskit/transpiler/passes/scheduling/base_scheduler.py @@ -20,6 +20,7 @@ from qiskit.circuit import Delay, Gate from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.target import Target class BaseSchedulerTransform(TransformationPass): @@ -217,9 +218,10 @@ class BaseSchedulerTransform(TransformationPass): def __init__( self, - durations: InstructionDurations, + durations: InstructionDurations = None, clbit_write_latency: int = 0, conditional_latency: int = 0, + target: Target = None, ): """Scheduler initializer. @@ -237,6 +239,9 @@ def __init__( The gate operation occurs after this latency. This appears as a delay in front of the DAGOpNode of the gate. This defaults to 0 dt. + target: The :class:`~.Target` representing the target backend, if both + ``durations`` and this are specified then this argument will take + precedence and ``durations`` will be ignored. """ super().__init__() self.durations = durations @@ -247,6 +252,8 @@ def __init__( # Ensure op node durations are attached and in consistent unit self.requires.append(TimeUnitConversion(durations)) + if target is not None: + self.durations = target.durations() @staticmethod def _get_node_duration( diff --git a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py index 7495528f4907..9dfeb8454161 100644 --- a/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/dynamical_decoupling.py @@ -91,7 +91,9 @@ def uhrig_pulse_location(k): timeline_drawer(circ_dd) """ - def __init__(self, durations, dd_sequence, qubits=None, spacing=None, skip_reset_qubits=True): + def __init__( + self, durations, dd_sequence, qubits=None, spacing=None, skip_reset_qubits=True, target=None + ): """Dynamical decoupling initializer. Args: @@ -108,6 +110,9 @@ def __init__(self, durations, dd_sequence, qubits=None, spacing=None, skip_reset skip_reset_qubits (bool): if True, does not insert DD on idle periods that immediately follow initialized/reset qubits (as qubits in the ground state are less susceptile to decoherence). + target (Target): The :class:`~.Target` representing the target backend, if both + ``durations`` and this are specified then this argument will take + precedence and ``durations`` will be ignored. """ warnings.warn( "The DynamicalDecoupling class has been supersceded by the " @@ -123,6 +128,8 @@ def __init__(self, durations, dd_sequence, qubits=None, spacing=None, skip_reset self._qubits = qubits self._spacing = spacing self._skip_reset_qubits = skip_reset_qubits + if target is not None: + self._durations = target.durations() def run(self, dag): """Run the DynamicalDecoupling pass on dag. diff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py index 7cde1c17f0ba..d8e4f13ea518 100644 --- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py @@ -25,6 +25,7 @@ from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.passes.optimization import Optimize1qGates +from qiskit.transpiler.target import Target from .base_padding import BasePadding @@ -101,13 +102,14 @@ def uhrig_pulse_location(k): def __init__( self, - durations: InstructionDurations, - dd_sequence: List[Gate], + durations: InstructionDurations = None, + dd_sequence: List[Gate] = None, qubits: Optional[List[int]] = None, spacing: Optional[List[float]] = None, skip_reset_qubits: bool = True, pulse_alignment: int = 1, extra_slack_distribution: str = "middle", + target: Target = None, ): """Dynamical decoupling initializer. @@ -140,14 +142,20 @@ def __init__( - "middle": Put the extra slack to the interval at the middle of the sequence. - "edges": Divide the extra slack as evenly as possible into intervals at beginning and end of the sequence. + target: The :class:`~.Target` representing the target backend, if both + ``durations`` and this are specified then this argument will take + precedence and ``durations`` will be ignored. Raises: TranspilerError: When invalid DD sequence is specified. TranspilerError: When pulse gate with the duration which is non-multiple of the alignment constraint value is found. + TypeError: If ``dd_sequence`` is not specified """ super().__init__() self._durations = durations + if dd_sequence is None: + raise TypeError("required argument 'dd_sequence' is not specified") self._dd_sequence = dd_sequence self._qubits = qubits self._skip_reset_qubits = skip_reset_qubits @@ -157,6 +165,8 @@ def __init__( self._dd_sequence_lengths = dict() self._sequence_phase = 0 + if target is not None: + self._durations = target.durations() def _pre_runhook(self, dag: DAGCircuit): super()._pre_runhook(dag) diff --git a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py index 8dacf45f1e83..a1552f91e1b8 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/base_scheduler.py @@ -22,6 +22,7 @@ from qiskit.circuit import Delay, Gate from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.target import Target class BaseScheduler(AnalysisPass): @@ -29,17 +30,23 @@ class BaseScheduler(AnalysisPass): CONDITIONAL_SUPPORTED = (Gate, Delay) - def __init__(self, durations: InstructionDurations): + def __init__(self, durations: InstructionDurations = None, target: Target = None): """Scheduler initializer. Args: durations: Durations of instructions to be used in scheduling + target: The :class:`~.Target` representing the target backend, if both + ``durations`` and this are specified then this argument will take + precedence and ``durations`` will be ignored. + """ super().__init__() self.durations = durations + if target is not None: + self.durations = target.durations() # Ensure op node durations are attached and in consistent unit - self.requires.append(TimeUnitConversion(durations)) + self.requires.append(TimeUnitConversion(inst_durations=durations, target=target)) # Initialize timeslot if "node_start_time" in self.property_set: diff --git a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py index e073023ebd6d..57c9587316b7 100644 --- a/qiskit/transpiler/passes/scheduling/time_unit_conversion.py +++ b/qiskit/transpiler/passes/scheduling/time_unit_conversion.py @@ -18,6 +18,7 @@ from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.instruction_durations import InstructionDurations +from qiskit.transpiler.target import Target class TimeUnitConversion(TransformationPass): @@ -35,14 +36,21 @@ class TimeUnitConversion(TransformationPass): * raise error: if they are a mix of SI units and 'dt'. """ - def __init__(self, inst_durations: InstructionDurations): + def __init__(self, inst_durations: InstructionDurations = None, target: Target = None): """TimeUnitAnalysis initializer. Args: inst_durations (InstructionDurations): A dictionary of durations of instructions. + target: The :class:`~.Target` representing the target backend, if both + ``inst_durations`` and this are specified then this argument will take + precedence and ``inst_durations`` will be ignored. + + """ super().__init__() self.inst_durations = inst_durations or InstructionDurations() + if target is not None: + self.inst_durations = target.durations() def run(self, dag: DAGCircuit): """Run the TimeUnitAnalysis pass on `dag`. diff --git a/qiskit/transpiler/passes/utils/check_cx_direction.py b/qiskit/transpiler/passes/utils/check_cx_direction.py index 261b6a9c1489..298a27aae130 100644 --- a/qiskit/transpiler/passes/utils/check_cx_direction.py +++ b/qiskit/transpiler/passes/utils/check_cx_direction.py @@ -19,8 +19,8 @@ class CheckCXDirection(CheckGateDirection): """Deprecated: use :class:`qiskit.transpiler.passes.CheckGateDirection` pass instead.""" - def __init__(self, coupling_map): - super().__init__(coupling_map) + def __init__(self, coupling_map=None, target=None): + super().__init__(coupling_map=coupling_map, target=target) warnings.warn( "The CheckCXDirection pass has been deprecated " "and replaced by a more generic CheckGateDirection pass.", diff --git a/qiskit/transpiler/preset_passmanagers/common.py b/qiskit/transpiler/preset_passmanagers/common.py index 8766b526ee72..8cfe3db8ab26 100644 --- a/qiskit/transpiler/preset_passmanagers/common.py +++ b/qiskit/transpiler/preset_passmanagers/common.py @@ -371,7 +371,7 @@ def generate_translation_passmanager( TranspilerError: If the ``method`` kwarg is not a valid value """ if method == "unroller": - unroll = [Unroller(basis_gates)] + unroll = [Unroller(basis=basis_gates, target=target)] elif method == "translator": unroll = [ # Use unitary synthesis for basis aware decomposition of @@ -424,7 +424,9 @@ def generate_translation_passmanager( return PassManager(unroll) -def generate_scheduling(instruction_durations, scheduling_method, timing_constraints, inst_map): +def generate_scheduling( + instruction_durations, scheduling_method, timing_constraints, inst_map, target=None +): """Generate a post optimization scheduling :class:`~qiskit.transpiler.PassManager` Args: @@ -434,6 +436,7 @@ def generate_scheduling(instruction_durations, scheduling_method, timing_constra ``'alap'``/``'as_late_as_possible'`` timing_constraints (TimingConstraints): Hardware time alignment restrictions. inst_map (InstructionScheduleMap): Mapping object that maps gate to schedule. + target (Target): The :class:`~.Target` object representing the backend Returns: PassManager: The scheduling pass manager @@ -443,7 +446,7 @@ def generate_scheduling(instruction_durations, scheduling_method, timing_constra """ scheduling = PassManager() if inst_map and inst_map.has_custom_gate(): - scheduling.append(PulseGates(inst_map=inst_map)) + scheduling.append(PulseGates(inst_map=inst_map, target=target)) if scheduling_method: # Do scheduling after unit conversion. scheduler = { @@ -452,9 +455,9 @@ def generate_scheduling(instruction_durations, scheduling_method, timing_constra "asap": ASAPScheduleAnalysis, "as_soon_as_possible": ASAPScheduleAnalysis, } - scheduling.append(TimeUnitConversion(instruction_durations)) + scheduling.append(TimeUnitConversion(instruction_durations, target=target)) try: - scheduling.append(scheduler[scheduling_method](instruction_durations)) + scheduling.append(scheduler[scheduling_method](instruction_durations, target=target)) except KeyError as ex: raise TranspilerError("Invalid scheduling method %s." % scheduling_method) from ex elif instruction_durations: @@ -463,7 +466,9 @@ def _contains_delay(property_set): return property_set["contains_delay"] scheduling.append(ContainsInstruction("delay")) - scheduling.append(TimeUnitConversion(instruction_durations), condition=_contains_delay) + scheduling.append( + TimeUnitConversion(instruction_durations, target=target), condition=_contains_delay + ) if ( timing_constraints.granularity != 1 or timing_constraints.min_length != 1 diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 6ead4237a113..d137e16bbe88 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -156,7 +156,7 @@ def _swap_mapped(property_set): pre_opt = None if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map + instruction_durations, scheduling_method, timing_constraints, inst_map, target=target ) else: sched = plugin_manager.get_passmanager_stage( diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 2180fc9e19ba..1ad77652005e 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -280,7 +280,7 @@ def _unroll_condition(property_set): ) if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map + instruction_durations, scheduling_method, timing_constraints, inst_map, target=target ) else: sched = plugin_manager.get_passmanager_stage( diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index b37c8d1a4d67..61699ee9c2ab 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -153,7 +153,7 @@ def _opt_control(property_set): _opt = [ Optimize1qGatesDecomposition(basis=basis_gates, target=target), - CommutativeCancellation(basis_gates=basis_gates), + CommutativeCancellation(basis_gates=basis_gates, target=target), ] unroll_3q = None @@ -234,7 +234,11 @@ def _unroll_condition(property_set): ) if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map + instruction_durations, + scheduling_method, + timing_constraints, + inst_map, + target=target, ) else: sched = plugin_manager.get_passmanager_stage( diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index f0bb68538178..9997cc6d88c4 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -170,7 +170,7 @@ def _opt_control(property_set): target=target, ), Optimize1qGatesDecomposition(basis=basis_gates, target=target), - CommutativeCancellation(), + CommutativeCancellation(target=target), ] # Build pass manager @@ -289,7 +289,7 @@ def _unroll_condition(property_set): if scheduling_method is None or scheduling_method in {"alap", "asap"}: sched = common.generate_scheduling( - instruction_durations, scheduling_method, timing_constraints, inst_map + instruction_durations, scheduling_method, timing_constraints, inst_map, target=target ) elif isinstance(scheduling_method, PassManager): sched = scheduling_method diff --git a/releasenotes/notes/target-transpile-d029922a5dbc3a52.yaml b/releasenotes/notes/target-transpile-d029922a5dbc3a52.yaml new file mode 100644 index 000000000000..fe7a53f595f1 --- /dev/null +++ b/releasenotes/notes/target-transpile-d029922a5dbc3a52.yaml @@ -0,0 +1,35 @@ +--- +features: + - | + The following layout and routing transpiler passes from the + :mod:`.qiskit.transpiler.passes` modules have a new keyword argument, + ``target`` which takes in a :class:`~.Target` object which is used to model + the constraints of a target backend. If the ``target`` keyword argument is + specified it will be used as the source of truth for any hardware + constraints used in the operation of the transpiler pass. It will + superscede any other arguments for specifying hardware constraints, + typically those arguments which take a :class:`~.CouplingMap`, + :class:~.InstructionScheduleMap` or a basis gate list. + The list of these passes with the new ``target`` argument are: + + * :class:`~.Unroller` + * :class:`~.PulseGate` + * :class:`~.RZXCalibrationBuilder` + * :class:`~.CommutativeCancellation` + * :class:`~.EchoRZXWeylDecomposition` + * :class:`~.Optimize1qGatesSimpleCommutation` + * :class:`~.Optimize1qGates` + * :class:`~.CheckCXDirection` + * :class:`~.ALAPSchedule` + * :class:`~.ASAPSchedule` + * :class:`~.ALAPScheduleAnalysis` + * :class:`~.ASAPScheduleAnalysis` + * :class:`~.DynamicalDecoupling` + * :class:`~.PadDynamicalDecoupling` + * :class:`~.TimeUnitConversion` + - | + The pass manager construction helper function :func:`~.generate_scheduling` + has a new keyword argument ``target`` which is used to specify a :class:`~.Target` + object to model the constraints of the target backend being compiled for + when generating a new :class:~.PassManager`. If specified this new argument will + superscede the other argument ``inst_map``. diff --git a/test/python/transpiler/test_commutative_cancellation.py b/test/python/transpiler/test_commutative_cancellation.py index 2d513951a2de..bb66d7135203 100644 --- a/test/python/transpiler/test_commutative_cancellation.py +++ b/test/python/transpiler/test_commutative_cancellation.py @@ -17,7 +17,9 @@ from qiskit.test import QiskitTestCase from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit.library import U1Gate, RZGate +from qiskit.circuit.library import U1Gate, RZGate, PhaseGate, CXGate, SXGate +from qiskit.circuit.parameter import Parameter +from qiskit.transpiler.target import Target from qiskit.transpiler import PassManager, PropertySet from qiskit.transpiler.passes import CommutationAnalysis, CommutativeCancellation, FixedPoint, Size from qiskit.quantum_info import Operator @@ -557,6 +559,27 @@ def test_basis_01(self): self.assertEqual(new_circuit, expected) + def test_target_basis_01(self): + """Test basis priority change, phase gate, with target.""" + circuit = QuantumCircuit(1) + circuit.s(0) + circuit.z(0) + circuit.t(0) + circuit.rz(np.pi, 0) + theta = Parameter("theta") + target = Target(num_qubits=2) + target.add_instruction(CXGate()) + target.add_instruction(PhaseGate(theta)) + target.add_instruction(SXGate()) + passmanager = PassManager() + passmanager.append(CommutativeCancellation(target=target)) + new_circuit = passmanager.run(circuit) + expected = QuantumCircuit(1) + expected.rz(11 * np.pi / 4, 0) + expected.global_phase = 11 * np.pi / 4 / 2 - np.pi / 2 + + self.assertEqual(new_circuit, expected) + def test_basis_02(self): """Test basis priority change, Rz gate""" circuit = QuantumCircuit(1) diff --git a/test/python/transpiler/test_dynamical_decoupling.py b/test/python/transpiler/test_dynamical_decoupling.py index e7c751b57e44..ca57a82239ed 100644 --- a/test/python/transpiler/test_dynamical_decoupling.py +++ b/test/python/transpiler/test_dynamical_decoupling.py @@ -17,8 +17,8 @@ from numpy import pi from ddt import ddt, data -from qiskit.circuit import QuantumCircuit, Delay -from qiskit.circuit.library import XGate, YGate, RXGate, UGate +from qiskit.circuit import QuantumCircuit, Delay, Measure, Reset, Parameter +from qiskit.circuit.library import XGate, YGate, RXGate, UGate, CXGate, HGate from qiskit.quantum_info import Operator from qiskit.transpiler.instruction_durations import InstructionDurations from qiskit.transpiler.passes import ( @@ -28,8 +28,8 @@ ) from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.exceptions import TranspilerError - -import qiskit.pulse as pulse +from qiskit.transpiler.target import Target, InstructionProperties +from qiskit import pulse from qiskit.test import QiskitTestCase @@ -143,6 +143,87 @@ def test_insert_dd_ghz(self): self.assertEqual(ghz4_dd, expected) + def test_insert_dd_ghz_with_target(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: ─────────────────────────────────────────────────────────────────────── + « + """ + target = Target(num_qubits=4, dt=1) + target.add_instruction(HGate(), {(0,): InstructionProperties(duration=50)}) + target.add_instruction( + CXGate(), + { + (0, 1): InstructionProperties(duration=700), + (1, 2): InstructionProperties(duration=200), + (2, 3): InstructionProperties(duration=300), + }, + ) + target.add_instruction( + XGate(), {(x,): InstructionProperties(duration=50) for x in range(4)} + ) + target.add_instruction( + YGate(), {(x,): InstructionProperties(duration=50) for x in range(4)} + ) + target.add_instruction( + UGate(Parameter("theta"), Parameter("phi"), Parameter("lambda")), + {(x,): InstructionProperties(duration=100) for x in range(4)}, + ) + target.add_instruction( + RXGate(Parameter("theta")), + {(x,): InstructionProperties(duration=100) for x in range(4)}, + ) + target.add_instruction( + Measure(), {(x,): InstructionProperties(duration=1000) for x in range(4)} + ) + target.add_instruction( + Reset(), {(x,): InstructionProperties(duration=1500) for x in range(4)} + ) + dd_sequence = [XGate(), XGate()] + pm = PassManager( + [ + ALAPScheduleAnalysis(target=target), + PadDynamicalDecoupling(target=target, dd_sequence=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. diff --git a/test/python/transpiler/test_optimize_1q_commutation.py b/test/python/transpiler/test_optimize_1q_commutation.py index 2a01e5593907..200c27eb5488 100644 --- a/test/python/transpiler/test_optimize_1q_commutation.py +++ b/test/python/transpiler/test_optimize_1q_commutation.py @@ -19,11 +19,13 @@ import ddt import numpy as np -from qiskit.circuit import QuantumCircuit +from qiskit.circuit import QuantumCircuit, Parameter from qiskit.converters import circuit_to_dag from qiskit.transpiler.passes.optimization.optimize_1q_commutation import ( Optimize1qGatesSimpleCommutation, ) +from qiskit.circuit.library import SXGate, PhaseGate +from qiskit.transpiler import Target from qiskit.test import QiskitTestCase @@ -77,6 +79,57 @@ def test_predecessor_commutation(self): msg = f"expected:\n{expected}\nresult:\n{result}" self.assertEqual(expected, result, msg=msg) + def test_successor_commutation_with_target(self): + """ + Check that Optimize1qGatesSimpleCommutation correctly moves 1Q gates later. + """ + # q_0: ────────■───────────────────────── + # ┌────┐┌─┴─┐┌───────┐┌────┐┌──────┐ + # q_1: ┤ √X ├┤ X ├┤ P(-π) ├┤ √X ├┤ P(π) ├ + # └────┘└───┘└───────┘└────┘└──────┘ + qc = QuantumCircuit(2) + qc.sx(1) + qc.cx(0, 1) + qc.p(-np.pi, 1) + qc.sx(1) + qc.p(np.pi, 1) + target = Target(num_qubits=1) + target.add_instruction(SXGate()) + target.add_instruction(PhaseGate(Parameter("theta"))) + optimize_pass = Optimize1qGatesSimpleCommutation(target=target, run_to_completion=True) + result = optimize_pass(qc) + + expected = QuantumCircuit(2, global_phase=np.pi / 2) + expected.cx(0, 1) + msg = f"expected:\n{expected}\nresult:\n{result}" + self.assertEqual(expected, result, msg=msg) + + def test_predecessor_commutation_with_target(self): + """ + Check that Optimize1qGatesSimpleCommutation correctly moves 1Q gates earlier. + """ + # q_0: ─────────────────────────■──────── + # ┌───────┐┌────┐┌──────┐┌─┴─┐┌────┐ + # q_1: ┤ P(-π) ├┤ √X ├┤ P(π) ├┤ X ├┤ √X ├ + # └───────┘└────┘└──────┘└───┘└────┘ + qc = QuantumCircuit(2) + qc.p(-np.pi, 1) + qc.sx(1) + qc.p(np.pi, 1) + qc.cx(0, 1) + qc.sx(1) + target = Target(num_qubits=1) + target.add_instruction(SXGate()) + target.add_instruction(PhaseGate(Parameter("theta"))) + + optimize_pass = Optimize1qGatesSimpleCommutation(target=target, run_to_completion=True) + result = optimize_pass(qc) + + expected = QuantumCircuit(2, global_phase=np.pi / 2) + expected.cx(0, 1) + msg = f"expected:\n{expected}\nresult:\n{result}" + self.assertEqual(expected, result, msg=msg) + def test_elaborate_commutation(self): """ Check that Optimize1qGatesSimpleCommutation can perform several steps without fumbling. diff --git a/test/python/transpiler/test_optimize_1q_gates.py b/test/python/transpiler/test_optimize_1q_gates.py index 0f4dc300a3ba..aa41d51892a2 100644 --- a/test/python/transpiler/test_optimize_1q_gates.py +++ b/test/python/transpiler/test_optimize_1q_gates.py @@ -23,6 +23,7 @@ from qiskit.circuit import Parameter from qiskit.circuit.library import U1Gate, U2Gate, U3Gate, UGate, PhaseGate from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.target import Target class TestOptimize1qGates(QiskitTestCase): @@ -484,6 +485,24 @@ def test_optimize_u3_basis_u2(self): self.assertEqual(expected, result) + def test_optimize_u3_basis_u2_with_target(self): + """U3(pi/2, 0, pi/4) -> U2(0, pi/4)""" + qr = QuantumRegister(1, "qr") + circuit = QuantumCircuit(qr) + circuit.append(U3Gate(np.pi / 2, 0, np.pi / 4), [qr[0]]) + + expected = QuantumCircuit(qr) + expected.append(U2Gate(0, np.pi / 4), [qr[0]]) + + target = Target(num_qubits=1) + target.add_instruction(U2Gate(Parameter("theta"), Parameter("phi"))) + + passmanager = PassManager() + passmanager.append(Optimize1qGates(target=target)) + result = passmanager.run(circuit) + + self.assertEqual(expected, result) + def test_optimize_u_basis_u(self): """U(pi/2, pi/3, pi/4) (basis[u3]) -> U(pi/2, pi/3, pi/4)""" qr = QuantumRegister(1, "qr") diff --git a/test/python/transpiler/test_pulse_gate_pass.py b/test/python/transpiler/test_pulse_gate_pass.py index b6a227f6a724..f5e9da25c727 100644 --- a/test/python/transpiler/test_pulse_gate_pass.py +++ b/test/python/transpiler/test_pulse_gate_pass.py @@ -14,7 +14,7 @@ from qiskit import pulse, circuit, transpile from qiskit.test import QiskitTestCase -from qiskit.providers.fake_provider import FakeAthens +from qiskit.providers.fake_provider import FakeAthens, FakeAthensV2 class TestPulseGate(QiskitTestCase): @@ -63,6 +63,23 @@ def test_transpile_with_bare_backend(self): ref_calibration = {} self.assertDictEqual(transpiled_qc.calibrations, ref_calibration) + def test_transpile_with_backend_target(self): + """Test transpile without custom calibrations from target.""" + backend = FakeAthensV2() + target = backend.target + + qc = circuit.QuantumCircuit(2) + qc.sx(0) + qc.x(0) + qc.rz(0, 0) + qc.sx(1) + qc.measure_all() + + transpiled_qc = transpile(qc, initial_layout=[0, 1], target=target) + + ref_calibration = {} + self.assertDictEqual(transpiled_qc.calibrations, ref_calibration) + def test_transpile_with_custom_basis_gate(self): """Test transpile with custom calibrations.""" backend = FakeAthens() @@ -86,6 +103,30 @@ def test_transpile_with_custom_basis_gate(self): } self.assertDictEqual(transpiled_qc.calibrations, ref_calibration) + def test_transpile_with_custom_basis_gate_in_target(self): + """Test transpile with custom calibrations.""" + backend = FakeAthensV2() + target = backend.target + target["sx"][(0,)].calibration = self.custom_sx_q0 + target["sx"][(1,)].calibration = self.custom_sx_q1 + + qc = circuit.QuantumCircuit(2) + qc.sx(0) + qc.x(0) + qc.rz(0, 0) + qc.sx(1) + qc.measure_all() + + transpiled_qc = transpile(qc, initial_layout=[0, 1], target=target) + + ref_calibration = { + "sx": { + ((0,), ()): self.custom_sx_q0, + ((1,), ()): self.custom_sx_q1, + } + } + self.assertDictEqual(transpiled_qc.calibrations, ref_calibration) + def test_transpile_with_instmap(self): """Test providing instruction schedule map.""" instmap = FakeAthens().defaults().instruction_schedule_map diff --git a/test/python/transpiler/test_scheduling_padding_pass.py b/test/python/transpiler/test_scheduling_padding_pass.py index bf05f6decaec..29d086a44163 100644 --- a/test/python/transpiler/test_scheduling_padding_pass.py +++ b/test/python/transpiler/test_scheduling_padding_pass.py @@ -16,6 +16,8 @@ from ddt import ddt, data, unpack from qiskit import QuantumCircuit +from qiskit.circuit import Measure +from qiskit.circuit.library import CXGate, HGate from qiskit.pulse import Schedule, Play, Constant, DriveChannel from qiskit.test import QiskitTestCase from qiskit.transpiler.instruction_durations import InstructionDurations @@ -27,6 +29,7 @@ ) from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.exceptions import TranspilerError +from qiskit.transpiler.target import Target, InstructionProperties @ddt @@ -55,6 +58,35 @@ def test_alap_agree_with_reverse_asap_reverse(self): self.assertEqual(alap_qc, new_qc) + def test_alap_agree_with_reverse_asap_with_target(self): + """Test if ALAP schedule agrees with doubly-reversed ASAP schedule.""" + qc = QuantumCircuit(2) + qc.h(0) + qc.delay(500, 1) + qc.cx(0, 1) + qc.measure_all() + + target = Target(num_qubits=2, dt=3.5555555555555554) + target.add_instruction(HGate(), {(0,): InstructionProperties(duration=200)}) + target.add_instruction(CXGate(), {(0, 1): InstructionProperties(duration=700)}) + target.add_instruction( + Measure(), + { + (0,): InstructionProperties(duration=1000), + (1,): InstructionProperties(duration=1000), + }, + ) + + pm = PassManager([ALAPScheduleAnalysis(target=target), PadDelay()]) + alap_qc = pm.run(qc) + + pm = PassManager([ASAPScheduleAnalysis(target=target), PadDelay()]) + new_qc = pm.run(qc.reverse_ops()) + new_qc = new_qc.reverse_ops() + new_qc.name = new_qc.name + + self.assertEqual(alap_qc, new_qc) + @data(ALAPScheduleAnalysis, ASAPScheduleAnalysis) def test_classically_controlled_gate_after_measure(self, schedule_pass): """Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit. diff --git a/test/python/transpiler/test_unroller.py b/test/python/transpiler/test_unroller.py index 2cdd7d86a4b4..0f8507a0104e 100644 --- a/test/python/transpiler/test_unroller.py +++ b/test/python/transpiler/test_unroller.py @@ -24,6 +24,7 @@ from qiskit.exceptions import QiskitError from qiskit.circuit import Parameter, Qubit, Clbit from qiskit.circuit.library import U1Gate, U2Gate, U3Gate, CU1Gate, CU3Gate +from qiskit.transpiler.target import Target class TestUnroller(QiskitTestCase): @@ -41,6 +42,21 @@ def test_basic_unroll(self): self.assertEqual(len(op_nodes), 1) self.assertEqual(op_nodes[0].name, "u2") + def test_basic_unroll_target(self): + """Test decompose a single H into U2 from target.""" + qc = QuantumCircuit(1) + qc.h(0) + target = Target(num_qubits=1) + phi = Parameter("phi") + lam = Parameter("lam") + target.add_instruction(U2Gate(phi, lam)) + dag = circuit_to_dag(qc) + pass_ = Unroller(target=target) + unrolled_dag = pass_.run(dag) + op_nodes = unrolled_dag.op_nodes() + self.assertEqual(len(op_nodes), 1) + self.assertEqual(op_nodes[0].name, "u2") + def test_unroll_toffoli(self): """Test unroll toffoli on multi regs to h, t, tdg, cx.""" qr1 = QuantumRegister(2, "qr1") diff --git a/test/python/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index 0eb868f15352..01ceadc13f7f 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -41,45 +41,47 @@ fontname=helvetica; label="[4] "; labeljust=l; 12 [color=blue, fontname=helvetica, label=Unroller, shape=rectangle]; -13 [color=black, fontname=helvetica, fontsize=10, label=basis, shape=ellipse, style=solid]; +13 [color=black, fontname=helvetica, fontsize=10, label=basis, shape=ellipse, style=dashed]; 13 -> 12; +14 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +14 -> 12; 10 -> 12; } -subgraph cluster_14 { +subgraph cluster_15 { fontname=helvetica; label="[5] "; labeljust=l; -15 [color=red, fontname=helvetica, label=CheckMap, shape=rectangle]; -16 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -16 -> 15; -12 -> 15; +16 [color=red, fontname=helvetica, label=CheckMap, shape=rectangle]; +17 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +17 -> 16; +12 -> 16; } -subgraph cluster_17 { +subgraph cluster_18 { fontname=helvetica; label="[6] do_while"; labeljust=l; -18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; -15 -> 18; +19 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +16 -> 19; } -subgraph cluster_19 { +subgraph cluster_20 { fontname=helvetica; label="[7] "; labeljust=l; -20 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; -21 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -21 -> 20; -18 -> 20; +21 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; +22 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +22 -> 21; +19 -> 21; } -subgraph cluster_22 { +subgraph cluster_23 { fontname=helvetica; label="[8] "; labeljust=l; -23 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 23; +24 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +21 -> 24; } } diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 136b4b0fea57..d7db9f544efb 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -41,45 +41,47 @@ fontname=helvetica; label="[4] "; labeljust=l; 12 [color=blue, fontname=helvetica, label=Unroller, shape=rectangle]; -13 [color=black, fontname=helvetica, fontsize=10, label=basis, shape=ellipse, style=solid]; +13 [color=black, fontname=helvetica, fontsize=10, label=basis, shape=ellipse, style=dashed]; 13 -> 12; +14 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +14 -> 12; 10 -> 12; } -subgraph cluster_14 { +subgraph cluster_15 { fontname=helvetica; label="[5] "; labeljust=l; -15 [color=green, fontname=helvetica, label=CheckMap, shape=rectangle]; -16 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -16 -> 15; -12 -> 15; +16 [color=green, fontname=helvetica, label=CheckMap, shape=rectangle]; +17 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +17 -> 16; +12 -> 16; } -subgraph cluster_17 { +subgraph cluster_18 { fontname=helvetica; label="[6] do_while"; labeljust=l; -18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; -15 -> 18; +19 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +16 -> 19; } -subgraph cluster_19 { +subgraph cluster_20 { fontname=helvetica; label="[7] "; labeljust=l; -20 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; -21 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; -21 -> 20; -18 -> 20; +21 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; +22 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +22 -> 21; +19 -> 21; } -subgraph cluster_22 { +subgraph cluster_23 { fontname=helvetica; label="[8] "; labeljust=l; -23 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 23; +24 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +21 -> 24; } }