Skip to content

Commit b045984

Browse files
Hadar01Cryoris
andauthored
Add PEP 484 type hints to transpiler analysis and utility passes (#15832)
* docs: add PEP 484 type hints to transpiler analysis and utility passes Add `from __future__ import annotations` and PEP 484 type annotations to all 9 analysis passes (CountOps, Depth, Size, Width, NumQubits, NumTensorFactors, CountOpsLongestPath, DAGLongestPath, ResourceEstimation) and 3 utility passes (DAGFixedPoint, FixedPoint, ContainsInstruction). Changes include: - Type-annotate __init__ parameters and run() method signatures with DAGCircuit, bool, str, and -> None return types - Expand sparse docstrings with Args blocks where missing - Fix copy-paste module docstring in contains_instruction.py (was incorrectly 'Check if a property reached a fixed point') - Rename placeholder parameter '_' to 'dag' in ResourceEstimation.run() for clarity * Refactor import statements in count_ops.py * Refactor imports and update docstring formatting * Refactor import statements and update docstring * Refactor imports and update docstring formatting * Refactor import statement for TYPE_CHECKING * Apply suggestion from @Cryoris Co-authored-by: Julien Gacon <gaconju@gmail.com> * Refactor imports and docstring formatting * Refactor import statements and docstring formatting * Refactor imports and update docstring format * Refactor import statements in width.py * Update instruction_name type to Iterable in ContainsInstruction * Update dag_fixed_point.py * Refactor imports and docstrings in fixed_point.py * Add copyright notice and docstring to count_ops.py * Add docstring for counting operations in DAGCircuit * Add type checking for DAGCircuit import * Add copyright notice and docstring to depth.py * Add copyright notice and docstring to num_qubits.py * Import DAGCircuit for type checking Add import statement for DAGCircuit in num_tensor_factors.py * Update resource_estimation.py for type checking * Add copyright notice and docstring to size.py * Update width.py * Update copyright notice in contains_instruction.py * Update copyright notice in dag_fixed_point.py * Add copyright notice and docstring to fixed_point.py * Update fixed point tracking in DAG utility * Fix fixed point property update logic * Remove unnecessary blank line in fixed_point.py * Remove unnecessary blank line in dag_fixed_point.py * fix: use from __future__ import annotations with TYPE_CHECKING guard Add from __future__ import annotations to all 12 files so that type hints are treated as strings at runtime (fixing Python 3.10 NameError). Keep TYPE_CHECKING guard for DAGCircuit import to satisfy ruff F821. This matches the pattern used in basepasses.py and layout.py. Also fixes contains_instruction.py module docstring (was copy-paste from fixed_point.py). --------- Co-authored-by: Julien Gacon <gaconju@gmail.com>
1 parent ac20c33 commit b045984

12 files changed

Lines changed: 126 additions & 39 deletions

qiskit/transpiler/passes/analysis/count_ops.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Count the operations in a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -21,10 +28,15 @@ class CountOps(AnalysisPass):
2128
The result is saved in ``property_set['count_ops']`` as an integer.
2229
"""
2330

24-
def __init__(self, *, recurse=True):
31+
def __init__(self, *, recurse: bool = True) -> None:
32+
"""
33+
Args:
34+
recurse: If ``True`` (default), recursively count operations
35+
inside control-flow blocks.
36+
"""
2537
super().__init__()
2638
self.recurse = recurse
2739

28-
def run(self, dag):
29-
"""Run the CountOps pass on `dag`."""
40+
def run(self, dag: DAGCircuit) -> None:
41+
"""Run the CountOps pass on ``dag``."""
3042
self.property_set["count_ops"] = dag.count_ops(recurse=self.recurse)

qiskit/transpiler/passes/analysis/count_ops_longest_path.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Count the operations on the longest path in a DAGCircuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -21,6 +28,6 @@ class CountOpsLongestPath(AnalysisPass):
2128
The result is saved in ``property_set['count_ops_longest_path']`` as an integer.
2229
"""
2330

24-
def run(self, dag):
25-
"""Run the CountOpsLongestPath pass on `dag`."""
31+
def run(self, dag: DAGCircuit) -> None:
32+
"""Run the CountOpsLongestPath pass on ``dag``."""
2633
self.property_set["count_ops_longest_path"] = dag.count_ops_longest_path()

qiskit/transpiler/passes/analysis/dag_longest_path.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212

1313
"""Return the longest path in a :class:`.DAGCircuit` as a list of DAGNodes."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

1825
class DAGLongestPath(AnalysisPass):
1926
"""Return the longest path in a :class:`.DAGCircuit` as a list of
2027
:class:`.DAGOpNode`\\ s, :class:`.DAGInNode`\\ s, and :class:`.DAGOutNode`\\ s."""
2128

22-
def run(self, dag):
23-
"""Run the DAGLongestPath pass on `dag`."""
29+
def run(self, dag: DAGCircuit) -> None:
30+
"""Run the DAGLongestPath pass on ``dag``."""
2431
self.property_set["dag_longest_path"] = dag.longest_path()

qiskit/transpiler/passes/analysis/depth.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,20 @@
1212

1313
"""Calculate the depth of a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

1825
class Depth(AnalysisPass):
1926
"""Calculate the depth of a DAG circuit."""
2027

21-
def __init__(self, *, recurse=False):
28+
def __init__(self, *, recurse: bool = False) -> None:
2229
"""
2330
Args:
2431
recurse: whether to allow recursion into control flow. If this is ``False`` (default),
@@ -28,6 +35,6 @@ def __init__(self, *, recurse=False):
2835
super().__init__()
2936
self.recurse = recurse
3037

31-
def run(self, dag):
32-
"""Run the Depth pass on `dag`."""
38+
def run(self, dag: DAGCircuit) -> None:
39+
"""Run the Depth pass on ``dag``."""
3340
self.property_set["depth"] = dag.depth(recurse=self.recurse)

qiskit/transpiler/passes/analysis/num_qubits.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Calculate the number of qubits of a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -21,6 +28,6 @@ class NumQubits(AnalysisPass):
2128
The result is saved in ``property_set['num_qubits']`` as an integer.
2229
"""
2330

24-
def run(self, dag):
25-
"""Run the NumQubits pass on `dag`."""
31+
def run(self, dag: DAGCircuit) -> None:
32+
"""Run the NumQubits pass on ``dag``."""
2633
self.property_set["num_qubits"] = dag.num_qubits()

qiskit/transpiler/passes/analysis/num_tensor_factors.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Calculate the number of tensor factors of a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -21,6 +28,6 @@ class NumTensorFactors(AnalysisPass):
2128
The result is saved in ``property_set['num_tensor_factors']`` as an integer.
2229
"""
2330

24-
def run(self, dag):
25-
"""Run the NumTensorFactors pass on `dag`."""
31+
def run(self, dag: DAGCircuit) -> None:
32+
"""Run the NumTensorFactors pass on ``dag``."""
2633
self.property_set["num_tensor_factors"] = dag.num_tensor_factors()

qiskit/transpiler/passes/analysis/resource_estimation.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Automatically require analysis passes for resource estimation."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623
from qiskit.transpiler.passes.analysis.depth import Depth
1724
from qiskit.transpiler.passes.analysis.width import Width
@@ -25,16 +32,18 @@ class ResourceEstimation(AnalysisPass):
2532
"""Automatically require analysis passes for resource estimation.
2633
2734
An analysis pass for automatically running:
35+
2836
* Depth()
2937
* Width()
3038
* Size()
3139
* CountOps()
3240
* NumTensorFactors()
41+
* NumQubits()
3342
"""
3443

35-
def __init__(self):
44+
def __init__(self) -> None:
3645
super().__init__()
3746
self.requires += [Depth(), Width(), Size(), CountOps(), NumTensorFactors(), NumQubits()]
3847

39-
def run(self, _):
40-
"""Run the ResourceEstimation pass on `dag`."""
48+
def run(self, dag: DAGCircuit) -> None:
49+
"""Run the ResourceEstimation pass on ``dag``."""

qiskit/transpiler/passes/analysis/size.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Calculate the size of a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -21,7 +28,7 @@ class Size(AnalysisPass):
2128
The result is saved in ``property_set['size']`` as an integer.
2229
"""
2330

24-
def __init__(self, *, recurse=False):
31+
def __init__(self, *, recurse: bool = False) -> None:
2532
"""
2633
Args:
2734
recurse: whether to allow recursion into control flow. If this is ``False`` (default),
@@ -31,6 +38,6 @@ def __init__(self, *, recurse=False):
3138
super().__init__()
3239
self.recurse = recurse
3340

34-
def run(self, dag):
35-
"""Run the Size pass on `dag`."""
41+
def run(self, dag: DAGCircuit) -> None:
42+
"""Run the Size pass on ``dag``."""
3643
self.property_set["size"] = dag.size(recurse=self.recurse)

qiskit/transpiler/passes/analysis/width.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
"""Calculate the width of a DAG circuit."""
1414

15+
from __future__ import annotations
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from qiskit.dagcircuit import DAGCircuit
21+
1522
from qiskit.transpiler.basepasses import AnalysisPass
1623

1724

@@ -22,6 +29,6 @@ class Width(AnalysisPass):
2229
contains the number of qubits + the number of clbits.
2330
"""
2431

25-
def run(self, dag):
26-
"""Run the Width pass on `dag`."""
32+
def run(self, dag: DAGCircuit) -> None:
33+
"""Run the Width pass on ``dag``."""
2734
self.property_set["width"] = dag.width()

qiskit/transpiler/passes/utils/contains_instruction.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@
1010
# copyright notice, and modified files need to carry a notice indicating
1111
# that they have been altered from the originals.
1212

13-
"""Check if a property reached a fixed point."""
13+
"""Check if the DAG contains a specific instruction."""
14+
15+
from __future__ import annotations
16+
17+
from collections.abc import Iterable
18+
from typing import TYPE_CHECKING
19+
20+
if TYPE_CHECKING:
21+
from qiskit.dagcircuit import DAGCircuit
1422

1523
from qiskit.transpiler.basepasses import AnalysisPass
1624

@@ -23,23 +31,22 @@ class ContainsInstruction(AnalysisPass):
2331
that instruction and ``False`` if it does not.
2432
"""
2533

26-
def __init__(self, instruction_name, recurse: bool = True):
27-
"""ContainsInstruction initializer.
28-
34+
def __init__(self, instruction_name: str | Iterable[str], recurse: bool = True) -> None:
35+
"""
2936
Args:
30-
instruction_name (str | Iterable[str]): The instruction or instructions to check are in
37+
instruction_name: The instruction or instructions to check are in
3138
the DAG. The output in the property set is set to ``contains_`` prefixed on each
3239
value for this parameter.
33-
recurse (bool): if ``True`` (default), then recurse into control-flow operations.
40+
recurse: if ``True`` (default), then recurse into control-flow operations.
3441
"""
3542
super().__init__()
3643
self._instruction_names = (
3744
{instruction_name} if isinstance(instruction_name, str) else set(instruction_name)
3845
)
3946
self._recurse = recurse
4047

41-
def run(self, dag):
42-
"""Run the ContainsInstruction pass on dag."""
48+
def run(self, dag: DAGCircuit) -> None:
49+
"""Run the ContainsInstruction pass on ``dag``."""
4350
names = dag.count_ops(recurse=self._recurse)
4451
for name in self._instruction_names:
4552
self.property_set[f"contains_{name}"] = name in names

0 commit comments

Comments
 (0)