From e1056ee62c895b33406f69823fb5073cfdcba9bd Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Mon, 22 May 2023 22:21:27 +0300 Subject: [PATCH] Fix mypy errors (transpiler) (#8266) * Fix transpiler mypy errors * Fix review comments, add a bit more annotations * Fix annotation * Fix sphinx * Fix lint * Update dynamical_decoupling.py * Replace deprecated function call * Update qiskit/transpiler/passes/layout/disjoint_utils.py Co-authored-by: Luciano Bello * Fix type definition --------- Co-authored-by: Luciano Bello --- qiskit/transpiler/instruction_durations.py | 30 ++++++----- qiskit/transpiler/layout.py | 9 ++-- .../passes/calibration/rzx_builder.py | 15 +++--- .../passes/layout/disjoint_utils.py | 22 +++++--- .../routing/algorithms/token_swapper.py | 11 ++-- .../passes/routing/algorithms/util.py | 11 ++-- .../commuting_2q_block.py | 8 +-- .../commuting_2q_gate_router.py | 38 ++++++++------ .../swap_strategy.py | 32 ++++++------ .../passes/routing/layout_transformation.py | 10 ++-- .../scheduling/alignments/align_measures.py | 21 +++++--- .../scheduling/alignments/reschedule.py | 6 +-- .../passes/scheduling/padding/base_padding.py | 7 +-- .../padding/dynamical_decoupling.py | 14 ++--- .../passes/synthesis/unitary_synthesis.py | 16 +++--- qiskit/transpiler/passmanager.py | 52 +++++++++---------- .../transpiler/preset_passmanagers/level0.py | 3 +- .../transpiler/preset_passmanagers/level1.py | 13 ++--- .../transpiler/preset_passmanagers/level2.py | 13 ++--- .../transpiler/preset_passmanagers/level3.py | 16 +++--- qiskit/transpiler/runningpassmanager.py | 10 ++-- .../transpiler/synthesis/aqc/approximate.py | 8 +-- .../synthesis/aqc/cnot_unit_circuit.py | 4 +- .../synthesis/aqc/cnot_unit_objective.py | 18 ++++--- .../aqc/fast_gradient/fast_grad_utils.py | 4 +- .../synthesis/aqc/fast_gradient/layer.py | 10 ++-- qiskit/transpiler/target.py | 42 +++++++-------- 27 files changed, 239 insertions(+), 204 deletions(-) diff --git a/qiskit/transpiler/instruction_durations.py b/qiskit/transpiler/instruction_durations.py index a56a39b97dd..d1611840a04 100644 --- a/qiskit/transpiler/instruction_durations.py +++ b/qiskit/transpiler/instruction_durations.py @@ -11,8 +11,10 @@ # that they have been altered from the originals. """Durations of instructions, one of transpiler configurations.""" -from typing import Optional, List, Tuple, Union, Iterable, Set +from __future__ import annotations +from typing import Optional, List, Tuple, Union, Iterable +import qiskit.circuit from qiskit.circuit import Barrier, Delay from qiskit.circuit import Instruction, Qubit, ParameterExpression from qiskit.circuit.duration import duration_in_dt @@ -22,7 +24,7 @@ from qiskit.utils.units import apply_prefix -def _is_deprecated_qubits_argument(qubits: Union[int, List[int], Qubit, List[Qubit]]) -> bool: +def _is_deprecated_qubits_argument(qubits: Union[int, list[int], Qubit, list[Qubit]]) -> bool: if isinstance(qubits, (int, Qubit)): qubits = [qubits] return isinstance(qubits[0], Qubit) @@ -41,11 +43,13 @@ class InstructionDurations: """ def __init__( - self, instruction_durations: Optional["InstructionDurationsType"] = None, dt: float = None + self, instruction_durations: "InstructionDurationsType" | None = None, dt: float = None ): - self.duration_by_name = {} - self.duration_by_name_qubits = {} - self.duration_by_name_qubits_params = {} + self.duration_by_name: dict[str, tuple[float, str]] = {} + self.duration_by_name_qubits: dict[tuple[str, tuple[int, ...]], tuple[float, str]] = {} + self.duration_by_name_qubits_params: dict[ + tuple[str, tuple[int, ...], tuple[float, ...]], tuple[float, str] + ] = {} self.dt = dt if instruction_durations: self.update(instruction_durations) @@ -99,7 +103,7 @@ def from_backend(cls, backend: Backend): return InstructionDurations(instruction_durations, dt=dt) - def update(self, inst_durations: Optional["InstructionDurationsType"], dt: float = None): + def update(self, inst_durations: "InstructionDurationsType" | None, dt: float = None): """Update self with inst_durations (inst_durations overwrite self). Args: @@ -177,10 +181,10 @@ def update(self, inst_durations: Optional["InstructionDurationsType"], dt: float ) def get( self, - inst: Union[str, Instruction], - qubits: Union[int, List[int], Qubit, List[Qubit]], + inst: str | qiskit.circuit.Instruction, + qubits: int | list[int] | Qubit | list[Qubit] | list[int | Qubit], unit: str = "dt", - parameters: Optional[List[float]] = None, + parameters: list[float] | None = None, ) -> float: """Get the duration of the instruction with the name, qubits, and parameters. @@ -224,9 +228,9 @@ def get( def _get( self, name: str, - qubits: List[int], + qubits: list[int], to_unit: str, - parameters: Optional[Iterable[float]] = None, + parameters: Iterable[float] | None = None, ) -> float: """Get the duration of the instruction with the name, qubits, and parameters.""" if name == "barrier": @@ -270,7 +274,7 @@ def _convert_unit(self, duration: float, from_unit: str, to_unit: str) -> float: else: raise TranspilerError(f"Conversion from '{from_unit}' to '{to_unit}' is not supported") - def units_used(self) -> Set[str]: + def units_used(self) -> set[str]: """Get the set of all units used in this instruction durations. Returns: diff --git a/qiskit/transpiler/layout.py b/qiskit/transpiler/layout.py index 56f8f7f5095..0e318e6a8db 100644 --- a/qiskit/transpiler/layout.py +++ b/qiskit/transpiler/layout.py @@ -17,9 +17,8 @@ Virtual (qu)bits are tuples, e.g. `(QuantumRegister(3, 'qr'), 2)` or simply `qr[2]`. Physical (qu)bits are integers. """ - +from __future__ import annotations from dataclasses import dataclass -from typing import Dict, Optional from qiskit.circuit.quantumregister import Qubit, QuantumRegister from qiskit.transpiler.exceptions import LayoutError @@ -262,7 +261,7 @@ def combine_into_edge_map(self, another_layout): return edge_map - def reorder_bits(self, bits): + def reorder_bits(self, bits) -> list[int]: """Given an ordered list of bits, reorder them according to this layout. The list of bits must exactly match the virtual bits in this layout. @@ -401,5 +400,5 @@ class TranspileLayout: """ initial_layout: Layout - input_qubit_mapping: Dict[Qubit, int] - final_layout: Optional[Layout] = None + input_qubit_mapping: dict[Qubit, int] + final_layout: Layout | None = None diff --git a/qiskit/transpiler/passes/calibration/rzx_builder.py b/qiskit/transpiler/passes/calibration/rzx_builder.py index 0b519a5a375..97799994fd0 100644 --- a/qiskit/transpiler/passes/calibration/rzx_builder.py +++ b/qiskit/transpiler/passes/calibration/rzx_builder.py @@ -11,11 +11,12 @@ # that they have been altered from the originals. """RZX calibration builders.""" +from __future__ import annotations import enum import warnings +from collections.abc import Sequence from math import pi, erf -from typing import List, Tuple, Union import numpy as np from qiskit.circuit import Instruction as CircuitInst @@ -68,7 +69,7 @@ class RZXCalibrationBuilder(CalibrationBuilder): def __init__( self, instruction_schedule_map: InstructionScheduleMap = None, - qubit_channel_mapping: List[List[str]] = None, + qubit_channel_mapping: list[list[str]] = None, verbose: bool = True, target: Target = None, ): @@ -97,7 +98,7 @@ def __init__( if self._inst_map is None: raise QiskitError("Calibrations can only be added to Pulse-enabled backends") - def supported(self, node_op: CircuitInst, qubits: List) -> bool: + def supported(self, node_op: CircuitInst, qubits: list) -> bool: """Determine if a given node supports the calibration. Args: @@ -166,7 +167,7 @@ def rescale_cr_inst(instruction: Play, theta: float, sample_mult: int = 16) -> i return round_duration - def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule, ScheduleBlock]: + def get_calibration(self, node_op: CircuitInst, qubits: list) -> Schedule | ScheduleBlock: """Builds the calibration schedule for the RZXGate(theta) with echos. Args: @@ -260,7 +261,7 @@ class RZXCalibrationBuilderNoEcho(RZXCalibrationBuilder): of the CX gate. """ - def get_calibration(self, node_op: CircuitInst, qubits: List) -> Union[Schedule, ScheduleBlock]: + def get_calibration(self, node_op: CircuitInst, qubits: list) -> Schedule | ScheduleBlock: """Builds the calibration schedule for the RZXGate(theta) without echos. Args: @@ -336,8 +337,8 @@ def _filter_comp_tone(time_inst_tup): def _check_calibration_type( - inst_sched_map: InstructionScheduleMap, qubits: List[int] -) -> Tuple[CRCalType, List[Play], List[Play]]: + inst_sched_map: InstructionScheduleMap, qubits: Sequence[int] +) -> tuple[CRCalType, list[Play], list[Play]]: """A helper function to check type of CR calibration. Args: diff --git a/qiskit/transpiler/passes/layout/disjoint_utils.py b/qiskit/transpiler/passes/layout/disjoint_utils.py index 2afef62955a..4a7975200fb 100644 --- a/qiskit/transpiler/passes/layout/disjoint_utils.py +++ b/qiskit/transpiler/passes/layout/disjoint_utils.py @@ -11,12 +11,13 @@ # that they have been altered from the originals. """This module contains common utils for disjoint coupling maps.""" - +from __future__ import annotations from collections import defaultdict from typing import List, Callable, TypeVar, Dict, Union import uuid import rustworkx as rx +from qiskit.dagcircuit import DAGOpNode from qiskit.circuit import Qubit, Barrier, Clbit from qiskit.dagcircuit.dagcircuit import DAGCircuit @@ -65,7 +66,7 @@ def run_pass_over_connected_components( for qreg in dag.qregs: out_dag.add_qreg(qreg) for creg in dag.cregs: - out_dag.add_cregs(creg) + out_dag.add_creg(creg) out_dag.compose(dag, qubits=dag.qubits, clbits=dag.clbits) out_component_pairs.append((out_dag, cmap_components[cmap_index])) res = [run_func(out_dag, cmap) for out_dag, cmap in out_component_pairs] @@ -119,7 +120,7 @@ def split_barriers(dag: DAGCircuit): def combine_barriers(dag: DAGCircuit, retain_uuid: bool = True): """Mutate input dag to combine barriers with UUID labels into a single barrier.""" qubit_indices = {bit: index for index, bit in enumerate(dag.qubits)} - uuid_map = {} + uuid_map: dict[uuid.UUID, DAGOpNode] = {} for node in dag.op_nodes(Barrier): if isinstance(node.op.label, uuid.UUID): barrier_uuid = node.op.label @@ -139,9 +140,18 @@ def combine_barriers(dag: DAGCircuit, retain_uuid: bool = True): def require_layout_isolated_to_component( dag: DAGCircuit, components_source: Union[Target, CouplingMap] -) -> bool: - """Check that the layout of the dag does not require connectivity across connected components - in the CouplingMap""" +): + """ + Check that the layout of the dag does not require connectivity across connected components + in the CouplingMap + + Args: + dag: DAGCircuit to check. + components_source: Target to check against. + + Raises: + TranspilerError: Chosen layout is not valid for the target disjoint connectivity. + """ if isinstance(components_source, Target): coupling_map = components_source.build_coupling_map(filter_idle_qubits=True) else: diff --git a/qiskit/transpiler/passes/routing/algorithms/token_swapper.py b/qiskit/transpiler/passes/routing/algorithms/token_swapper.py index 63dd1e1f0d4..ffb995018fa 100644 --- a/qiskit/transpiler/passes/routing/algorithms/token_swapper.py +++ b/qiskit/transpiler/passes/routing/algorithms/token_swapper.py @@ -26,9 +26,10 @@ """Permutation algorithms for general graphs.""" +from __future__ import annotations import copy import logging -from typing import Iterator, Mapping, MutableMapping, MutableSet, List, Iterable, Union +from collections.abc import Mapping, MutableSet, MutableMapping, Iterator, Iterable import numpy as np import rustworkx as rx @@ -46,9 +47,7 @@ class ApproximateTokenSwapper: Internally caches the graph and associated datastructures for re-use. """ - def __init__( - self, graph: rx.PyGraph, seed: Union[int, np.random.Generator, None] = None - ) -> None: + def __init__(self, graph: rx.PyGraph, seed: int | np.random.Generator | None = None) -> None: """Construct an ApproximateTokenSwapping object. Args: @@ -80,7 +79,7 @@ def permutation_circuit(self, permutation: Permutation, trials: int = 4) -> Perm parallel_swaps = [[swap] for swap in sequential_swaps] return permutation_circuit(parallel_swaps) - def map(self, mapping: Mapping[int, int], trials: int = 4) -> List[Swap[int]]: + def map(self, mapping: Mapping[int, int], trials: int = 4) -> list[Swap[int]]: """Perform an approximately optimal Token Swapping algorithm to implement the permutation. Supports partial mappings (i.e. not-permutations) for graphs with missing tokens. @@ -113,7 +112,7 @@ def map(self, mapping: Mapping[int, int], trials: int = 4) -> List[Swap[int]]: ) # Once we find a zero solution we stop. - def take_until_zero(results: Iterable[List[int]]) -> Iterator[List[int]]: + def take_until_zero(results: Iterable[list[Swap[int]]]) -> Iterable[list[Swap[int]]]: """Take results until one is emitted of length zero (and also emit that).""" for result in results: yield result diff --git a/qiskit/transpiler/passes/routing/algorithms/util.py b/qiskit/transpiler/passes/routing/algorithms/util.py index 096b9fd11f0..930c835603d 100644 --- a/qiskit/transpiler/passes/routing/algorithms/util.py +++ b/qiskit/transpiler/passes/routing/algorithms/util.py @@ -26,7 +26,10 @@ """Utility functions shared between permutation functionality.""" -from typing import List, TypeVar, Iterable, MutableMapping +from __future__ import annotations + +from collections.abc import Iterable +from typing import TypeVar, MutableMapping from qiskit.circuit import QuantumRegister from qiskit.dagcircuit import DAGCircuit @@ -53,8 +56,8 @@ def swap_permutation( for swap_step in swaps: for sw1, sw2 in swap_step: # Take into account non-existent keys. - val1 = None # type: Optional[_V] - val2 = None # type: Optional[_V] + val1: _V | None = None + val2: _V | None = None if allow_missing_keys: val1 = mapping.pop(sw1, None) val2 = mapping.pop(sw2, None) @@ -68,7 +71,7 @@ def swap_permutation( mapping[sw1] = val2 -def permutation_circuit(swaps: Iterable[List[Swap[_V]]]) -> PermutationCircuit: +def permutation_circuit(swaps: Iterable[list[Swap[_V]]]) -> PermutationCircuit: """Produce a circuit description of a list of swaps. With a given permutation and permuter you can compute the swaps using the permuter function then feed it into this circuit function to obtain a circuit description. diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_block.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_block.py index d121b70baa3..bc3d7f794bf 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_block.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_block.py @@ -11,11 +11,12 @@ # that they have been altered from the originals. """A gate made of commuting two-qubit gates.""" +from __future__ import annotations -from typing import Iterable +from collections.abc import Iterable from qiskit.exceptions import QiskitError -from qiskit.circuit import Gate +from qiskit.circuit import Gate, Qubit, Clbit from qiskit.dagcircuit import DAGOpNode @@ -34,7 +35,8 @@ def __init__(self, node_block: Iterable[DAGOpNode]) -> None: Raises: QiskitError: If the nodes in the node block do not apply to two-qubits. """ - qubits, cbits = set(), set() + qubits: set[Qubit] = set() + cbits: set[Clbit] = set() for node in node_block: if len(node.qargs) != 2: raise QiskitError(f"Node {node.name} does not apply to two-qubits.") diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py index 11919bf18b6..1b0c734bd0b 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/commuting_2q_gate_router.py @@ -11,11 +11,10 @@ # that they have been altered from the originals. """A swap strategy pass for blocks of commuting gates.""" - +from __future__ import annotations from collections import defaultdict -from typing import Dict, List, Optional, Tuple -from qiskit.circuit import Gate, QuantumCircuit +from qiskit.circuit import Gate, QuantumCircuit, Qubit from qiskit.converters import circuit_to_dag from qiskit.dagcircuit import DAGCircuit, DAGOpNode from qiskit.transpiler import TransformationPass, Layout, TranspilerError @@ -105,8 +104,8 @@ class Commuting2qGateRouter(TransformationPass): def __init__( self, - swap_strategy: Optional[SwapStrategy] = None, - edge_coloring: Optional[Dict[Tuple[int, int], int]] = None, + swap_strategy: SwapStrategy | None = None, + edge_coloring: dict[tuple[int, int], int] | None = None, ) -> None: r""" Args: @@ -127,7 +126,7 @@ def __init__( """ super().__init__() self._swap_strategy = swap_strategy - self._bit_indices = None + self._bit_indices: dict[Qubit, int] | None = None self._edge_coloring = edge_coloring def run(self, dag: DAGCircuit) -> DAGCircuit: @@ -206,7 +205,7 @@ def _compose_non_swap_nodes( """ # Add all the non-swap strategy nodes that we have accumulated up to now. order = layout.reorder_bits(new_dag.qubits) - order_bits = [None] * len(layout) + order_bits: list[int | None] = [None] * len(layout) for idx, val in enumerate(order): order_bits[val] = idx @@ -215,7 +214,7 @@ def _compose_non_swap_nodes( # Re-initialize the node accumulator return new_dag.copy_empty_like() - def _position_in_cmap(self, j: int, k: int, layout: Layout) -> Tuple[int, ...]: + def _position_in_cmap(self, j: int, k: int, layout: Layout) -> tuple[int, ...]: """A helper function to track the movement of virtual qubits through the swaps. Args: @@ -231,7 +230,9 @@ def _position_in_cmap(self, j: int, k: int, layout: Layout) -> Tuple[int, ...]: return bit0, bit1 - def _build_sub_layers(self, current_layer: Dict[tuple, Gate]) -> List[Dict[tuple, Gate]]: + def _build_sub_layers( + self, current_layer: dict[tuple[int, int], Gate] + ) -> list[dict[tuple[int, int], Gate]]: """A helper method to build-up sets of gates to simultaneously apply. This is done with an edge coloring if the ``edge_coloring`` init argument was given or with @@ -256,10 +257,12 @@ def _build_sub_layers(self, current_layer: Dict[tuple, Gate]) -> List[Dict[tuple return self._greedy_build_sub_layers(current_layer) def _edge_coloring_build_sub_layers( - self, current_layer: Dict[tuple, Gate] - ) -> List[Dict[tuple, Gate]]: + self, current_layer: dict[tuple[int, int], Gate] + ) -> list[dict[tuple[int, int], Gate]]: """The edge coloring method of building sub-layers of commuting gates.""" - sub_layers = [{} for _ in set(self._edge_coloring.values())] + sub_layers: list[dict[tuple[int, int], Gate]] = [ + {} for _ in set(self._edge_coloring.values()) + ] for edge, gate in current_layer.items(): color = self._edge_coloring[edge] sub_layers[color][edge] = gate @@ -267,11 +270,14 @@ def _edge_coloring_build_sub_layers( return sub_layers @staticmethod - def _greedy_build_sub_layers(current_layer: Dict[tuple, Gate]) -> List[Dict[tuple, Gate]]: + def _greedy_build_sub_layers( + current_layer: dict[tuple[int, int], Gate] + ) -> list[dict[tuple[int, int], Gate]]: """The greedy method of building sub-layers of commuting gates.""" sub_layers = [] while len(current_layer) > 0: - current_sub_layer, remaining_gates, blocked_vertices = {}, {}, set() + current_sub_layer, remaining_gates = {}, {} + blocked_vertices: set[tuple] = set() for edge, evo_gate in current_layer.items(): if blocked_vertices.isdisjoint(edge): @@ -342,10 +348,10 @@ def swap_decompose( def _make_op_layers( self, dag: DAGCircuit, op: Commuting2qBlock, layout: Layout, swap_strategy: SwapStrategy - ) -> Dict[int, Dict[tuple, Gate]]: + ) -> dict[int, dict[tuple, Gate]]: """Creates layers of two-qubit gates based on the distance in the swap strategy.""" - gate_layers = defaultdict(dict) + gate_layers: dict[int, dict[tuple, Gate]] = defaultdict(dict) for node in op.node_block: edge = (self._bit_indices[node.qargs[0]], self._bit_indices[node.qargs[1]]) diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py index a3ec6d4b565..72b62dc49a8 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/swap_strategy.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Defines a swap strategy class.""" - -from typing import Any, List, Optional, Set, Tuple +from __future__ import annotations +from typing import Any import copy import numpy as np @@ -48,7 +48,7 @@ class SwapStrategy: """ def __init__( - self, coupling_map: CouplingMap, swap_layers: Tuple[Tuple[Tuple[int, int], ...], ...] + self, coupling_map: CouplingMap, swap_layers: tuple[tuple[tuple[int, int], ...], ...] ) -> None: """ Args: @@ -66,9 +66,9 @@ def __init__( self._coupling_map = coupling_map self._num_vertices = coupling_map.size() self._swap_layers = swap_layers - self._distance_matrix = None - self._possible_edges = None - self._missing_couplings = None + self._distance_matrix: np.ndarray | None = None + self._possible_edges: set[tuple[int, int]] | None = None + self._missing_couplings: set[tuple[int, int]] | None = None self._inverse_composed_permutation = {0: list(range(self._num_vertices))} edge_set = set(self._coupling_map.get_edges()) @@ -86,7 +86,7 @@ def __init__( raise QiskitError(f"The {i}th swap layer contains a qubit with multiple swaps.") @classmethod - def from_line(cls, line: List[int], num_swap_layers: Optional[int] = None) -> "SwapStrategy": + def from_line(cls, line: list[int], num_swap_layers: int | None = None) -> "SwapStrategy": """Creates a swap strategy for a line graph with the specified number of SWAP layers. This SWAP strategy will use the full line if instructed to do so (i.e. num_variables @@ -151,7 +151,7 @@ def __repr__(self) -> str: return description - def swap_layer(self, idx: int) -> List[Tuple[int, int]]: + def swap_layer(self, idx: int) -> list[tuple[int, int]]: """Return the layer of swaps at the given index. Args: @@ -164,7 +164,7 @@ def swap_layer(self, idx: int) -> List[Tuple[int, int]]: return list(self._swap_layers[idx]) @property - def distance_matrix(self) -> np.array: + def distance_matrix(self) -> np.ndarray: """A matrix describing when qubits become adjacent in the swap strategy. Returns: @@ -190,7 +190,7 @@ def distance_matrix(self) -> np.array: return self._distance_matrix - def new_connections(self, idx: int) -> List[Set[int]]: + def new_connections(self, idx: int) -> list[set[int]]: """ Returns the new connections obtained after applying the SWAP layer specified by idx, i.e. a list of qubit pairs that are adjacent to one another after idx steps of the SWAP strategy. @@ -209,7 +209,7 @@ def new_connections(self, idx: int) -> List[Set[int]]: connections.append({i, j}) return connections - def _build_edges(self) -> Set[Tuple[int, int]]: + def _build_edges(self) -> set[tuple[int, int]]: """Build the possible edges that the swap strategy accommodates.""" possible_edges = set() @@ -221,7 +221,7 @@ def _build_edges(self) -> Set[Tuple[int, int]]: return possible_edges @property - def possible_edges(self) -> Set[Tuple[int, int]]: + def possible_edges(self) -> set[tuple[int, int]]: """Return the qubit connections that can be generated. Returns: @@ -233,7 +233,7 @@ def possible_edges(self) -> Set[Tuple[int, int]]: return self._possible_edges @property - def missing_couplings(self) -> Set[Tuple[int, int]]: + def missing_couplings(self) -> set[tuple[int, int]]: """Return the set of couplings that cannot be reached. Returns: @@ -262,8 +262,8 @@ def swapped_coupling_map(self, idx: int) -> CouplingMap: return CouplingMap(couplinglist=edges) def apply_swap_layer( - self, list_to_swap: List[Any], idx: int, inplace: bool = False - ) -> List[Any]: + self, list_to_swap: list[Any], idx: int, inplace: bool = False + ) -> list[Any]: """Permute the elements of ``list_to_swap`` based on layer indexed by ``idx``. Args: @@ -285,7 +285,7 @@ def apply_swap_layer( return x - def inverse_composed_permutation(self, idx: int) -> List[int]: + def inverse_composed_permutation(self, idx: int) -> list[int]: """ Returns the inversed composed permutation of all swap layers applied up to layer ``idx``. Permutations are represented by list of integers where the ith element diff --git a/qiskit/transpiler/passes/routing/layout_transformation.py b/qiskit/transpiler/passes/routing/layout_transformation.py index 3d67e0ffb57..218e6ca958d 100644 --- a/qiskit/transpiler/passes/routing/layout_transformation.py +++ b/qiskit/transpiler/passes/routing/layout_transformation.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """Map (with minimum effort) a DAGCircuit onto a `coupling_map` adding swap gates.""" -from typing import Union +from __future__ import annotations import numpy as np @@ -31,10 +31,10 @@ class LayoutTransformation(TransformationPass): def __init__( self, - coupling_map: Union[CouplingMap, Target, None], - from_layout: Union[Layout, str], - to_layout: Union[Layout, str], - seed: Union[int, np.random.default_rng] = None, + coupling_map: CouplingMap | Target | None, + from_layout: Layout | str, + to_layout: Layout | str, + seed: int | np.random.Generator | None = None, trials=4, ): """LayoutTransformation initializer. diff --git a/qiskit/transpiler/passes/scheduling/alignments/align_measures.py b/qiskit/transpiler/passes/scheduling/alignments/align_measures.py index 9a1b3c94c5e..164e9557fbb 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/align_measures.py +++ b/qiskit/transpiler/passes/scheduling/alignments/align_measures.py @@ -11,13 +11,16 @@ # that they have been altered from the originals. """Align measurement instructions.""" +from __future__ import annotations import itertools import warnings from collections import defaultdict -from typing import List, Union +from collections.abc import Iterable +from typing import Type + +from qiskit.circuit.quantumcircuit import ClbitSpecifier, QubitSpecifier from qiskit.circuit.delay import Delay -from qiskit.circuit.instruction import Instruction from qiskit.circuit.measure import Measure from qiskit.circuit.parameterexpression import ParameterExpression from qiskit.dagcircuit import DAGCircuit @@ -141,12 +144,14 @@ def run(self, dag: DAGCircuit): # * pad_with_delay is called only with non-delay node to avoid consecutive delay new_dag = dag.copy_empty_like() - qubit_time_available = defaultdict(int) # to track op start time - qubit_stop_times = defaultdict(int) # to track delay start time for padding - clbit_readable = defaultdict(int) - clbit_writeable = defaultdict(int) + qubit_time_available: dict[QubitSpecifier, int] = defaultdict(int) # to track op start time + qubit_stop_times: dict[QubitSpecifier, int] = defaultdict( + int + ) # to track delay start time for padding + clbit_readable: dict[ClbitSpecifier, int] = defaultdict(int) + clbit_writeable: dict[ClbitSpecifier, int] = defaultdict(int) - def pad_with_delays(qubits: List[int], until, unit) -> None: + def pad_with_delays(qubits: Iterable[QubitSpecifier], until, unit) -> None: """Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``.""" for q in qubits: if qubit_stop_times[q] < until: @@ -206,7 +211,7 @@ def pad_with_delays(qubits: List[int], until, unit) -> None: def _check_alignment_required( dag: DAGCircuit, alignment: int, - instructions: Union[Instruction, List[Instruction]], + instructions: Type | list[Type], ) -> bool: """Check DAG nodes and return a boolean representing if instruction scheduling is necessary. diff --git a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py index 325cb4c9654..b53d0f864ce 100644 --- a/qiskit/transpiler/passes/scheduling/alignments/reschedule.py +++ b/qiskit/transpiler/passes/scheduling/alignments/reschedule.py @@ -11,8 +11,8 @@ # that they have been altered from the originals. """Rescheduler pass to adjust node start times.""" - -from typing import List +from __future__ import annotations +from collections.abc import Generator from qiskit.circuit.gate import Gate from qiskit.circuit.delay import Delay @@ -79,7 +79,7 @@ def __init__( self.pulse_align = pulse_alignment @classmethod - def _get_next_gate(cls, dag: DAGCircuit, node: DAGOpNode) -> List[DAGOpNode]: + def _get_next_gate(cls, dag: DAGCircuit, node: DAGOpNode) -> Generator[DAGOpNode, None, None]: """Get next non-delay nodes. Args: diff --git a/qiskit/transpiler/passes/scheduling/padding/base_padding.py b/qiskit/transpiler/passes/scheduling/padding/base_padding.py index 5fb25790cfb..16d58dbae25 100644 --- a/qiskit/transpiler/passes/scheduling/padding/base_padding.py +++ b/qiskit/transpiler/passes/scheduling/padding/base_padding.py @@ -11,9 +11,10 @@ # that they have been altered from the originals. """Padding pass to fill empty timeslot.""" +from __future__ import annotations +from collections.abc import Iterable import logging -from typing import List, Optional, Union from qiskit.circuit import Qubit, Clbit, Instruction from qiskit.circuit.delay import Delay @@ -195,8 +196,8 @@ def _apply_scheduled_op( dag: DAGCircuit, t_start: int, oper: Instruction, - qubits: Union[Qubit, List[Qubit]], - clbits: Optional[Union[Clbit, List[Clbit]]] = None, + qubits: Qubit | Iterable[Qubit], + clbits: Clbit | Iterable[Clbit] | None = None, ): """Add new operation to DAG with scheduled information. diff --git a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py index e3923ed8b26..bbb1b39bbd7 100644 --- a/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py +++ b/qiskit/transpiler/passes/scheduling/padding/dynamical_decoupling.py @@ -11,11 +11,11 @@ # that they have been altered from the originals. """Dynamical Decoupling insertion pass.""" +from __future__ import annotations import logging -from typing import List, Optional - import numpy as np + from qiskit.circuit import Qubit, Gate from qiskit.circuit.delay import Delay from qiskit.circuit.library.standard_gates import IGate, UGate, U3Gate @@ -106,9 +106,9 @@ def uhrig_pulse_location(k): def __init__( self, durations: InstructionDurations = None, - dd_sequence: List[Gate] = None, - qubits: Optional[List[int]] = None, - spacing: Optional[List[float]] = None, + dd_sequence: list[Gate] = None, + qubits: list[int] | None = None, + spacing: list[float] | None = None, skip_reset_qubits: bool = True, pulse_alignment: int = 1, extra_slack_distribution: str = "middle", @@ -166,8 +166,8 @@ def __init__( self._spacing = spacing self._extra_slack_distribution = extra_slack_distribution - self._no_dd_qubits = set() - self._dd_sequence_lengths = {} + self._no_dd_qubits: set[int] = set() + self._dd_sequence_lengths: dict[Qubit, list[int]] = {} self._sequence_phase = 0 if target is not None: self._durations = target.durations() diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index 881d3cc1055..e4555c7fba0 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -11,9 +11,9 @@ # that they have been altered from the originals. """Synthesize UnitaryGates.""" - +from __future__ import annotations from math import pi, inf, isclose -from typing import List, Union, Optional +from typing import Any from copy import deepcopy from itertools import product from functools import partial @@ -265,13 +265,13 @@ class UnitarySynthesis(TransformationPass): def __init__( self, - basis_gates: List[str] = None, - approximation_degree: Optional[float] = 1.0, + basis_gates: list[str] = None, + approximation_degree: float | None = 1.0, coupling_map: CouplingMap = None, backend_props: BackendProperties = None, - pulse_optimize: Union[bool, None] = None, - natural_direction: Union[bool, None] = None, - synth_gates: Union[List[str], None] = None, + pulse_optimize: bool | None = None, + natural_direction: bool | None = None, + synth_gates: list[str] | None = None, method: str = "default", min_qubits: int = None, plugin_config: dict = None, @@ -384,7 +384,7 @@ def run(self, dag: DAGCircuit) -> DAGCircuit: plugin_method = self.plugins.ext_plugins[self.method].obj else: plugin_method = DefaultUnitarySynthesis() - plugin_kwargs = {"config": self._plugin_config} + plugin_kwargs: dict[str, Any] = {"config": self._plugin_config} _gate_lengths = _gate_errors = None _gate_lengths_by_qubit = _gate_errors_by_qubit = None diff --git a/qiskit/transpiler/passmanager.py b/qiskit/transpiler/passmanager.py index eb339aec901..03019e872ff 100644 --- a/qiskit/transpiler/passmanager.py +++ b/qiskit/transpiler/passmanager.py @@ -11,10 +11,11 @@ # that they have been altered from the originals. """Manager for a set of Passes and their scheduling during transpilation.""" - +from __future__ import annotations import io import re -from typing import Union, List, Tuple, Callable, Dict, Any, Optional, Iterator, Iterable, TypeVar +from collections.abc import Iterator, Iterable, Callable, Sequence +from typing import Union, List, Any import dill @@ -24,14 +25,13 @@ from .exceptions import TranspilerError from .runningpassmanager import RunningPassManager, FlowController - -_CircuitsT = TypeVar("_CircuitsT", bound=Union[List[QuantumCircuit], QuantumCircuit]) +_CircuitsT = Union[List[QuantumCircuit], QuantumCircuit] class PassManager: """Manager for a set of Passes and their scheduling during transpilation.""" - def __init__(self, passes: Union[BasePass, List[BasePass]] = None, max_iteration: int = 1000): + def __init__(self, passes: BasePass | list[BasePass] | None = None, max_iteration: int = 1000): """Initialize an empty `PassManager` object (with no passes scheduled). Args: @@ -43,7 +43,7 @@ def __init__(self, passes: Union[BasePass, List[BasePass]] = None, max_iteration # the pass manager's schedule of passes, including any control-flow. # Populated via PassManager.append(). - self._pass_sets = [] + self._pass_sets: list[dict[str, Any]] = [] if passes is not None: self.append(passes) self.max_iteration = max_iteration @@ -51,7 +51,7 @@ def __init__(self, passes: Union[BasePass, List[BasePass]] = None, max_iteration def append( self, - passes: Union[BasePass, List[BasePass]], + passes: BasePass | Sequence[BasePass | FlowController], max_iteration: int = None, **flow_controller_conditions: Any, ) -> None: @@ -84,7 +84,7 @@ def append( def replace( self, index: int, - passes: Union[BasePass, List[BasePass]], + passes: BasePass | list[BasePass], max_iteration: int = None, **flow_controller_conditions: Any, ) -> None: @@ -164,8 +164,8 @@ def __add__(self, other): @staticmethod def _normalize_passes( - passes: Union[BasePass, List[BasePass], FlowController] - ) -> List[BasePass]: + passes: BasePass | Sequence[BasePass | FlowController] | FlowController, + ) -> Sequence[BasePass | FlowController] | FlowController: if isinstance(passes, FlowController): return passes if isinstance(passes, BasePass): @@ -187,8 +187,8 @@ def _normalize_passes( def run( self, circuits: _CircuitsT, - output_name: Optional[str] = None, - callback: Optional[Callable] = None, + output_name: str | None = None, + callback: Callable | None = None, ) -> _CircuitsT: """Run all the passes on the specified ``circuits``. @@ -249,8 +249,8 @@ def _in_parallel(circuit, pm_dill=None) -> QuantumCircuit: def _run_several_circuits( self, circuits: List[QuantumCircuit], - output_name: Optional[str] = None, - callback: Optional[Callable] = None, + output_name: str | None = None, + callback: Callable | None = None, ) -> List[QuantumCircuit]: """Run all the passes on the specified ``circuits``. @@ -274,8 +274,8 @@ def _run_several_circuits( def _run_single_circuit( self, circuit: QuantumCircuit, - output_name: Optional[str] = None, - callback: Optional[Callable] = None, + output_name: str | None = None, + callback: Callable | None = None, ) -> QuantumCircuit: """Run all the passes on a ``circuit``. @@ -319,7 +319,7 @@ def draw(self, filename=None, style=None, raw=False): return pass_manager_drawer(self, filename=filename, style=style, raw=raw) - def passes(self) -> List[Dict[str, BasePass]]: + def passes(self) -> list[dict[str, BasePass]]: """Return a list structure of the appended passes and its options. Returns: @@ -400,7 +400,7 @@ class StagedPassManager(PassManager): r"\s|\+|\-|\*|\/|\\|\%|\<|\>|\@|\!|\~|\^|\&|\:|\[|\]|\{|\}|\(|\)" ) - def __init__(self, stages: Optional[Iterable[str]] = None, **kwargs) -> None: + def __init__(self, stages: Iterable[str] | None = None, **kwargs) -> None: """Initialize a new StagedPassManager object Args: @@ -448,19 +448,19 @@ def _validate_stages(self, stages: Iterable[str]) -> None: msg.write(f", {invalid_stage}") raise ValueError(msg.getvalue()) - def _validate_init_kwargs(self, kwargs: Dict[str, Any]) -> None: + def _validate_init_kwargs(self, kwargs: dict[str, Any]) -> None: expanded_stages = set(self.expanded_stages) for stage in kwargs.keys(): if stage not in expanded_stages: raise AttributeError(f"{stage} is not a valid stage.") @property - def stages(self) -> Tuple[str, ...]: + def stages(self) -> tuple[str, ...]: """Pass manager stages""" return self._stages # pylint: disable=no-member @property - def expanded_stages(self) -> Tuple[str, ...]: + def expanded_stages(self) -> tuple[str, ...]: """Expanded Pass manager stages including ``pre_`` and ``post_`` phases.""" return self._expanded_stages # pylint: disable=no-member @@ -486,7 +486,7 @@ def __setattr__(self, attr, value): def append( self, - passes: Union[BasePass, List[BasePass]], + passes: BasePass | Sequence[BasePass | FlowController], max_iteration: int = None, **flow_controller_conditions: Any, ) -> None: @@ -495,7 +495,7 @@ def append( def replace( self, index: int, - passes: Union[BasePass, List[BasePass]], + passes: BasePass | list[BasePass], max_iteration: int = None, **flow_controller_conditions: Any, ) -> None: @@ -523,15 +523,15 @@ def _create_running_passmanager(self) -> RunningPassManager: self._update_passmanager() return super()._create_running_passmanager() - def passes(self) -> List[Dict[str, BasePass]]: + def passes(self) -> list[dict[str, BasePass]]: self._update_passmanager() return super().passes() def run( self, circuits: _CircuitsT, - output_name: Optional[str] = None, - callback: Optional[Callable] = None, + output_name: str | None = None, + callback: Callable | None = None, ) -> _CircuitsT: self._update_passmanager() return super().run(circuits, output_name, callback) diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 36b62d2c22f..b0790f1026f 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -14,6 +14,7 @@ Level 0 pass manager: no explicit optimization other than mapping to backend. """ +from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.timing_constraints import TimingConstraints @@ -83,7 +84,7 @@ def _choose_layout_condition(property_set): coupling_map_layout = target if layout_method == "trivial": - _choose_layout = TrivialLayout(coupling_map_layout) + _choose_layout: BasePass = TrivialLayout(coupling_map_layout) elif layout_method == "dense": _choose_layout = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 46045b54ef5..db8b09b716b 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -14,12 +14,13 @@ Level 1 pass manager: light optimization by simple adjacent gate collapsing. """ - +from __future__ import annotations +from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager -from qiskit.transpiler import ConditionalController +from qiskit.transpiler import ConditionalController, FlowController from qiskit.transpiler.passes import CXCancellation from qiskit.transpiler.passes import SetLayout @@ -118,13 +119,13 @@ def _vf2_match_not_found(property_set): else: coupling_map_layout = target - _choose_layout_0 = ( + _choose_layout_0: list[BasePass] = ( [] if pass_manager_config.layout_method else [TrivialLayout(coupling_map_layout), CheckMap(coupling_map_layout)] ) - _choose_layout_1 = ( + _choose_layout_1: list[BasePass] | BasePass = ( [] if pass_manager_config.layout_method else VF2Layout( @@ -138,7 +139,7 @@ def _vf2_match_not_found(property_set): ) if layout_method == "trivial": - _improve_layout = TrivialLayout(coupling_map_layout) + _improve_layout: BasePass = TrivialLayout(coupling_map_layout) elif layout_method == "dense": _improve_layout = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": @@ -275,7 +276,7 @@ def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] # Check if any gate is not in the basis, and if so, run unroll passes - _unroll_if_out_of_basis = [ + _unroll_if_out_of_basis: list[BasePass | FlowController] = [ GatesInBasis(basis_gates, target=target), ConditionalController(unroll, condition=_unroll_condition), ] diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index 900cb2668e9..743018881da 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -15,12 +15,13 @@ Level 2 pass manager: medium optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules. """ - +from __future__ import annotations +from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passmanager import PassManager from qiskit.transpiler.passmanager import StagedPassManager -from qiskit.transpiler import ConditionalController +from qiskit.transpiler import ConditionalController, FlowController from qiskit.transpiler.passes import SetLayout from qiskit.transpiler.passes import VF2Layout @@ -109,7 +110,7 @@ def _vf2_match_not_found(property_set): return False # Try using VF2 layout to find a perfect layout - _choose_layout_0 = ( + _choose_layout_0: list[BasePass] | BasePass = ( [] if pass_manager_config.layout_method else VF2Layout( @@ -128,7 +129,7 @@ def _vf2_match_not_found(property_set): coupling_map_layout = target if layout_method == "trivial": - _choose_layout_1 = TrivialLayout(coupling_map_layout) + _choose_layout_1: BasePass = TrivialLayout(coupling_map_layout) elif layout_method == "dense": _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": @@ -160,7 +161,7 @@ def _vf2_match_not_found(property_set): def _opt_control(property_set): return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"]) - _opt = [ + _opt: list[BasePass] = [ Optimize1qGatesDecomposition(basis=basis_gates, target=target), CommutativeCancellation(basis_gates=basis_gates, target=target), ] @@ -230,7 +231,7 @@ def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] # Check if any gate is not in the basis, and if so, run unroll passes - _unroll_if_out_of_basis = [ + _unroll_if_out_of_basis: list[BasePass | FlowController] = [ GatesInBasis(basis_gates, target=target), ConditionalController(unroll, condition=_unroll_condition), ] diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 57cfa5d0620..7fe64eed521 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -15,8 +15,8 @@ Level 3 pass manager: heavy optimization by noise adaptive qubit mapping and gate cancellation using commutativity rules and unitary synthesis. """ - - +from __future__ import annotations +from qiskit.transpiler.basepasses import BasePass from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.timing_constraints import TimingConstraints from qiskit.transpiler.passmanager import PassManager @@ -41,7 +41,7 @@ from qiskit.transpiler.passes import UnitarySynthesis from qiskit.transpiler.passes import GatesInBasis from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements -from qiskit.transpiler.runningpassmanager import ConditionalController +from qiskit.transpiler.runningpassmanager import ConditionalController, FlowController from qiskit.transpiler.preset_passmanagers import common from qiskit.transpiler.passes.layout.vf2_layout import VF2LayoutStopReason from qiskit.transpiler.preset_passmanagers.plugin import ( @@ -115,7 +115,7 @@ def _vf2_match_not_found(property_set): return False # 2a. If layout method is not set, first try VF2Layout - _choose_layout_0 = ( + _choose_layout_0: list[BasePass] | BasePass = ( [] if pass_manager_config.layout_method else VF2Layout( @@ -135,7 +135,7 @@ def _vf2_match_not_found(property_set): # 2b. if VF2 didn't converge on a solution use layout_method (dense). if layout_method == "trivial": - _choose_layout_1 = TrivialLayout(coupling_map_layout) + _choose_layout_1: BasePass = TrivialLayout(coupling_map_layout) elif layout_method == "dense": _choose_layout_1 = DenseLayout(coupling_map, backend_properties, target=target) elif layout_method == "noise_adaptive": @@ -161,7 +161,7 @@ def _vf2_match_not_found(property_set): # 8. Optimize iteratively until no more change in depth. Removes useless gates # after reset and before measure, commutes gates and optimizes contiguous blocks. - _minimum_point_check = [ + _minimum_point_check: list[BasePass | FlowController] = [ Depth(recurse=True), Size(recurse=True), MinimumPoint(["depth", "size"], "optimization_loop"), @@ -170,7 +170,7 @@ def _vf2_match_not_found(property_set): def _opt_control(property_set): return not property_set["optimization_loop_minimum_point"] - _opt = [ + _opt: list[BasePass | FlowController] = [ Collect2qBlocks(), ConsolidateBlocks( basis_gates=basis_gates, target=target, approximation_degree=approximation_degree @@ -257,7 +257,7 @@ def _unroll_condition(property_set): return not property_set["all_gates_in_basis"] # Check if any gate is not in the basis, and if so, run unroll passes - _unroll_if_out_of_basis = [ + _unroll_if_out_of_basis: list[BasePass | FlowController] = [ GatesInBasis(basis_gates, target=target), ConditionalController(unroll, condition=_unroll_condition), ] diff --git a/qiskit/transpiler/runningpassmanager.py b/qiskit/transpiler/runningpassmanager.py index a89427af4ba..6290ed4d52b 100644 --- a/qiskit/transpiler/runningpassmanager.py +++ b/qiskit/transpiler/runningpassmanager.py @@ -12,7 +12,7 @@ """RunningPassManager class for the transpiler. This object holds the state of a pass manager during running-time.""" - +from __future__ import annotations from functools import partial from collections import OrderedDict import logging @@ -58,11 +58,11 @@ def __init__(self, max_iteration): self.count = 0 - def append(self, passes, **flow_controller_conditions): + def append(self, passes: list[BasePass], **flow_controller_conditions): """Append a Pass to the schedule of passes. Args: - passes (list[BasePass]): passes to be added to schedule + passes (list[TBasePass]): passes to be added to schedule flow_controller_conditions (kwargs): See add_flow_controller(): Dictionary of control flow plugins. Default: @@ -308,11 +308,11 @@ def remove_flow_controller(cls, name): del cls.registered_controllers[name] @classmethod - def controller_factory(cls, passes, options, **partial_controller): + def controller_factory(cls, passes: list[BasePass], options, **partial_controller): """Constructs a flow controller based on the partially evaluated controller arguments. Args: - passes (list[BasePass]): passes to add to the flow controller. + passes (list[TBasePass]): passes to add to the flow controller. options (dict): PassManager options. **partial_controller (dict): Partially evaluated controller arguments in the form `{name:partial}` diff --git a/qiskit/transpiler/synthesis/aqc/approximate.py b/qiskit/transpiler/synthesis/aqc/approximate.py index 418d766dc35..6c3f11fb71f 100644 --- a/qiskit/transpiler/synthesis/aqc/approximate.py +++ b/qiskit/transpiler/synthesis/aqc/approximate.py @@ -10,9 +10,9 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. """Base classes for an approximate circuit definition.""" - +from __future__ import annotations from abc import ABC, abstractmethod -from typing import Optional +from typing import Optional, SupportsFloat import numpy as np from qiskit import QuantumCircuit @@ -61,10 +61,10 @@ class ApproximatingObjective(ABC): def __init__(self) -> None: # must be set before optimization - self._target_matrix = None + self._target_matrix: np.ndarray | None = None @abstractmethod - def objective(self, param_values: np.ndarray) -> float: + def objective(self, param_values: np.ndarray) -> SupportsFloat: """ Computes a value of the objective function given a vector of parameter values. diff --git a/qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py b/qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py index 8ece18d47c4..6973ae55d72 100644 --- a/qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py +++ b/qiskit/transpiler/synthesis/aqc/cnot_unit_circuit.py @@ -13,7 +13,7 @@ This is the Parametric Circuit class: anything that you need for a circuit to be parametrized and used for approximate compiling optimization. """ - +from __future__ import annotations from typing import Optional import numpy as np @@ -53,7 +53,7 @@ def __init__( self._tol = tol # Thetas to be optimized by the AQC algorithm - self._thetas = None + self._thetas: np.ndarray | None = None @property def thetas(self) -> np.ndarray: diff --git a/qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py b/qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py index 46ee7ad45a6..b8bcd6ea9ab 100644 --- a/qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py +++ b/qiskit/transpiler/synthesis/aqc/cnot_unit_objective.py @@ -13,6 +13,8 @@ A definition of the approximate circuit compilation optimization problem based on CNOT unit definition. """ +from __future__ import annotations +import typing from abc import ABC import numpy as np @@ -68,13 +70,13 @@ def __init__(self, num_qubits: int, cnots: np.ndarray) -> None: super().__init__(num_qubits, cnots) # last objective computations to be re-used by gradient - self._last_thetas = None - self._cnot_right_collection = None - self._cnot_left_collection = None - self._rotation_matrix = None - self._cnot_matrix = None + self._last_thetas: np.ndarray | None = None + self._cnot_right_collection: np.ndarray | None = None + self._cnot_left_collection: np.ndarray | None = None + self._rotation_matrix: int | np.ndarray | None = None + self._cnot_matrix: np.ndarray | None = None - def objective(self, param_values: np.ndarray) -> float: + def objective(self, param_values: np.ndarray) -> typing.SupportsFloat: # rename parameters just to make shorter and make use of our dictionary thetas = param_values n = self._num_qubits @@ -143,7 +145,7 @@ def objective(self, param_values: np.ndarray) -> float: # this is the matrix corresponding to the initial rotations # we start with 1 and kronecker product each qubit's rotations - rotation_matrix = 1 + rotation_matrix: int | np.ndarray = 1 for q in range(n): theta_index = 4 * num_cnots + 3 * q rz0 = rz_matrix(thetas[0 + theta_index]) @@ -268,7 +270,7 @@ def gradient(self, param_values: np.ndarray) -> np.ndarray: # now we compute the partial derivatives in the rotation part # we start with 1 and kronecker product each qubit's rotations for i in range(3 * n): - der_rotation_matrix = 1 + der_rotation_matrix: int | np.ndarray = 1 for q in range(n): theta_index = 4 * num_cnots + 3 * q rz0 = rz_matrix(thetas[0 + theta_index]) diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py b/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py index a3b61042b43..b13c96ea3fe 100644 --- a/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py +++ b/qiskit/transpiler/synthesis/aqc/fast_gradient/fast_grad_utils.py @@ -13,7 +13,7 @@ """ Utility functions in the fast gradient implementation. """ - +from __future__ import annotations from typing import Union import numpy as np @@ -58,7 +58,7 @@ def reverse_bits(x: Union[int, np.ndarray], nbits: int, enable: bool) -> Union[i return x if isinstance(x, int): - res = int(0) + res: int | np.ndarray = int(0) else: x = x.copy() res = np.full_like(x, fill_value=0) diff --git a/qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py b/qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py index eb4a2cf5aa9..c567260fad5 100644 --- a/qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py +++ b/qiskit/transpiler/synthesis/aqc/fast_gradient/layer.py @@ -13,9 +13,9 @@ """ Layer classes for the fast gradient implementation. """ - +from __future__ import annotations from abc import abstractmethod, ABC -from typing import Tuple, Optional +from typing import Optional import numpy as np from .fast_grad_utils import ( bit_permutation_1q, @@ -46,7 +46,7 @@ def set_from_matrix(self, mat: np.ndarray): raise NotImplementedError() @abstractmethod - def get_attr(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: + def get_attr(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ Returns gate matrix, direct and inverse permutations. @@ -91,7 +91,7 @@ def set_from_matrix(self, mat: np.ndarray): """See base class description.""" np.copyto(self._gmat, mat) - def get_attr(self) -> (np.ndarray, np.ndarray, np.ndarray): + def get_attr(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """See base class description.""" return self._gmat, self._perm, self._inv_perm @@ -132,7 +132,7 @@ def set_from_matrix(self, mat: np.ndarray): """See base class description.""" np.copyto(self._gmat, mat) - def get_attr(self) -> (np.ndarray, np.ndarray, np.ndarray): + def get_attr(self) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """See base class description.""" return self._gmat, self._perm, self._inv_perm diff --git a/qiskit/transpiler/target.py b/qiskit/transpiler/target.py index 61999e6e741..08c5dfdfe38 100644 --- a/qiskit/transpiler/target.py +++ b/qiskit/transpiler/target.py @@ -21,7 +21,7 @@ import itertools -from typing import Tuple, Union, Optional, Dict, List, Any +from typing import Any from collections.abc import Mapping from collections import defaultdict import datetime @@ -70,9 +70,9 @@ class InstructionProperties: def __init__( self, - duration: float = None, - error: float = None, - calibration: Union[Schedule, ScheduleBlock, CalibrationEntry] = None, + duration: float | None = None, + error: float | None = None, + calibration: Schedule | ScheduleBlock | CalibrationEntry | None = None, ): """Create a new ``InstructionProperties`` object @@ -83,7 +83,7 @@ def __init__( set of qubits. calibration: The pulse representation of the instruction. """ - self._calibration = None + self._calibration: CalibrationEntry | None = None self.duration = duration self.error = error @@ -122,7 +122,7 @@ def calibration(self): return self._calibration.get_schedule() @calibration.setter - def calibration(self, calibration: Union[Schedule, ScheduleBlock, CalibrationEntry]): + def calibration(self, calibration: Schedule | ScheduleBlock | CalibrationEntry): if isinstance(calibration, (Schedule, ScheduleBlock)): new_entry = ScheduleDef() new_entry.define(calibration, user_provided=True) @@ -860,7 +860,7 @@ def check_obj_params(parameters, obj): def has_calibration( self, operation_name: str, - qargs: Tuple[int, ...], + qargs: tuple[int, ...], ) -> bool: """Return whether the instruction (operation + qubits) defines a calibration. @@ -881,10 +881,10 @@ def has_calibration( def get_calibration( self, operation_name: str, - qargs: Tuple[int, ...], + qargs: tuple[int, ...], *args: ParameterValueType, **kwargs: ParameterValueType, - ) -> Union[Schedule, ScheduleBlock]: + ) -> Schedule | ScheduleBlock: """Get calibrated pulse schedule for the instruction. If calibration is templated with parameters, one can also provide those values @@ -1209,15 +1209,15 @@ def __str__(self): @classmethod def from_configuration( cls, - basis_gates: List[str], - num_qubits: Optional[int] = None, - coupling_map: Optional[CouplingMap] = None, - inst_map: Optional[InstructionScheduleMap] = None, - backend_properties: Optional[BackendProperties] = None, - instruction_durations: Optional[InstructionDurations] = None, - dt: Optional[float] = None, - timing_constraints: Optional[TimingConstraints] = None, - custom_name_mapping: Optional[Dict[str, Any]] = None, + basis_gates: list[str], + num_qubits: int | None = None, + coupling_map: CouplingMap | None = None, + inst_map: InstructionScheduleMap | None = None, + backend_properties: BackendProperties | None = None, + instruction_durations: InstructionDurations | None = None, + dt: float | None = None, + timing_constraints: TimingConstraints | None = None, + custom_name_mapping: dict[str, Any] | None = None, ) -> Target: """Create a target object from the individual global configuration @@ -1351,7 +1351,7 @@ def from_configuration( "with <= 2 qubits (because connectivity is defined on a CouplingMap)." ) for gate in one_qubit_gates: - gate_properties = {} + gate_properties: dict[tuple, InstructionProperties] = {} for qubit in range(num_qubits): error = None duration = None @@ -1441,7 +1441,7 @@ def from_configuration( def target_to_backend_properties(target: Target): """Convert a :class:`~.Target` object into a legacy :class:`~.BackendProperties`""" - properties_dict = { + properties_dict: dict[str, Any] = { "backend_name": "", "backend_version": "", "last_update_date": None, @@ -1481,7 +1481,7 @@ def target_to_backend_properties(target: Target): } ) else: - qubit_props = {x: None for x in range(target.num_qubits)} + qubit_props: dict[int, Any] = {x: None for x in range(target.num_qubits)} for qargs, props in qargs_list.items(): if qargs is None: continue