Source code for mimiqcircuits.decomposition.abstract
#
# Copyright © 2022-2024 University of Strasbourg. All Rights Reserved.
# Copyright © 2023-2025 QPerfect. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Abstract base classes for the decomposition framework.
This module defines the core abstractions for quantum circuit decomposition:
- :class:`RewriteRule`: Single-step transformation rules
- :class:`DecompositionBasis`: Recursive decomposition to a target gate set
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Sequence
if TYPE_CHECKING:
from mimiqcircuits import Circuit, Operation
[docs]
class DecompositionError(Exception):
"""Raised when an operation cannot be decomposed."""
pass
[docs]
class RewriteRule(ABC):
"""Abstract base class for single-step rewrite rules.
A RewriteRule defines *how* to transform an operation into a sequence of
simpler operations, but not *when* to stop. For recursive decomposition
to a target set of operations, see :class:`DecompositionBasis`.
Subclasses must implement:
- :meth:`matches`: Return True if this rule can transform the operation
- :meth:`decompose_step`: Apply the transformation
Example:
>>> class MyRewrite(RewriteRule):
... def matches(self, op: Operation) -> bool:
... return isinstance(op, GateToffoli)
...
... def decompose_step(self, op, qubits, bits, zvars):
... from mimiqcircuits import Circuit
... circ = Circuit()
... # ... decomposition logic ...
... return circ
"""
[docs]
@abstractmethod
def matches(self, op: Operation) -> bool:
"""Check if this rule can transform the given operation.
Args:
op: The operation to check.
Returns:
True if this rule can decompose the operation.
"""
...
[docs]
@abstractmethod
def decompose_step(
self,
op: Operation,
qubits: Sequence[int],
bits: Sequence[int],
zvars: Sequence[int],
) -> Circuit:
"""Apply this rule to transform an operation.
This method should only be called if :meth:`matches` returns True.
Args:
op: The operation to transform.
qubits: Qubit indices for the operation.
bits: Classical bit indices for the operation.
zvars: Z-variable indices for the operation.
Returns:
A Circuit containing the decomposed instructions.
Raises:
DecompositionError: If the operation cannot be decomposed.
"""
...
def __repr__(self) -> str:
return f"{self.__class__.__name__}()"
[docs]
class DecompositionBasis(ABC):
"""Abstract base class for decomposition targets.
A DecompositionBasis combines two concerns:
1. **What is terminal**: Operations that should not be decomposed further.
2. **How to decompose**: The transformation logic for non-terminal operations.
Subclasses must implement:
- :meth:`isterminal`: Return True if the operation is in the target set
- :meth:`decompose`: Decompose non-terminal operations
Example:
>>> class CliffordT(DecompositionBasis):
... def isterminal(self, op: Operation) -> bool:
... return isinstance(op, (GateH, GateS, GateT, GateCX))
...
... def decompose(self, op, qubits, bits, zvars):
... # Use a rewrite rule to decompose
... return CanonicalRewrite().decompose_step(op, qubits, bits, zvars)
"""
[docs]
@abstractmethod
def isterminal(self, op: Operation) -> bool:
"""Check if an operation is terminal in this basis.
Terminal operations are not decomposed further - they represent the
target instruction set that decomposition reduces circuits to.
Args:
op: The operation to check.
Returns:
True if the operation is terminal.
"""
...
[docs]
@abstractmethod
def decompose(
self,
op: Operation,
qubits: Sequence[int],
bits: Sequence[int],
zvars: Sequence[int],
) -> Circuit:
"""Decompose a non-terminal operation.
This method should only be called for non-terminal operations
(i.e., when :meth:`isterminal` returns False).
Args:
op: The operation to decompose.
qubits: Qubit indices for the operation.
bits: Classical bit indices for the operation.
zvars: Z-variable indices for the operation.
Returns:
A Circuit containing the decomposed instructions.
Raises:
DecompositionError: If the operation cannot be decomposed.
"""
...
def __repr__(self) -> str:
return f"{self.__class__.__name__}()"
__all__ = ["RewriteRule", "DecompositionBasis", "DecompositionError"]