mimiqcircuits.decomposition¶
Quantum circuit decomposition framework.
This module provides a flexible framework for decomposing quantum circuits into different target gate sets.
Core Concepts¶
RewriteRule: Single-step transformation rules that define how to transform operations, but not when to stop.
DecompositionBasis: Recursive decomposition targets that define both what is terminal (operations that shouldn’t be decomposed) and how to decompose non-terminal operations.
Main Functions¶
decompose(): Recursively decompose a circuit to a target basisdecompose_step(): Apply a single decomposition step
Available Bases¶
CanonicalBasis: Decomposes to GateU + GateCX (default)CliffordTBasis: Decomposes to Clifford+T gate setFlattenedBasis: Flattens GateCall/Block containersQASMBasis: Decomposes to OpenQASM 2.0 gate setStimBasis: Decomposes to Stim Clifford gate setRuleBasis: Wraps any RewriteRule as a DecompositionBasis
Available Rules¶
CanonicalRewrite: Uses built-in gate decomposition methodsFlattenContainers: Flattens GateCall/Block containersZYZRewrite: ZYZ Euler angle decompositionSpecialAngleRewrite: Rotations with special angles -> Clifford+TToZRotationRewrite: Convert RX/RY to RZ + CliffordToffoliToCliffordTRewrite: Toffoli -> Clifford+T decompositionSolovayKitaevRewrite: Solovay-Kitaev approximation with Clifford+TGridSynthRewrite: Ross-Selinger approximation of RZ with Clifford+T
Usage¶
Basic decomposition to canonical basis (GateU + GateCX):
>>> from mimiqcircuits import Circuit, GateH, GateCCX
>>> from mimiqcircuits.decomposition import decompose
>>> c = Circuit()
>>> c.push(GateH(), 0)
1-qubit circuit with 1 instruction:
└── H @ q[0]
>>> c.push(GateCCX(), 0, 1, 2)
3-qubit circuit with 2 instructions:
├── H @ q[0]
└── C₂X @ q[0:1], q[2]
>>> decomposed = decompose(c)
Decompose to Clifford+T:
>>> from mimiqcircuits.decomposition import decompose, CliffordTBasis
>>> decomposed = decompose(c, CliffordTBasis())
Use a specific rewrite rule:
>>> from mimiqcircuits.decomposition import decompose_step, ZYZRewrite
>>> stepped = decompose_step(c, ZYZRewrite())
Creating Custom Rules¶
You can create custom rewrite rules by subclassing RewriteRule:
>>> from mimiqcircuits.decomposition import RewriteRule
>>> class MyRewrite(RewriteRule):
... def matches(self, op):
... return isinstance(op, SomeGate)
...
... def decompose_step(self, op, qubits, bits, zvars):
... from mimiqcircuits import Circuit
... circ = Circuit()
... # ... decomposition logic ...
... return circ
And custom decomposition bases by subclassing DecompositionBasis:
>>> from mimiqcircuits.decomposition import DecompositionBasis
>>> class MyBasis(DecompositionBasis):
... def isterminal(self, op):
... return isinstance(op, (TargetGate1, TargetGate2))
...
... def decompose(self, op, qubits, bits, zvars):
... # Use a rewrite rule or custom logic
... return SomeRewrite().decompose_step(op, qubits, bits, zvars)
- class mimiqcircuits.decomposition.RewriteRule[source]¶
Bases:
ABCAbstract 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
DecompositionBasis.- Subclasses must implement:
matches(): Return True if this rule can transform the operationdecompose_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
- abstract decompose_step(op, qubits, bits, zvars)[source]¶
Apply this rule to transform an operation.
This method should only be called if
matches()returns True.- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- class mimiqcircuits.decomposition.DecompositionBasis[source]¶
Bases:
ABCAbstract base class for decomposition targets.
- A DecompositionBasis combines two concerns:
What is terminal: Operations that should not be decomposed further.
How to decompose: The transformation logic for non-terminal operations.
- Subclasses must implement:
isterminal(): Return True if the operation is in the target setdecompose(): 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)
- abstract isterminal(op)[source]¶
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.
- abstract decompose(op, qubits, bits, zvars)[source]¶
Decompose a non-terminal operation.
This method should only be called for non-terminal operations (i.e., when
isterminal()returns False).- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- exception mimiqcircuits.decomposition.DecompositionError[source]¶
Bases:
ExceptionRaised when an operation cannot be decomposed.
- class mimiqcircuits.decomposition.CanonicalBasis[source]¶
Bases:
DecompositionBasisDecomposition basis targeting the canonical primitive gate set.
- The canonical basis decomposes all operations to a minimal set of primitives:
GateU: Arbitrary single-qubit unitaryGateCX: Two-qubit CNOT gateMeasure,Reset: Measurement and reset operationsBarrier: Scheduling barriersClassical operations, annotations, noise channels
This is the default basis used by
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] >>> c.push(GateCCX(), 0, 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[0] └── C₂X @ q[0:1], q[2] >>> decomposed = decompose(c, CanonicalBasis()) >>> # All gates are now GateU or GateCX
- isterminal(op)[source]¶
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)
- decompose(op, qubits, bits, zvars)[source]¶
Decompose a non-terminal operation using canonical rewrite.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- class mimiqcircuits.decomposition.CliffordTBasis(sk_depth=None, rotations=None)[source]¶
Bases:
DecompositionBasisDecomposition basis targeting the Clifford+T gate set.
Clifford+T is the standard universal gate set of error-corrected quantum computing. The terminal operations of this basis are those of
StimBasisplusGateTandGateTDG, so the two bases always keep the same Clifford and stabilizer operations:single-qubit Cliffords:
GateID,GateX,GateY,GateZ,GateH,GateHXY,GateHYZ,GateS,GateSDG,GateSX,GateSXDG,GateSY,GateSYDG;two-qubit Cliffords:
GateCX,GateCY,GateCZ,GateSWAP,GateISWAP,GateISWAPDG,GateDCX,GateECR;Pauli rotations by
+-pi/2:GateRXX,GateRYY,GateRZZ, andRPauliwith a non-identity Pauli string;the non-Clifford
GateTandGateTDG;measurements, resets and measure-and-resets in the X, Y and Z bases,
MeasureXX,MeasureYY,MeasureZZandMeasurePauli;Pauli noise:
PauliX,PauliY,PauliZ,Depolarizing1,Depolarizing2andPauliNoise;barriers and annotations;
classical operations, which
StimBasisdoes not keep;an
IfStatementwhose body is terminal in this basis, with any condition.StimBasisonly keeps a conditionalGateX,GateYorGateZon a single bit.
Non-terminal operations are rewritten by the first rule that matches, in this order:
SpecialAngleRewrite: rotations by multiples ofpi/4to exact Clifford+T.ToffoliToCliffordTRewrite:GateCCXto Clifford+T with 7 T gates.A
GateUwhose angles are all zero (a pure global phase) is dropped.ZYZRewrite:GateUtoRZ*RY*RZ.ToZRotationRewrite:GateRX,GateRYtoGateRZand Cliffords.The
rotationsrule (defaultGridSynthRewrite): remainingGateRZto an approximate Clifford+T sequence.CanonicalRewrite: any other operation, towardGateUandGateCX.
The
rotationsrule only sees angles that step 1 could not decompose exactly. The body of anIfStatementgoes through the same pipeline and stays conditional. No T-count optimization is performed on the result.- Parameters:
sk_depth (int | None) – Use
SolovayKitaevRewritewith this recursion depth as therotationsrule.rotations (RewriteRule) – Rule that synthesizes the remaining Z-rotations. The default
GridSynthRewrite()approximates each rotation to operator-norm error1e-10. The rule must matchGateRZwith a numeric angle and emit only gates of this basis.
- Raises:
ValueError – If both
sk_depthandrotationsare given, or ifrotationsdoes not matchGateRZ.
Decomposing an operation that no rule handles, such as a noise channel other than the Pauli ones above, raises a
DecompositionError.Example
>>> from mimiqcircuits import Circuit, GateH, GateT, GateRZ >>> from mimiqcircuits.decomposition import decompose, CliffordTBasis >>> from mimiqcircuits.decomposition import GridSynthRewrite >>> from symengine import pi >>> c = Circuit() >>> c.push(GateRZ(pi/4), 0) # special angle, decomposed exactly 1-qubit circuit with 1 instruction: └── RZ((1/4)*pi) @ q[0] >>> decomposed = decompose(c, CliffordTBasis())
>>> # An arbitrary angle is approximated to 1e-10 by gridsynth >>> c2 = Circuit() >>> c2.push(GateRZ(0.123), 0) 1-qubit circuit with 1 instruction: └── RZ(0.123) @ q[0] >>> decomposed2 = decompose(c2, CliffordTBasis())
>>> # A looser error bound per rotation gives fewer T gates >>> basis = CliffordTBasis(rotations=GridSynthRewrite(epsilon=1e-6)) >>> decomposed3 = decompose(c2, basis)
>>> # Solovay-Kitaev instead of gridsynth >>> decomposed4 = decompose(c2, CliffordTBasis(sk_depth=0))
- rotations: RewriteRule¶
- decompose(op, qubits, bits, zvars)[source]¶
Decompose a non-terminal operation toward Clifford+T.
Applies the rules listed in the class docstring, in that order.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- class mimiqcircuits.decomposition.FlattenedBasis[source]¶
Bases:
DecompositionBasisFlatten container operations while leaving all other operations untouched.
- class mimiqcircuits.decomposition.QASMBasis[source]¶
Bases:
DecompositionBasisDecomposition basis targeting the OpenQASM 2.0 gate library.
The terminal gates are the OpenQASM 2.0 built-ins and the gates of the
qelib1.incinclude file that have a direct MIMIQ counterpart, so a decomposed circuit can be written as OpenQASM 2.0 and read back by any parser that ships the standardqelib1.inc.Terminal Operations¶
- Fundamental gates (OpenQASM 2.0 built-in):
GateU,GateCX
- Single-qubit gates from qelib1.inc:
Legacy U gates:
GateU3,GateU2,GateU1Pauli:
GateID,GateX,GateY,GateZHadamard:
GateHPhase:
GateS,GateSDG,GateT,GateTDGRotations:
GateRX,GateRY,GateRZ
- Two-qubit gates from qelib1.inc:
Controlled Paulis and Hadamard:
GateCY,GateCZ,GateCHControlled rotations:
GateCRX,GateCRY,GateCRZGateSWAPIsing couplings:
GateRXX,GateRZZ
- Three- and four-qubit gates from qelib1.inc:
GateCCX,GateCSWAP,GateC3X
- Non-unitary operations:
Measure,Reset,BarrierIfStatementwhose inner operation is terminal
- Custom gates:
GateCallwhose declaration contains only terminal operations
Every other operation is decomposed through
CanonicalRewrite. Operations that OpenQASM 2.0 cannot express and that have no decomposition into the gates above (noise channels, classical operations, annotations, Z-register operations, …) raiseDecompositionError.MeasureResetdecomposes intoMeasurefollowed byReset. This leaves the same state and bit as its canonical decomposition, a measurement and anXconditioned on its outcome, and unlike that conditional it can be written for a circuit of any number of bits. OpenQASM 2.0 has no loops, soWhileStatementraisesDecompositionError.Example
>>> from mimiqcircuits import Circuit, GateH, GateCCX >>> from mimiqcircuits.decomposition import decompose, QASMBasis >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> c.push(GateCCX(), 0, 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[0] └── C₂X @ q[0:1], q[2] >>> # Decompose to QASM-compatible gates >>> qasm_circuit = decompose(c, QASMBasis())
See also
- decompose(op, qubits, bits, zvars)[source]¶
Decompose a non-terminal operation using canonical rewrite.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- class mimiqcircuits.decomposition.RuleBasis(rule)[source]¶
Bases:
DecompositionBasisA decomposition basis that recursively applies a single rewrite rule.
This allows any
RewriteRuleto be used directly as a decomposition basis. The rule is applied recursively until no more operations match.An operation is terminal if the rule does not match it.
- Parameters:
rule (RewriteRule) – The rewrite rule to apply.
Example
>>> from mimiqcircuits import Circuit, GateH >>> from mimiqcircuits.decomposition import decompose, RuleBasis, ZYZRewrite >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> # Decompose using ZYZ rewrite rule >>> decomposed = decompose(c, RuleBasis(ZYZRewrite()))
- rule: RewriteRule¶
- decompose(op, qubits, bits, zvars)[source]¶
Apply the rewrite rule to decompose the operation.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Return type:
- __init__(rule)¶
- class mimiqcircuits.decomposition.StimBasis[source]¶
Bases:
DecompositionBasisDecomposition basis targeting the Stim stabilizer simulator gate set.
Stim is a high-performance stabilizer circuit simulator by Craig Gidney. It supports only Clifford gates, stabilizer operations, noise channels, and quantum error correction annotations.
Decomposition Pipeline¶
The decomposition follows this priority order:
A
GateUwhose angles are all zero (a pure global phase) is dropped.ZYZRewrite:
GateU->RZ * RY * RZSpecialAngleRewrite (Clifford-only): Rotations at
k*pi/2-> Clifford gatesCanonicalRewrite: Fallback for other gates
Non-Clifford rotations (angles not multiples of pi/2) will raise a
DecompositionError.Terminal Operations¶
- Single-qubit Clifford gates:
GateIDPauli:
GateX,GateY,GateZHadamard:
GateH,GateHXY,GateHYZPhase gates:
GateS,GateSDGSquare root gates:
GateSX,GateSXDG,GateSY,GateSYDG
- Two-qubit Clifford gates:
Controlled Pauli:
GateCX,GateCY,GateCZSwap family:
GateSWAP,GateISWAP,ISWAP†GateDCX,GateECR
- Pauli rotations by +-pi/2:
GateRXX,GateRYY,GateRZZwith angle +-pi/2RPauliwith angle +-pi/2 and a non-identity Pauli string
Angles are compared modulo 2*pi, since a rotation by 3*pi/2 equals a rotation by -pi/2 up to a global phase. Other angles are decomposed.
- Feedback:
IfStatementofGateX,GateYorGateZconditioned on a single bit being1. The same gate conditioned on the bit being0is decomposed into the gate followed by the gate conditioned on1. Any other conditional operation has its body decomposed while staying conditional, and raises aDecompositionErrorif that does not lead to a conditional Pauli.
- Measurements (all Pauli bases):
Measure,MeasureX,MeasureYTwo-qubit parities:
MeasureXX,MeasureYY,MeasureZZPauli products:
MeasurePauli
- Resets (all Pauli bases):
Reset,ResetX,ResetY
- Measure-and-reset (all Pauli bases):
MeasureReset,MeasureResetX,MeasureResetY
- Noise channels:
PauliX,PauliY,PauliZDepolarizing1,Depolarizing2PauliNoise
- QEC annotations:
Detector,ObservableInclude,QubitCoordinatesBarrier
Stim Gate Mapping¶
MIMIQ Operation
Stim Operation
GateIDIGateXXGateYYGateZZGateHHGateHXYH_XYGateHYZH_YZGateSSGateSDGS_DAGGateSXSQRT_XGateSXDGSQRT_X_DAGGateSYSQRT_YGateSYDGSQRT_Y_DAGGateCXCNOT/CXGateCYCYGateCZCZGateSWAPSWAPGateISWAPISWAPISWAP†ISWAP_DAGGateDCXDCX(custom)GateECRECR(custom)GateRXX(+-pi/2)SQRT_XX/SQRT_XX_DAGGateRYY(+-pi/2)SQRT_YY/SQRT_YY_DAGGateRZZ(+-pi/2)SQRT_ZZ/SQRT_ZZ_DAGRPauli(P, +-pi/2)SPP/SPP_DAGIfStatement(GateX(), 1)CX rec[-k] qIfStatement(GateY(), 1)CY rec[-k] qIfStatement(GateZ(), 1)CZ rec[-k] qMeasureMMeasureXMXMeasureYMYMeasureXXMXXMeasureYYMYYMeasureZZMZZMeasurePauliMPPResetRResetXRXResetYRYMeasureResetMRMeasureResetXMRXMeasureResetYMRYPauliXX_ERRORPauliYY_ERRORPauliZZ_ERRORDepolarizing1DEPOLARIZE1Depolarizing2DEPOLARIZE2PauliNoisePAULI_CHANNEL_1,PAULI_CHANNEL_2orE/ELSE_CORRELATED_ERRORDetectorDETECTORObservableIncludeOBSERVABLE_INCLUDEQubitCoordinatesQUBIT_COORDSExample
>>> from mimiqcircuits import Circuit, GateH, GateS, GateCX >>> from mimiqcircuits.decomposition import decompose, StimBasis >>> from symengine import pi >>> # Decompose Clifford circuit to Stim-compatible gates >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> c.push(GateS(), 0) 1-qubit circuit with 2 instructions: ├── H @ q[0] └── S @ q[0] >>> c.push(GateCX(), 0, 1) 2-qubit circuit with 3 instructions: ├── H @ q[0] ├── S @ q[0] └── CX @ q[0], q[1] >>> decomposed = decompose(c, StimBasis())
>>> # RZ(pi/2) = S is Clifford, so this works >>> from mimiqcircuits import GateRZ >>> c2 = Circuit() >>> c2.push(GateRZ(pi/2), 0) 1-qubit circuit with 1 instruction: └── RZ((1/2)*pi) @ q[0] >>> decomposed2 = decompose(c2, StimBasis())
Note
- Non-Clifford rotations will raise a DecompositionError::
>>> from mimiqcircuits import * >>> c3 = Circuit() >>> c3.push(GateRZ(pi/4), 0) # T gate is non-Clifford 1-qubit circuit with 1 instruction: └── RZ((1/4)*pi) @ q[0] >>> decompose(c3, StimBasis()) # raises DecompositionError doctest: +ELLIPSIS Traceback (most recent call last): ... mimiqcircuits.decomposition.abstract.DecompositionError: Rotation RZ cannot be decomposed into the Stim basis. Only rotations with angles that are multiples of pi/2 are Clifford gates.
See also
- decompose(op, qubits, bits, zvars)[source]¶
Decompose a non-terminal operation toward the Stim Clifford gate set.
- Applies rules in priority order:
Drops a GateU whose angles are all zero (a global phase)
ZYZRewrite for GateU
SpecialAngleRewrite (Clifford-only) for rotations at k*pi/2
CanonicalRewrite as fallback
Non-Clifford rotations (angles not multiples of pi/2) will raise an error.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed to Clifford gates.
- Return type:
- class mimiqcircuits.decomposition.CanonicalRewrite[source]¶
Bases:
RewriteRuleRewrite rule that decomposes operations using their built-in decomposition.
This rule delegates to each operation’s
_decomposemethod, which typically reduces gates to a minimal set of primitives (GateU, GateCX, etc.).The canonical decomposition is the default decomposition defined by each gate class. Most gates decompose down to GateU (arbitrary single-qubit) and GateCX (two-qubit entangling gate).
Example
>>> from mimiqcircuits import GateH >>> from mimiqcircuits.decomposition import CanonicalRewrite >>> rule = CanonicalRewrite() >>> rule.matches(GateH()) True >>> circ = rule.decompose_step(GateH(), [0], [], []) >>> print(circ) 1-qubit circuit with 1 instruction: └── U((1/2)*pi, 0, pi, 0.0) @ q[0]
- matches(op)[source]¶
Check if this rule can decompose the operation.
Matches every operation that has a canonical decomposition, that is whose
supports_canonical_rewrite()is true. The answer does not depend on any basis: an operation thatCanonicalBasiskeeps terminal (such asResetX) still matches, so other bases can expand it. Primitives without a decomposition (GateU,GateCX,Measure,Reset,Barrier, …) do not match.
- class mimiqcircuits.decomposition.FlattenContainers[source]¶
Bases:
RewriteRuleFlatten container operations into their constituent instructions.
This rule expands: -
GateCall-Block-Inverse(GateCall)-Inverse(Block)(if present)The rule does not perform gate-level decomposition; it only expands container structure. Decomposition behavior is delegated to
CanonicalRewrite.
- class mimiqcircuits.decomposition.ZYZRewrite[source]¶
Bases:
RewriteRuleRewrite 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], [], [])
- matches(op)[source]¶
Check if this rule can decompose the operation.
Matches GateU (unless it’s identity) and GateRX.
- decompose_step(op, qubits, bits, zvars)[source]¶
Apply ZYZ decomposition to the operation.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation is not supported.
- Return type:
- class mimiqcircuits.decomposition.SpecialAngleRewrite(only_cliffords=False)[source]¶
Bases:
RewriteRuleRewrite rule for decomposing rotations with special angles to Clifford+T.
Decomposes single-qubit rotations whose angle is a multiple of pi/4 into explicit Clifford or Clifford+T gates.
This rule only matches rotations with angles k*pi/4 (or k*pi/2 in Clifford-only mode). Generic rotations with arbitrary angles are not matched.
- Parameters:
only_cliffords (bool) – If True, only match angles k*pi/2 (Clifford gates only). If False (default), match angles k*pi/4 (Clifford+T).
- Supported operations:
GateRX(theta)where theta = k*pi/4GateRY(theta)where theta = k*pi/4GateRZ(lambda)where lambda = k*pi/4
Example
>>> from mimiqcircuits import GateRZ >>> from mimiqcircuits.decomposition import SpecialAngleRewrite >>> from symengine import pi >>> rule = SpecialAngleRewrite() >>> rule.matches(GateRZ(pi/4)) # True, decomposes to T True >>> rule.matches(GateRZ(0.123)) # False, not a special angle False
- decompose_step(op, qubits, bits, zvars)[source]¶
Decompose the rotation to Clifford+T gates.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- __init__(only_cliffords=False)¶
- class mimiqcircuits.decomposition.ToZRotationRewrite[source]¶
Bases:
RewriteRuleRewrite rule that converts RX and RY rotations to RZ + Clifford gates.
This is useful for gate sets where only Z-rotations are available as the parametric rotation (e.g., some hardware native gate sets).
- Transformations:
RX(theta)->H * RZ(theta) * HRY(theta)->Sdg * H * RZ(theta) * H * S
Example
>>> from mimiqcircuits import GateRX, GateRY >>> from mimiqcircuits.decomposition import ToZRotationRewrite >>> from symengine import pi >>> rule = ToZRotationRewrite() >>> circ = rule.decompose_step(GateRX(pi/3), [0], [], [])
- decompose_step(op, qubits, bits, zvars)[source]¶
Convert RX or RY to RZ + Clifford gates.
- Parameters:
- Returns:
A Circuit containing the decomposed instructions.
- Raises:
DecompositionError – If the operation is not supported.
- Return type:
- class mimiqcircuits.decomposition.ToffoliToCliffordTRewrite[source]¶
Bases:
RewriteRuleDecompose
GateCCXinto an explicit Clifford+T sequence.
- class mimiqcircuits.decomposition.SolovayKitaevRewrite(depth=3, simplify=True, net_max_depth=10, net_max_points=50000, net_min_dist=0.02)[source]¶
Bases:
RewriteRuleRewrite rule using the Solovay-Kitaev algorithm.
Approximates arbitrary single-qubit unitaries using a sequence of Clifford+T gates. The algorithm recursively refines an initial approximation using group commutators, achieving ε-approximation with O(log^c(1/ε)) gates.
- Parameters:
depth (int) – Recursion depth (default: 3). Higher = better precision, more gates.
simplify (bool) – Whether to simplify the resulting sequence (default: True).
net_max_depth (int) – Maximum depth for epsilon-net generation (default: 15).
net_max_points (int) – Maximum points in epsilon-net (default: 100,000).
net_min_dist (float) – Minimum distance between epsilon-net points (default: 0.01).
- Supported operations:
GateRZ(λ): Z-rotation by angle λ
GateRY(θ): Y-rotation by angle θ
GateRX(θ): X-rotation by angle θ
GateU(θ, φ, λ, γ): General single-qubit unitary
Symbolic parameters are NOT supported.
Example
>>> from mimiqcircuits import GateRZ >>> from mimiqcircuits.decomposition import SolovayKitaevRewrite >>> rule = SolovayKitaevRewrite(depth=0) >>> circ = rule.decompose_step(GateRZ(0.123), [0], [], [])
Note
The first call may be slow due to epsilon-net generation. Subsequent calls reuse the cached net.
- matches(op)[source]¶
Check if this rule can decompose the operation.
Matches single-qubit rotations with concrete (non-symbolic) angles.
- decompose_step(op, qubits, bits, zvars)[source]¶
Decompose the operation using Solovay-Kitaev.
- Parameters:
- Returns:
A Circuit containing the Clifford+T approximation.
- Raises:
DecompositionError – If the operation cannot be decomposed.
- Return type:
- __init__(depth=3, simplify=True, net_max_depth=10, net_max_points=50000, net_min_dist=0.02)¶
- class mimiqcircuits.decomposition.GridSynthRewrite(epsilon=1e-10, seed=0)[source]¶
Bases:
RewriteRuleRewrite rule that approximates
GateRZwith a Clifford+T sequence, using the Ross–Selinger gridsynth algorithm.For
GateRZ(λ)the rule emits a sequence ofGateH,GateS,GateSDG,GateZ,GateTandGateXwhose unitaryVsatisfiesmin_φ ‖RZ(λ) − e^{iφ}V‖ ≤ epsilonin operator norm. The approximation holds up to a global phase, which is dropped. The T-count is about3·log₂(1/epsilon)and, except for rare failures of the integer factoring step, optimal among ancilla-free Clifford+T circuits with this error. Angles that are multiples ofπ/4give their exact short decomposition.The factoring step is randomized. For a fixed
seed, the same angle and tolerance always give the same sequence. Results are cached per angle, since circuits often repeat the same rotation.The synthesis runs in the compiled
mimiqcircuits._coreextension without holding the GIL, so several threads can synthesize in parallel.- Parameters:
- Raises:
ValueError – If
epsilonorseedis out of range.TypeError – If
epsilonis not a real number orseedis not an integer.
- Supported operations:
GateRZ(λ)with a numeric angleλ.
Symbolic angles are not matched.
GateRX,GateRYandGateUmust first be reduced to Z-rotations, for example withZYZRewriteandToZRotationRewrite.CliffordTBasisdoes this and uses this rule by default.Example
>>> from mimiqcircuits import GateRZ, GateT >>> from mimiqcircuits.decomposition import GridSynthRewrite >>> def tcount(c): ... return sum(isinstance(i.operation, GateT) for i in c) >>> # Approximate one rotation to the default tolerance 1e-10 >>> circ = GridSynthRewrite().decompose_step(GateRZ(0.123), [0], [], []) >>> # A coarser tolerance gives fewer T gates >>> coarse = GridSynthRewrite(epsilon=1e-4).decompose_step(GateRZ(0.123), [0], [], []) >>> tcount(coarse) < tcount(circ) True
References
N. J. Ross and P. Selinger, “Optimal ancilla-free Clifford+T approximation of z-rotations”, Quantum Inf. Comput. 16, 901 (2016), arXiv:1403.2975.
- decompose_step(op, qubits, bits, zvars)[source]¶
Decompose the Z-rotation into Clifford+T gates.
- Parameters:
- Returns:
A Circuit containing the Clifford+T approximation.
- Raises:
DecompositionError – If the operation is not a numeric
GateRZ.ValueError – If the angle is not finite.
- Return type:
- __init__(epsilon=1e-10, seed=0)¶
- mimiqcircuits.decomposition.decompose(source, basis=None, wrap=False)[source]¶
Recursively decompose a circuit to a target basis.
Decomposes all operations in the source until they are terminal in the given basis. This is the main entry point for circuit decomposition.
- Parameters:
source (Circuit | Instruction | Operation) – The circuit, instruction, or operation to decompose.
basis (DecompositionBasis | RewriteRule | None) – The target decomposition basis. If a RewriteRule is provided, it is wrapped in a RuleBasis. Defaults to CanonicalBasis.
wrap (bool) – If True, wrap non-terminal ops instead of flattening them into primitives. Gates become a GateCall to a GateDecl, shared between identical gates. Other operations, such as MeasureReset or MeasurePauli, become a Block holding their decomposition, whose gates are wrapped in turn. This produces a circuit with only terminal ops, GateCalls and Blocks.
- Returns:
A new Circuit containing only terminal operations (and GateCalls and Blocks if wrap=True).
- Return type:
Example
>>> from mimiqcircuits import * >>> from mimiqcircuits.decomposition import decompose, CanonicalBasis >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> c.push(GateCCX(), 0, 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[0] └── C₂X @ q[0:1], q[2] >>> decomposed = decompose(c) >>> # decomposed contains only GateU and GateCX
Using a specific basis:
>>> from mimiqcircuits.decomposition import CliffordTBasis >>> decomposed = decompose(c, CliffordTBasis())
Using a rewrite rule directly:
>>> from mimiqcircuits.decomposition import ZYZRewrite >>> decomposed = decompose(c, ZYZRewrite())
- mimiqcircuits.decomposition.decompose_step(source, rule=None)[source]¶
Apply a single decomposition step to a circuit.
Unlike
decompose(), this function applies the rule only once (non-recursively). Operations that don’t match the rule are left unchanged.- Parameters:
source (Circuit | Instruction | Operation) – The circuit, instruction, or operation to decompose.
rule (RewriteRule | None) – The rewrite rule to apply. Defaults to CanonicalRewrite.
- Returns:
A new Circuit with the rule applied once.
- Return type:
Example
>>> from mimiqcircuits import * >>> from mimiqcircuits.decomposition import decompose_step, ZYZRewrite >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> # Apply ZYZ rewrite once (non-recursive) >>> stepped = decompose_step(c, ZYZRewrite())
- mimiqcircuits.decomposition.eachdecomposed(source, basis=None, wrap=False)[source]¶
Return an iterator over decomposed instructions.
This is memory-efficient for large circuits as it yields instructions one at a time without materializing the full decomposed circuit.
- Parameters:
source (Circuit | Instruction | Operation) – The circuit, instruction, or operation to decompose.
basis (DecompositionBasis | RewriteRule | None) – The target decomposition basis. Defaults to CanonicalBasis.
wrap (bool) – If True, wrap non-terminal gates into GateDecl/GateCall and other non-terminal operations into a Block.
- Returns:
An iterator yielding decomposed instructions.
- Return type:
Example
>>> from mimiqcircuits import Circuit, GateH >>> from mimiqcircuits.decomposition import eachdecomposed >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> for inst in eachdecomposed(c): ... print(inst) U((1/2)*pi, 0, pi, 0.0) @ q[0]
- class mimiqcircuits.decomposition.DecomposeIterator(source, basis, wrap=False, cache=None)[source]¶
Bases:
Iterator[Instruction]Iterator that yields instructions from recursive decomposition.
Performs depth-first decomposition of a circuit to a target basis. This is memory-efficient for large circuits as it doesn’t materialize the full decomposed circuit at once.
- Parameters:
source (Circuit | Instruction | Operation) – The source to decompose (Circuit, Instruction, or Operation).
basis (DecompositionBasis) – The target decomposition basis.
wrap (bool) – If True, wrap non-terminal ops instead of flattening them: gates into GateDecl/GateCall, other operations (such as MeasureReset) into a Block holding their wrapped decomposition.
cache (dict | None) – Shared cache dict for wrapped GateDecls (used when wrap=True).
Example
>>> from mimiqcircuits import * >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> for inst in DecomposeIterator(c, CanonicalBasis()): ... print(inst) U((1/2)*pi, 0, pi, 0.0) @ q[0]
Modules