Source code for qrisp.circuit.pass_management.pass_manager

"""********************************************************************************
* Copyright (c) 2026 the Qrisp authors
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************
"""

from __future__ import annotations

from collections.abc import Iterator
from typing import Any

from qrisp.circuit.pass_management.circuit_pass import CircuitPass
from qrisp.circuit.quantum_circuit import QuantumCircuit


[docs] class PassManager: """A manager for organizing and applying quantum circuit transformation passes. The PassManager maintains an ordered list of :class:`CircuitPass` instances and applies them sequentially to quantum circuits. Only :class:`CircuitPass` instances are accepted — raw functions must be wrapped first, for example with the ``@CircuitPass`` decorator or by calling ``CircuitPass(my_func)``. Example: -------- Build a pipeline with ``+=``, then inspect, insert, and remove passes:: >>> from qrisp import PassManager >>> from qrisp import convert_to_cz, fuse_adjacents, remove_barriers >>> >>> pm = PassManager() >>> pm += convert_to_cz() >>> pm += fuse_adjacents >>> pm += remove_barriers >>> >>> print(pm) PassManager(['convert_to_cz', 'fuse_adjacents', 'remove_barriers']) >>> >>> # Insert a pass at a specific position >>> from qrisp import decompose >>> pm.insert_pass(1, decompose) >>> print(pm) PassManager(['convert_to_cz', 'decompose', 'fuse_adjacents', 'remove_barriers']) >>> >>> # Remove the last pass >>> pm.remove_pass(len(pm) - 1) >>> print(pm) PassManager(['convert_to_cz', 'decompose', 'fuse_adjacents']) >>> >>> transpiled_qc = pm.run(qc) """ def __init__(self, passes: list[CircuitPass] | None = None) -> None: """Initialize a PassManager. Parameters ---------- passes : list[CircuitPass], optional Initial list of :class:`CircuitPass` instances. """ self._passes: list[CircuitPass] = [] if passes is not None: for p in passes: self._validate_pass(p) self._passes.extend(passes) @staticmethod def _validate_pass(pass_obj: Any) -> None: """Raise :exc:`TypeError` if *pass_obj* is not a :class:`CircuitPass`.""" if not isinstance(pass_obj, CircuitPass): raise TypeError( f"PassManager only accepts CircuitPass instances, " f"got {type(pass_obj).__name__}. " f"Wrap raw functions with CircuitPass(func) or the @CircuitPass decorator." )
[docs] def add_pass(self, pass_obj: CircuitPass) -> PassManager: """Add a transformation pass to the manager. Parameters ---------- pass_obj : CircuitPass A :class:`CircuitPass` wrapping a transformation with signature ``QuantumCircuit -> QuantumCircuit``. Returns ------- PassManager Returns self for method chaining. """ self._validate_pass(pass_obj) self._passes.append(pass_obj) return self
[docs] def insert_pass(self, index: int, pass_obj: CircuitPass) -> PassManager: """Insert a pass at a specific position. Parameters ---------- index : int Position to insert the pass. pass_obj : CircuitPass The :class:`CircuitPass` to insert. Returns ------- PassManager Returns self for method chaining. """ self._validate_pass(pass_obj) self._passes.insert(index, pass_obj) return self
[docs] def remove_pass(self, index: int) -> PassManager: """Remove a pass at the specified index. Parameters ---------- index : int Index of the pass to remove. Returns ------- PassManager Returns self for method chaining. """ del self._passes[index] return self
[docs] def clear(self) -> PassManager: """Remove all passes from the manager. Returns ------- PassManager Returns self for method chaining. """ self._passes.clear() return self
[docs] def run(self, qc: QuantumCircuit) -> QuantumCircuit: """Apply all passes in sequence to the quantum circuit. Parameters ---------- qc : QuantumCircuit The input quantum circuit to transform. Returns ------- QuantumCircuit The transformed quantum circuit after all passes have been applied. """ result = qc for circuit_pass in self._passes: result = circuit_pass(result) return result
[docs] def verify( self, qc: QuantumCircuit, verification_type: str, visualize_failures: bool = False, **verification_kwargs: Any, ) -> list[tuple[str, bool]]: """Verify every pass in the manager against *qc* one by one. For each pass, the method captures the circuit state before applying the pass and then verifies that the pass preserves it using either :meth:`CircuitPass.compare_unitary` or :meth:`CircuitPass.compare_measurement`. Parameters ---------- qc : QuantumCircuit The input quantum circuit to test the pipeline against. verification_type : str Which verification method to use — ``"unitary"`` or ``"measurements"``. visualize_failures : bool, optional If ``True``, call :meth:`CircuitPass.visualize` on any pass that fails verification. The default is ``False``. **verification_kwargs : Any Keyword arguments forwarded to the verification method (e.g. ``precision``, ``ignore_gphase``, ``backend``). Returns ------- list of tuple(str, bool) A list of ``(pass_name, passed)`` pairs, one for each pass in verification order. Raises ------ ValueError If *verification_type* is not ``"unitary"`` or ``"measurements"``. """ if verification_type not in ("unitary", "measurements"): raise ValueError(f"Unknown verification_type {verification_type!r}. Expected 'unitary' or 'measurements'.") results: list[tuple[str, bool]] = [] current = qc for circuit_pass in self._passes: if verification_type == "unitary": passed = circuit_pass.compare_unitary(current, **verification_kwargs) else: passed = circuit_pass.compare_measurement(current, **verification_kwargs) results.append((circuit_pass.__name__, passed)) if not passed and visualize_failures: circuit_pass.visualize(current) # Always apply the pass so subsequent passes see a realistic # circuit state — even if this one failed verification. current = circuit_pass(current) return results
[docs] def __iadd__(self, other: CircuitPass | PassManager) -> PassManager: """Add a pass or extend with the passes of another PassManager in-place. Usage: ``pm += circuit_pass`` or ``pm += other_pass_manager``. Parameters ---------- other : CircuitPass or PassManager The pass or pass manager whose passes to add. Returns ------- PassManager Returns self after modification. """ if isinstance(other, CircuitPass): self._passes.append(other) elif isinstance(other, PassManager): self._passes.extend(other._passes) else: raise TypeError( f"PassManager only accepts CircuitPass or PassManager instances, got {type(other).__name__}." ) return self
def __len__(self) -> int: """Return the number of passes.""" return len(self._passes) def __iter__(self) -> Iterator[CircuitPass]: """Iterate over passes.""" return iter(self._passes) def __repr__(self) -> str: pass_names = [getattr(p, "__name__", repr(p)) for p in self._passes] return f"PassManager({pass_names})" @classmethod def from_list(cls, pass_list: list[CircuitPass]) -> PassManager: """Create a PassManager from a list of passes. Parameters ---------- pass_list : list[CircuitPass] List of :class:`CircuitPass` instances. Returns ------- PassManager A new PassManager instance. """ return cls(pass_list)