Custom Decompositions
MIMIQ allows you to define your own decomposition logic by creating custom DecompositionBasis and RewriteRule types. This is useful for targeting specific hardware gate sets, implementing novel compilation strategies, or enforcing constraints.
Implementing a Custom Basis
To define a new decomposition target, you need to:
- Define a struct subtype of
DecompositionBasis. - Implement
isterminal(basis, op)to specify allowed operations. - Implement
decompose!(builder, basis, op, ...)to handle unsupported operations.
Example: The "HTCX" Basis
Let's implement a basis that only allows Hadamard (H), T (T), and CNOT (CX) gates. All other gates (like S or Z) must be decomposed.
using MimiqCircuitsBase
# 1. Define the basis type
struct HTCXBasis <: DecompositionBasis end
# 2. Define terminal (allowed) operations
isterminal(::HTCXBasis, ::GateH) = true
isterminal(::HTCXBasis, ::GateT) = true
isterminal(::HTCXBasis, ::GateCX) = true
isterminal(::HTCXBasis, ::Measure) = true
# Default for others is false
# 3. Define decomposition logic
function MimiqCircuitsBase.decompose!(builder, basis::HTCXBasis, op::Operation, qt, ct, zt)
# Strategy: Use existing rewrite rules or define custom logic
# Try the CanonicalRewrite first (handles many standard decompositions)
if matches(CanonicalRewrite(), op)
return decompose_step!(builder, CanonicalRewrite(), op, qt, ct, zt)
end
# Custom decomposition for S gate: S = T * T
if op isa GateS
push!(builder, Instruction(GateT(), qt...))
push!(builder, Instruction(GateT(), qt...))
return builder
end
# Custom decomposition for Z gate: Z = S^2 = T^4
if op isa GateZ
for _ in 1:4
push!(builder, Instruction(GateT(), qt...))
end
return builder
end
throw(DecompositionError("Operation $(opname(op)) not supported in HTCXBasis."))
endNow you can use this basis:
circuit = Circuit()
push!(circuit, GateS(), 1) # Not supported directly
# Decompose to H, T, CX
new_circuit = decompose(circuit, basis=HTCXBasis())
# Result: 2 T gatesImplementing Custom Rewrite Rules
If you have a reusable decomposition logic (e.g., "how to decompose a Toffoli gate"), you can package it as a RewriteRule. Rules are modular and can be shared across multiple bases.
To create a rule:
- Define a struct subtype of
RewriteRule. - Implement
matches(rule, op). - Implement
decompose_step!(builder, rule, op, ...).
Example: Decomposing Swap to 3 CNOTs
struct MySwapToCNOT <: RewriteRule end
# Match SWAP gates
MimiqCircuitsBase.matches(::MySwapToCNOT, ::GateSWAP) = true
MimiqCircuitsBase.matches(::MySwapToCNOT, ::Operation) = false
function MimiqCircuitsBase.decompose_step!(builder, ::MySwapToCNOT, op::GateSWAP, qt, ct, zt)
q1, q2 = qt
# SWAP = CX(1,2) CX(2,1) CX(1,2)
push!(builder, GateCX(), q1, q2)
push!(builder, GateCX(), q2, q1)
push!(builder, GateCX(), q1, q2)
return builder
endYou can then use this rule inside any basis or directly with decompose_step.
# Use the rule directly
decomposed_swap = decompose_step(GateSWAP(), rule=MySwapToCNOT())