Skip to content

Commit

Permalink
Add target support to basis, optimization, and util passes
Browse files Browse the repository at this point in the history
This commit updates all the basis, optimization, and util passes which
leverage hardware constraints to have a new optional keyword argument,
target, to specify a Target object for modelling the constraints of the
target backend. This option will superscede any other specified arguments
for backend constraints.

With this and Qiskit#9263 all the passes except for the scheduling passes
which take in backend constraints are made target aware. Making all the
passes target aware is the first step towards making all backend
constraints for the transpiler used the Target internally (see Qiskit#9256).
Once all passes that take hardware constraints parameters as an input are
updated to have a target we can start internally using the Target only for
all preset pass manager construction and start the long process of
deprecating the legacy interface in these passes.
  • Loading branch information
mtreinish committed Jan 4, 2023
1 parent 2f5944d commit 42ff7c2
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 82 deletions.
50 changes: 35 additions & 15 deletions qiskit/transpiler/passes/basis/unroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -57,18 +61,28 @@ def run(self, dag):
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:
if self.target is not None:
if (
self.target.instruction_supported(node.op.name, qargs=tuple(node.qargs))
or node.op.name == "barrier"
):
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
Expand All @@ -87,10 +101,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, qargs=node.qargs):
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
Expand Down
9 changes: 8 additions & 1 deletion qiskit/transpiler/passes/calibration/pulse_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ScheduleBlock,
)
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap
from qiskit.transpiler.target import Target

from .base_builder import CalibrationBuilder

Expand Down Expand Up @@ -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.
Expand Down
7 changes: 7 additions & 0 deletions qiskit/transpiler/passes/calibration/rzx_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 39 additions & 15 deletions qiskit/transpiler/passes/optimization/optimize_1q_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,22 @@
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:
basis (list[str]): Basis gates to consider, e.g. `['u3', 'cx']`. For the effects
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`.
Expand All @@ -64,10 +68,16 @@ def run(self, dag):
runs = dag.collect_runs(["u1", "u2", "u3", "u", "p"])
runs = _split_runs_on_parameters(runs)
for run in runs:
if use_p:
right_name = "p"
if self.target is not None:
if self.target.instruction_supported("p", run[0].qargs):
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:
Expand Down Expand Up @@ -256,16 +266,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[0].qargs):
if self.target.instruction_supported("u", run[0].qargs):
right_name = "u"
else:
right_name = "u3"
if right_name in ("u1", "p") and not self.target.instruction_supported(
right_name, run[0].qargs
):
if self.target.instruction_supported("u", run[0].qargs):
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":
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/passes/utils/check_cx_direction.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
9 changes: 6 additions & 3 deletions qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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 = {
Expand Down
Loading

0 comments on commit 42ff7c2

Please sign in to comment.