Source code for mimiqcircuits.decomposition.rules.zyz
#
# 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.
#
"""ZYZ Euler angle decomposition rewrite rule."""
from __future__ import annotations
from typing import TYPE_CHECKING, Sequence
from mimiqcircuits.decomposition.abstract import DecompositionError, RewriteRule
if TYPE_CHECKING:
from mimiqcircuits import Circuit, Operation
def _iszero(val) -> bool:
"""Check if a value is zero (handling symbolic expressions)."""
import symengine as se
import sympy as sp
if isinstance(val, (se.Basic, sp.Basic)):
try:
return complex(val) == 0
except (TypeError, ValueError):
return False
return val == 0
[docs]
class ZYZRewrite(RewriteRule):
"""Rewrite rule for ZYZ Euler angle decomposition.
Decomposes single-qubit unitaries into the ZYZ Euler angle form:
``RZ(phi) * RY(theta) * RZ(lambda)``
This is a standard decomposition for arbitrary single-qubit gates,
producing only Z and Y rotations (plus a global phase for GateU).
Supported operations:
- ``GateU(theta, phi, lambda, gamma)`` -> ``RZ(lambda) * RY(theta) * RZ(phi) * Phase(gamma)``
- ``GateRX(theta)`` -> ``S * RY(theta) * Sdg``
Identity rotations (angle = 0) are omitted from the output.
Example:
>>> from mimiqcircuits import GateU
>>> from mimiqcircuits.decomposition import ZYZRewrite
>>> from symengine import pi
>>> rule = ZYZRewrite()
>>> circ = rule.decompose_step(GateU(pi/2, pi/4, pi/3), [0], [], [])
"""
[docs]
def matches(self, op: Operation) -> bool:
"""Check if this rule can decompose the operation.
Matches GateU (unless it's identity) and GateRX.
Args:
op: The operation to check.
Returns:
True if this rule can decompose the operation.
"""
import mimiqcircuits as mc
if isinstance(op, mc.GateU):
# Match unless it's the identity (all angles zero)
return not (
_iszero(op.theta) and _iszero(op.phi) and _iszero(op.lmbda)
)
if isinstance(op, mc.GateRX):
return True
return False
[docs]
def decompose_step(
self,
op: Operation,
qubits: Sequence[int],
bits: Sequence[int],
zvars: Sequence[int],
) -> Circuit:
"""Apply ZYZ decomposition to the operation.
Args:
op: The operation to decompose (GateU or GateRX).
qubits: Qubit indices for the operation.
bits: Classical bit indices (unused).
zvars: Z-variable indices (unused).
Returns:
A Circuit containing the decomposed instructions.
Raises:
DecompositionError: If the operation is not supported.
"""
import mimiqcircuits as mc
if not self.matches(op):
raise DecompositionError(
f"ZYZRewrite cannot decompose {type(op).__name__}"
)
circ = mc.Circuit()
q = qubits[0]
if isinstance(op, mc.GateU):
# GateU(theta, phi, lambda, gamma) = e^{i*gamma} * RZ(phi) * RY(theta) * RZ(lambda)
# Emit gates right-to-left (matrix multiplication order)
lmbda = op.lmbda
theta = op.theta
phi = op.phi
gamma = op.gamma
if not _iszero(lmbda):
circ.push(mc.GateRZ(lmbda), q)
if not _iszero(theta):
circ.push(mc.GateRY(theta), q)
if not _iszero(phi):
circ.push(mc.GateRZ(phi), q)
if not _iszero(gamma):
circ.push(mc.GateU(0, 0, 0, gamma), q)
elif isinstance(op, mc.GateRX):
# RX(theta) = S * RY(theta) * Sdg
# This converts X-rotation to Y-rotation via S conjugation
circ.push(mc.GateS(), q)
circ.push(mc.GateRY(op.theta), q)
circ.push(mc.GateSDG(), q)
return circ
__all__ = ["ZYZRewrite"]