Circuit Compilation & Optimization¶
MIMIQ provides tools to optimize and transform quantum circuits.
Cleaning Circuits¶
Remove Unused¶
The remove_unused() function cleans up a circuit by removing unused qubits, classical bits, and Z-variables.
new_circuit, qubit_map, bit_map, zvar_map = remove_unused(circuit)
It returns:
The new optimized
Circuit.A mapping (dict) of old qubit indices to new ones.
A mapping (dict) of old bit indices to new ones.
A mapping (dict) of old Z-variable indices to new ones.
Example:
>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GateH(), 0)
>>> c.push(GateCX(), 0, 2) # Qubit 1 is unused
>>> new_c, qmap, bmap, zmap = remove_unused(c)
>>> new_c
2-qubit circuit with 2 instructions:
├── H @ q[0]
└── CX @ q[0], q[1]
Optimization Passes¶
Remove SWAPs¶
The remove_swaps() function eliminates SWAP gates by tracking qubit permutations and remapping subsequent operations.
new_circuit, final_permutation = remove_swaps(circuit, recursive=False)
Arguments:
circuit: The input circuit.recursive(defaultFalse): IfTrue, recursively removes swaps from nested blocks.
It returns:
A new
Circuitwithout SWAP gates.A list representing the final physical permutation of qubits.
Example:
>>> from mimiqcircuits import *
>>> c = Circuit()
>>> c.push(GateH(), 1)
2-qubit circuit with 1 instruction:
└── H @ q[1]
>>> c.push(GateSWAP(), 1, 2)
3-qubit circuit with 2 instructions:
├── H @ q[1]
└── SWAP @ q[1:2]
>>> c.push(GateCX(), 2, 3)
4-qubit circuit with 3 instructions:
├── H @ q[1]
├── SWAP @ q[1:2]
└── CX @ q[2], q[3]
>>> new_c, perm = remove_swaps(c)
>>> new_c
4-qubit circuit with 2 instructions:
├── H @ q[1]
└── CX @ q[1], q[3]
>>> perm
[0, 2, 1, 3]
Reference¶
- mimiqcircuits.remove_unused(self)[source]
Removes unused qubits, bits, and zvars from the given circuit. Returns (new_circuit, qubit_map, bit_map, zvar_map).
Examples
>>> from mimiqcircuits import * >>> c = Circuit() >>> c.push(GateH(), 0) 1-qubit circuit with 1 instruction: └── H @ q[0] >>> c.push(GateCX(), 0, 2) # qubit 1 is unused 3-qubit circuit with 2 instructions: ├── H @ q[0] └── CX @ q[0], q[2] >>> c.push(Measure(), 2, 2) # bits 0 & 1 are unused 3-qubit, 3-bit circuit with 3 instructions: ├── H @ q[0] ├── CX @ q[0], q[2] └── M @ q[2], c[2] >>> c.push(ExpectationValue(GateX()), 0, 1) # zvar 0 is unused 3-qubit, 3-bit, 2-zvar circuit with 4 instructions: ├── H @ q[0] ├── CX @ q[0], q[2] ├── M @ q[2], c[2] └── ⟨X⟩ @ q[0], z[1] >>> new_c, qmap, bmap, zmap = remove_unused(c) >>> new_c 2-qubit, 1-bit, 1-zvar circuit with 4 instructions: ├── H @ q[0] ├── CX @ q[0], q[1] ├── M @ q[1], c[0] └── ⟨X⟩ @ q[0], z[0]
>>> qmap {0: 0, 2: 1} >>> bmap {2: 0} >>> zmap {1: 0}
- mimiqcircuits.remove_swaps(circuit, recursive=False, cache=None)[source]
Remove all SWAP gates from a quantum circuit by tracking qubit permutations and remapping subsequent operations to their correct physical qubits.
- Returns:
- A tuple containing:
new_circuit: Circuit with SWAP gates removed and operations remapped
qubit_permutation: Final permutation where qubit_permutation[i] gives the physical qubit location of logical qubit i
- Return type:
- Parameters:
circuit – Input quantum circuit
recursive – If True, recursively remove swaps from nested blocks/subcircuits
- Details:
When a SWAP gate is encountered on qubits (i, j), instead of keeping the gate:
The qubit mapping is updated to track that logical qubits i and j have exchanged physical positions
All subsequent gates are automatically remapped to operate on the correct physical qubits
This transformation preserves circuit semantics while eliminating SWAP overhead.
Examples
>>> from mimiqcircuits import * >>> c = Circuit() >>> c.push(GateH(), 1) 2-qubit circuit with 1 instruction: └── H @ q[1] >>> c.push(GateSWAP(), 1, 2) 3-qubit circuit with 2 instructions: ├── H @ q[1] └── SWAP @ q[1:2] >>> c.push(GateCX(), 2, 3) 4-qubit circuit with 3 instructions: ├── H @ q[1] ├── SWAP @ q[1:2] └── CX @ q[2], q[3] >>> new_c, perm = remove_swaps(c) >>> new_c 4-qubit circuit with 2 instructions: ├── H @ q[1] └── CX @ q[1], q[3] >>> perm [0, 2, 1, 3]
>>> c = Circuit() >>> c.push(GateSWAP(), 1, 2) 3-qubit circuit with 1 instruction: └── SWAP @ q[1:2] >>> c.push(GateSWAP(), 2, 3) 4-qubit circuit with 2 instructions: ├── SWAP @ q[1:2] └── SWAP @ q[2:3] >>> c.push(GateCX(), 1, 3) 4-qubit circuit with 3 instructions: ├── SWAP @ q[1:2] ├── SWAP @ q[2:3] └── CX @ q[1], q[3] >>> new_c, perm = remove_swaps(c) >>> new_c 3-qubit circuit with 1 instruction: └── CX @ q[2], q[1] >>> perm [0, 2, 3, 1]
# Example of checking recursive removel of swaps
>>> inner = Circuit() >>> _ = inner.push(GateSWAP(), 0, 1) >>> _ = inner.push(GateCX(), 1, 0) >>> Inner = GateDecl("Inner", [], inner)
>>> mid = Circuit() >>> _ = mid.push(GateSWAP(), 0, 1) >>> _ = mid.push(GateCall(Inner, ()), 0, 1) >>> Mid = GateDecl("Mid", [], mid)
>>> outer = Circuit() >>> _ = outer.push(GateSWAP(), 1, 2) >>> _ = outer.push(GateCall(Mid, ()), 1, 2) >>> Outer = GateDecl("Outer", [], outer)
>>> c = Circuit() >>> _ = c.push(GateSWAP(), 1, 2) >>> _ = c.push(GateCall(Outer, ()), 0, 1, 2)
>>> c 3-qubit circuit with 2 instructions: ├── SWAP @ q[1:2] └── Outer() @ q[0:2]
>>> c.decompose() 3-qubit circuit with 13 instructions: ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] └── CX @ q[2], q[1]
>>> c.decompose().decompose() 3-qubit circuit with 13 instructions: ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] ├── CX @ q[1], q[2] ├── CX @ q[2], q[1] ├── CX @ q[1], q[2] └── CX @ q[2], q[1]
# Test to see that all swaps are removed recursively
>>> new_c, _ = remove_swaps(c, recursive=True)
>>> any(isinstance(i.get_operation(), GateSWAP) for i in new_c) False
>>> Outercheck = new_c.instructions[0].get_operation()._decl >>> any(isinstance(i.get_operation(), GateSWAP) for i in Outercheck.circuit) False
>>> Midcheck = Outercheck.circuit.instructions[0].get_operation()._decl >>> any(isinstance(i.get_operation(), GateSWAP) for i in Midcheck.circuit) False
>>> Innercheck = Midcheck.circuit.instructions[0].get_operation()._decl >>> any(isinstance(i.get_operation(), GateSWAP) for i in Innercheck.circuit) False