Wire cuts#
Wire cuts can be used to cut the wires of qubits in a circuit. This is done by inserting special cut instructions into the circuit.
A wire cut marks a point on a qubit’s wire where the qubit is measured and a fresh state prepared, so everything before the cut and everything after it can run as separate circuits. Unlike a gate cut it does not need a gate to attach to, which makes it the only option where a qubit has to be handed from one partition to another without an interaction to cut.
from qiskit import QuantumCircuit
from QCut import cut
cut_circuit = QuantumCircuit(3)
cut_circuit.h(0)
cut_circuit.cx(0,1)
cut_circuit.append(cut(), [1])
cut_circuit.cx(1,2)
cut_circuit.draw("mpl")
After this the circuit can be processed as usual with QCut (take a look at the Usage documentation for more details).
Cutting several wires at once#
Cutting \(n\) wires separately costs \(4^n\), since each wire cut costs 4 and the overheads multiply. Wires cut at the same point in the circuit can do better than that if the two sides are allowed to exchange the measured outcome: the side that measures tells the side that prepares what it saw, and the block then costs \(2^{n+1} - 1\).
wires in the block |
cut separately |
exchanging outcomes |
|---|---|---|
1 |
4 |
3 |
2 |
16 |
7 |
3 |
64 |
15 |
The shot count scales as \(\gamma^2\), so a block of two is around five times cheaper and a block of three around eighteen times. The construction is ancilla-free and is derived in Theory.
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
import QCut as ck
from QCut import cut, CutOptions
circuit = QuantumCircuit(4)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(0, 2)
# Two wires cut at the same point, so they can share one decomposition.
circuit.append(cut(), [1])
circuit.append(cut(), [2])
circuit.cx(1, 2)
circuit.cx(2, 3)
observables = SparsePauliOp(["IIIZ", "IIZI", "IZII", "IIZZ"])
cut_circuit = ck.get_locations_and_subcircuits(circuit)
experiment = ck.get_experiment_circuits(cut_circuit, observables)
print(experiment.num_circuits)
That gives 56 circuits. CutOptions(wire_cut_communication="never") cuts the two
wires independently instead, for 128.
What it costs to run#
Exchanging the outcome means one side cannot be run until the other has been, so the experiment runs in waves rather than all at once: every measuring subcircuit first, then the preparing ones, with each wave’s shots allocated according to what the previous wave actually measured. Chains take more than two waves, since a subcircuit can be a preparing side and a measuring side at the same time.
The advertised \(\gamma\) assumes the prepared state follows the measured outcome
shot by shot. Emulating that with batched runs and post-selection costs extra, and at a
single wire that extra outweighs the gain, which is why wire_cut_communication
defaults to "auto" and applies only to blocks of two or more. "always" uses it
for any block including single wires, and "never" for none. See Options.