From 42ff7c274cfc6c75bbda59922058cbf295bbac8e Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 4 Jan 2023 16:02:05 -0500 Subject: [PATCH] Add target support to basis, optimization, and util passes 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 #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 #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. --- qiskit/transpiler/passes/basis/unroller.py | 50 +++++++++++------ .../passes/calibration/pulse_gate.py | 9 +++- .../passes/calibration/rzx_builder.py | 7 +++ .../optimization/commutative_cancellation.py | 7 ++- .../crosstalk_adaptive_schedule.py | 10 +++- .../echo_rzx_weyl_decomposition.py | 7 ++- .../optimization/optimize_1q_commutation.py | 8 +-- .../passes/optimization/optimize_1q_gates.py | 54 +++++++++++++------ .../passes/utils/check_cx_direction.py | 4 +- .../transpiler/preset_passmanagers/common.py | 9 ++-- .../transpiler/preset_passmanagers/level0.py | 2 +- .../transpiler/preset_passmanagers/level1.py | 2 +- .../transpiler/preset_passmanagers/level2.py | 8 ++- .../transpiler/preset_passmanagers/level3.py | 4 +- .../target-transpile-d029922a5dbc3a52.yaml | 28 ++++++++++ .../references/pass_manager_standard.dot | 38 +++++++------ .../references/pass_manager_style.dot | 38 +++++++------ 17 files changed, 203 insertions(+), 82 deletions(-) create mode 100644 releasenotes/notes/target-transpile-d029922a5dbc3a52.yaml diff --git a/qiskit/transpiler/passes/basis/unroller.py b/qiskit/transpiler/passes/basis/unroller.py index 5027e99bd550..f59d784708ea 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`. @@ -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 @@ -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 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_gates.py b/qiskit/transpiler/passes/optimization/optimize_1q_gates.py index ad473f6983af..94b6837d2381 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`. @@ -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: @@ -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": 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..52c82ad40849 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 = { 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..017e1a55a099 --- /dev/null +++ b/releasenotes/notes/target-transpile-d029922a5dbc3a52.yaml @@ -0,0 +1,28 @@ +--- +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` + - | + 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/visualization/references/pass_manager_standard.dot b/test/python/visualization/references/pass_manager_standard.dot index 0eb868f15352..ba79a7e900c4 100644 --- a/test/python/visualization/references/pass_manager_standard.dot +++ b/test/python/visualization/references/pass_manager_standard.dot @@ -41,45 +41,49 @@ 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=dashed]; +17 -> 16; +18 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +18 -> 16; +12 -> 16; } -subgraph cluster_17 { +subgraph cluster_19 { fontname=helvetica; label="[6] do_while"; labeljust=l; -18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; -15 -> 18; +20 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +16 -> 20; } -subgraph cluster_19 { +subgraph cluster_21 { 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; +22 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; +23 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +23 -> 22; +20 -> 22; } -subgraph cluster_22 { +subgraph cluster_24 { fontname=helvetica; label="[8] "; labeljust=l; -23 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 23; +25 [color=blue, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +22 -> 25; } } diff --git a/test/python/visualization/references/pass_manager_style.dot b/test/python/visualization/references/pass_manager_style.dot index 136b4b0fea57..9f77e82c63eb 100644 --- a/test/python/visualization/references/pass_manager_style.dot +++ b/test/python/visualization/references/pass_manager_style.dot @@ -41,45 +41,49 @@ 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=dashed]; +17 -> 16; +18 [color=black, fontname=helvetica, fontsize=10, label=target, shape=ellipse, style=dashed]; +18 -> 16; +12 -> 16; } -subgraph cluster_17 { +subgraph cluster_19 { fontname=helvetica; label="[6] do_while"; labeljust=l; -18 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; -15 -> 18; +20 [color=blue, fontname=helvetica, label=BarrierBeforeFinalMeasurements, shape=rectangle]; +16 -> 20; } -subgraph cluster_19 { +subgraph cluster_21 { 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; +22 [color=blue, fontname=helvetica, label=CXDirection, shape=rectangle]; +23 [color=black, fontname=helvetica, fontsize=10, label=coupling_map, shape=ellipse, style=solid]; +23 -> 22; +20 -> 22; } -subgraph cluster_22 { +subgraph cluster_24 { fontname=helvetica; label="[8] "; labeljust=l; -23 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; -20 -> 23; +25 [color=grey, fontname=helvetica, label=RemoveResetInZeroState, shape=rectangle]; +22 -> 25; } }