Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qiskit/compiler/transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def transpile(
argument.
dt: Backend sample time (resolution) in seconds.
If ``None`` (default), ``backend.dt`` is used.
approximation_degree (float): heuristic dial used for circuit approximation
approximation_degree: heuristic dial used for circuit approximation
(1.0=no approximation, 0.0=maximal approximation)
seed_transpiler: Sets random seed for the stochastic parts of the transpiler
optimization_level: How much optimization to perform on the circuits.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

"""Commutative Optimization transpiler pass."""

from __future__ import annotations

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.circuit.commutation_library import SessionCommutationChecker as scc
Expand All @@ -38,7 +39,7 @@ class CommutativeOptimization(TransformationPass):
:class:`.CommutativeInverseCancellation`.
"""

def __init__(self, approximation_degree: float = 1.0, matrix_max_num_qubits: int = 0):
def __init__(self, approximation_degree: float | None = 1.0, matrix_max_num_qubits: int = 0):
"""
Args:
approximation_degree: the threshold used in the average gate fidelity
Expand All @@ -49,7 +50,7 @@ def __init__(self, approximation_degree: float = 1.0, matrix_max_num_qubits: int
"""
super().__init__()
self.commutation_checker = scc.cc
self.approximation_degree = approximation_degree
self.approximation_degree = approximation_degree or 1.0
self.matrix_max_num_qubits = matrix_max_num_qubits

@trivial_recurse
Expand Down
27 changes: 17 additions & 10 deletions qiskit/transpiler/passes/optimization/consolidate_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
"""Replace each block of consecutive gates by a single Unitary node."""
from __future__ import annotations

import typing

from qiskit.synthesis.two_qubit import TwoQubitBasisDecomposer, TwoQubitControlledUDecomposer
from qiskit.circuit.library.standard_gates import (
CXGate,
Expand All @@ -36,6 +38,11 @@
from .collect_1q_runs import Collect1qRuns
from .collect_2q_blocks import Collect2qBlocks


if typing.TYPE_CHECKING:
from qiskit.transpiler.target import Target
from qiskit.circuit.gate import Gate

KAK_GATE_NAMES = {
"cx": CXGate(),
"cz": CZGate(),
Expand Down Expand Up @@ -78,11 +85,11 @@ class ConsolidateBlocks(TransformationPass):

def __init__(
self,
kak_basis_gate=None,
force_consolidate=False,
basis_gates=None,
approximation_degree=1.0,
target=None,
kak_basis_gate: Gate | None = None,
force_consolidate: bool = False,
basis_gates: list[str] | None = None,
approximation_degree: float | None = 1.0,
target: Target | None = None,
):
"""ConsolidateBlocks initializer.

Expand All @@ -91,11 +98,11 @@ def __init__(
Otherwise, the basis gate will be :class:`.CXGate`.

Args:
kak_basis_gate (Gate): Basis gate for KAK decomposition.
force_consolidate (bool): Force block consolidation.
basis_gates (List(str)): Basis gates from which to choose a KAK gate.
approximation_degree (float): a float between :math:`[0.0, 1.0]`. Lower approximates more.
target (Target): The target object for the compilation target backend.
kak_basis_gate: Basis gate for KAK decomposition.
force_consolidate: Force block consolidation.
basis_gates: Basis gates from which to choose a KAK gate.
approximation_degree: a float between :math:`[0.0, 1.0]`. Lower approximates more.
target: The target object for the compilation target backend.
"""
super().__init__()
self.basis_gates = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

"""Convert rotation gates into {Clifford,T,Tdg} when their angles are integer multiples of pi/4"""

from __future__ import annotations

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.passes.utils import control_flow
Expand Down Expand Up @@ -67,14 +69,14 @@ class SubstitutePi4Rotations(TransformationPass):
assert Operator(qc) == Operator(qct)
"""

def __init__(self, approximation_degree: float = 1.0):
def __init__(self, approximation_degree: float | None = 1.0):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If None just immediately defaults to 1.0, why would we change to supporting None as input? The current interface seems complete without it, no? (Same question for CommutativeOptimization & co)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I fully understand the question. Calling CommutativeOptimization(approximation_degree=None) is not the same as calling CommutativeOptimization(), as in the former case approximation_degree will be None, and in the latter case it will the default value of 1.0, do you agree?

In other transpiler passes, passing approximation_degree=None means "compute approximation degree based on the target", so this option has a special meaning that I want to expose here as well. This will also allow a more consistent story: in all user-facing functions (Python and C) approximation_degree is of type float | None.

Having said the above, CommutativeOptimization currently does not take target as an argument (so internally we do set approximation_degree to 1.0), however we want to change this in the future.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm saying that CommutativeOptimization(approximation_degree=None) is not a valid input, so we don't have to support it. If we add a Target support in the future we can enable setting it to None -- or is there a reason to add it now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove it. But I think it would be nice to have the same convention for what approximation degree is across the code.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it would be nice, but for that we'd need to have the Target-specific behavior implemented 🙂

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we already have a similar workaround in ConsolidateBlocks:

basis_fidelity=approximation_degree or 1.0,
.

More importantly, since we allow approximation_degree to be None when generating preset pass managers, defining plugins, etc., we need to explicitly remember to overwrite None with 1.0 when calling CommutativeOptimization and Substitute4PiRotations, or else we will hit python type errors. The only reason we don't get these errors now is because we currently only enable these passes in the Clifford+T pipeline and because I forgot to pass the approximation_degree to these passes 😅.

"""
Args:
approximation_degree: Used in the tolerance computations.
This gives the threshold for the average gate fidelity.
"""
super().__init__()
self.approximation_degree = approximation_degree
self.approximation_degree = approximation_degree or 1.0

@control_flow.trivial_recurse
def run(self, dag: DAGCircuit) -> DAGCircuit:
Expand Down
18 changes: 9 additions & 9 deletions qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ def __init__(
self,
basis_gates: list[str] | None = None,
approximation_degree: float | None = 1.0,
coupling_map: CouplingMap = None,
coupling_map: CouplingMap | None = None,
pulse_optimize: bool | None = None,
natural_direction: bool | None = None,
synth_gates: list[str] | None = None,
method: str = "default",
min_qubits: int = 0,
plugin_config: dict | None = None,
target: Target = None,
target: Target | None = None,
fallback_on_default: bool = False,
):
"""Synthesize unitaries over some basis gates.
Expand All @@ -76,24 +76,24 @@ def __init__(
``approximation_degree``.

Args:
basis_gates (list[str]): List of gate names to target. If this is
basis_gates: List of gate names to target. If this is
not specified the ``target`` argument must be used. If both this
and the ``target`` are specified the value of ``target`` will
be used and this will be ignored.
approximation_degree (float): heuristic dial used for circuit approximation
approximation_degree: heuristic dial used for circuit approximation
(1.0=no approximation, 0.0=maximal approximation). Approximation can
make the synthesized circuit cheaper at the cost of straying from
the original unitary. If None, approximation is done based on gate fidelities.
coupling_map (CouplingMap): the coupling map of the target
coupling_map: the coupling map of the target
in case synthesis is done on a physical circuit. The
directionality of the coupling_map will be taken into
account if ``pulse_optimize`` is ``True``/``None`` and ``natural_direction``
is ``True``/``None``.
pulse_optimize (bool): Whether to optimize pulses during
pulse_optimize: Whether to optimize pulses during
synthesis. A value of ``None`` will attempt it but fall
back if it does not succeed. A value of ``True`` will raise
an error if pulse-optimized synthesis does not succeed.
natural_direction (bool): Whether to apply synthesis considering
natural_direction: Whether to apply synthesis considering
directionality of 2-qubit gates. Only applies when
``pulse_optimize`` is ``True`` or ``None``. The natural direction is
determined by first checking to see whether the
Expand All @@ -105,11 +105,11 @@ def __init__(
determined, raises :class:`.TranspilerError`. If set to None, no
exception will be raised if a natural direction can
not be determined.
synth_gates (list[str]): List of gates to synthesize. If None and
synth_gates: List of gates to synthesize. If None and
``pulse_optimize`` is False or None, default to
``['unitary']``. If ``None`` and ``pulse_optimize == True``,
default to ``['unitary', 'swap']``
method (str): The unitary synthesis method plugin to use.
method: The unitary synthesis method plugin to use.
min_qubits: The minimum number of qubits in the unitary to synthesize. If this is set
and the unitary is less than the specified number of qubits it will not be
synthesized.
Expand Down
82 changes: 46 additions & 36 deletions qiskit/transpiler/passmanager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,72 +12,82 @@

"""Pass Manager Configuration class."""

from __future__ import annotations

import typing

if typing.TYPE_CHECKING:
from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
from qiskit.transpiler import Target, CouplingMap, Layout
from qiskit.transpiler.timing_constraints import TimingConstraints
from .instruction_durations import InstructionDurations


class PassManagerConfig:
"""Pass Manager Configuration."""

def __init__(
self,
initial_layout=None,
basis_gates=None,
coupling_map=None,
layout_method=None,
routing_method=None,
translation_method=None,
scheduling_method=None,
instruction_durations=None,
approximation_degree=None,
seed_transpiler=None,
timing_constraints=None,
unitary_synthesis_method="default",
unitary_synthesis_plugin_config=None,
target=None,
hls_config=None,
init_method=None,
optimization_method=None,
qubits_initially_zero=True,
initial_layout: Layout | None = None,
basis_gates: list[str] | None = None,
coupling_map: CouplingMap | None = None,
layout_method: str | None = None,
routing_method: str | None = None,
translation_method: str | None = None,
scheduling_method: str | None = None,
instruction_durations: InstructionDurations | None = None,
approximation_degree: float | None = None,
seed_transpiler: int | None = None,
timing_constraints: TimingConstraints | None = None,
unitary_synthesis_method: str = "default",
unitary_synthesis_plugin_config: dict | None = None,
target: Target | None = None,
hls_config: HLSConfig | None = None,
init_method: str | None = None,
optimization_method: str | None = None,
qubits_initially_zero: bool = True,
):
"""Initialize a PassManagerConfig object

Args:
initial_layout (Layout): Initial position of virtual qubits on
initial_layout: Initial position of virtual qubits on
physical qubits.
basis_gates (list): List of basis gate names to unroll to.
coupling_map (CouplingMap): Directed graph representing a coupling
basis_gates: List of basis gate names to unroll to.
coupling_map: Directed graph representing a coupling
map.
layout_method (str): the pass to use for choosing initial qubit
layout_method: the pass to use for choosing initial qubit
placement. This will be the plugin name if an external layout stage
plugin is being used.
routing_method (str): the pass to use for routing qubits on the
routing_method: the pass to use for routing qubits on the
architecture. This will be a plugin name if an external routing stage
plugin is being used.
translation_method (str): the pass to use for translating gates to
translation_method: the pass to use for translating gates to
basis_gates. This will be a plugin name if an external translation stage
plugin is being used.
scheduling_method (str): the pass to use for scheduling instructions. This will
scheduling_method: the pass to use for scheduling instructions. This will
be a plugin name if an external scheduling stage plugin is being used.
instruction_durations (InstructionDurations): Dictionary of duration
instruction_durations: Dictionary of duration
(in dt) for each instruction.
approximation_degree (float): heuristic dial used for circuit approximation
approximation_degree: heuristic dial used for circuit approximation
(1.0=no approximation, 0.0=maximal approximation)
seed_transpiler (int): Sets random seed for the stochastic parts of
seed_transpiler: Sets random seed for the stochastic parts of
the transpiler.
timing_constraints (TimingConstraints): Hardware time alignment restrictions.
unitary_synthesis_method (str): The string method to use for the
timing_constraints: Hardware time alignment restrictions.
unitary_synthesis_method: The string method to use for the
:class:`~qiskit.transpiler.passes.UnitarySynthesis` pass. Will
search installed plugins for a valid method. You can see a list of
installed plugins with :func:`.unitary_synthesis_plugin_names`.
unitary_synthesis_plugin_config (dict): The configuration dictionary that will
unitary_synthesis_plugin_config: The configuration dictionary that will
be passed to the specified unitary synthesis plugin. Refer to
the plugin documentation for how to use this.
target (Target): The backend target
hls_config (HLSConfig): An optional configuration class to use for
target: The backend target
hls_config: An optional configuration class to use for
:class:`~qiskit.transpiler.passes.HighLevelSynthesis` pass.
Specifies how to synthesize various high-level objects.
init_method (str): The plugin name for the init stage plugin to use
optimization_method (str): The plugin name for the optimization stage plugin
init_method: The plugin name for the init stage plugin to use
optimization_method: The plugin name for the optimization stage plugin
to use.
qubits_initially_zero (bool): Indicates whether the input circuit is
qubits_initially_zero: Indicates whether the input circuit is
zero-initialized.
"""
self.initial_layout = initial_layout
Expand Down
Loading