Source code for mimiqcircuits.decomposition.basis.canonical

#
# 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.
#
"""Canonical decomposition basis targeting GateU and GateCX."""

from __future__ import annotations

from typing import TYPE_CHECKING, Sequence

from mimiqcircuits.decomposition.abstract import DecompositionBasis, DecompositionError

if TYPE_CHECKING:
    from mimiqcircuits import Circuit, Operation


# Terminality per operation type, filled on first use. Shared by all
# instances: the canonical gate set does not depend on the basis's state.
_TERMINAL_BY_TYPE: dict[type, bool] = {}


[docs] class CanonicalBasis(DecompositionBasis): """Decomposition basis targeting the canonical primitive gate set. The canonical basis decomposes all operations to a minimal set of primitives: - ``GateU``: Arbitrary single-qubit unitary - ``GateCX``: Two-qubit CNOT gate - ``Measure``, ``Reset``: Measurement and reset operations - ``Barrier``: Scheduling barriers - Classical operations, annotations, noise channels This is the default basis used by :func:`decompose`. Example: >>> from mimiqcircuits import Circuit, GateH, GateCCX >>> from mimiqcircuits.decomposition import decompose, CanonicalBasis >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] <BLANKLINE> >>> c.push(GateCCX(), 0, 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[0] └── C₂X @ q[0:1], q[2] <BLANKLINE> >>> decomposed = decompose(c, CanonicalBasis()) >>> # All gates are now GateU or GateCX """
[docs] def isterminal(self, op: Operation) -> bool: """Check if an operation is terminal in the canonical basis. Terminal operations include: - Core gates: GateU, GateCX - Measurement/reset: Measure, Reset and variants - Control flow: Barrier, IfStatement (if inner op is terminal) - Classical operations, annotations - Noise channels, expectation values - Z-register operations: Add, Multiply, Pow - Delay (timing information) Args: op: The operation to check. Returns: True if the operation is terminal. """ # Terminality depends only on the operation's type, except for the # conditionals, which defer to their body. Caching by type skips the # chain of isinstance checks, each of which goes through ABCMeta. t = type(op) known = _TERMINAL_BY_TYPE.get(t) if known is not None: return known import mimiqcircuits as mc # IfStatement, WhileStatement: terminal if the inner operation is if isinstance(op, (mc.IfStatement, mc.WhileStatement)): return self.isterminal(op.get_operation()) known = isinstance( op, ( # Core gates mc.GateU, mc.GateCX, # Measurement, reset and Loss operations mc.Measure, mc.Reset, mc.Loss, mc.Reload, mc.Check, mc.MeasureCheck, # Control flow mc.Barrier, # Classical operations mc.AbstractClassical, # Annotations mc.AbstractAnnotation, # Noise channels mc.krauschannel, # Expectation values and entanglement measures mc.ExpectationValue, mc.Amplitude, mc.SchmidtRank, mc.VonNeumannEntropy, mc.BondDim, # Z-register operations mc.Add, mc.Multiply, mc.Pow, # Delay (timing information) mc.Delay, ), ) _TERMINAL_BY_TYPE[t] = known return known
[docs] def decompose( self, op: Operation, qubits: Sequence[int], bits: Sequence[int], zvars: Sequence[int], ) -> Circuit: """Decompose a non-terminal operation using canonical rewrite. 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. """ from mimiqcircuits.decomposition.rules import CanonicalRewrite rule = CanonicalRewrite() if rule.matches(op): return rule.decompose_step(op, qubits, bits, zvars) raise DecompositionError( f"Operation {op.name} is not supported by CanonicalBasis " "and cannot be decomposed." )
__all__ = ["CanonicalBasis"]