Quantum Circuits and Instructions
MimiqCircuitsBase.Instruction — Type
Instruction(op, qtargets, ctargets) <: AbstractInstructionRepresentation of an operation applied to specific qubit and bit targets.
Example
julia> Instruction(GateX(), (1,), (), ())
X @ q[1]
julia> Instruction(GateCX(), (1,2), (), ())
CX @ q[1], q[2]
julia> Instruction(Measure(), (3,), (3,), ())
M @ q[3], c[3]
See also
MimiqCircuitsBase.Instruction — Method
Instruction(op, targets...)Constructors an instruction from an operation and a list of targets.
By convention, if op is an N-qubit and M-bit operations, then the first N targets are used as qubits and the last M as bits.
Examples
julia> Instruction(GateX(), 1)
X @ q[1]
julia> Instruction(GateCX(), 1,2)
CX @ q[1], q[2]
julia> Instruction(Measure(), 3, 3)
M @ q[3], c[3]
MimiqCircuitsBase.getbit — Function
getbit(instruction, i)i-th target classical bit of an instruction.
Examples
julia> inst = Instruction(Measure(), 1, 3)
M @ q[1], c[3]
julia> getbit(inst, 1)
3
See also
MimiqCircuitsBase.getbits — Function
getbits(instruction)Tuple of the classical bits which the instruction is applied to.
Examples
julia> inst = Instruction(Measure(), 1, 3)
M @ q[1], c[3]
julia> getbits(inst)
(3,)
See also
MimiqCircuitsBase.getqubit — Function
getqubit(instruction, i)i-th target qubit of an instruction.
Examples
julia> inst = Instruction(GateCX(), 1, 3)
CX @ q[1], q[3]
julia> getqubit(inst, 2)
3
See also
MimiqCircuitsBase.getqubits — Function
getqubits(instruction)Tuple of quantum bits which the instruction is applied to.
julia> inst = Instruction(GateCX(), 1, 3)
CX @ q[1], q[3]
julia> getqubits(inst)
(1, 3)
See also
MimiqCircuitsBase.getztarget — Function
getztarget(instruction)Tuple of the classical bits which the instruction is applied to.
Examples
julia> inst = Instruction(ExpectationValue(pauli"ZZ"), 1, 2, 1)
⟨ZZ⟩ @ q[1:2], z[1]
julia> getztarget(inst,1)
1
See also
MimiqCircuitsBase.getztargets — Function
getztargets(instruction)Tuple of the classical bits which the instruction is applied to.
Examples
julia> inst = Instruction(ExpectationValue(pauli"ZZ"), 1, 2, 1)
⟨ZZ⟩ @ q[1:2], z[1]
julia> getztargets(inst)
(1,)
See also
MimiqCircuitsBase.Circuit — Type
Circuit([instructions])Representation of a quantum circuit as a vector of instructions applied to the qubits.
The circuit can be initialized with an optional vector of instructions.
A Circuit can be manipulated as either a list of inctructions or as a direct acyclic graph (DAG) of instructions.
See OPERATIONS, GATES, or GENERALIZED for the list of operations to add to circuits.
Examples
Operation can be added one by one to a circuit with the push!(circuit, operation, targets...) function
julia> c = LinearCircuit()
empty circuit
julia> push!(c, GateH(), 1)
1-qubit circuit with 1 instruction:
└── H @ q[1]
julia> push!(c, GateCX(), 1, 2)
2-qubit circuit with 2 instructions:
├── H @ q[1]
└── CX @ q[1], q[2]
julia> push!(c, GateRX(π / 4), 1)
2-qubit circuit with 3 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
└── RX(π/4) @ q[1]
julia> push!(c, Barrier(2), 1, 3)
3-qubit circuit with 4 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── RX(π/4) @ q[1]
└── Barrier @ q[1,3]
julia> push!(c, Measure(), 1, 1)
3-qubit, 1-bit circuit with 5 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── RX(π/4) @ q[1]
├── Barrier @ q[1,3]
└── M @ q[1], c[1]
Targets are not restricted to be single values, but also vectors. In this case a single push! will add multiple operations.
julia> push!(LinearCircuit(), GateCCX(), 1, 2:4, 4:10)
6-qubit circuit with 3 instructions:
├── C₂X @ q[1:2], q[4]
├── C₂X @ q[1,3], q[5]
└── C₂X @ q[1,4], q[6]is equivalent to
for (i, j) in zip(2:4, 4:10)
push!(c, GateCX(), 1, i)
endNotice how the range 4:10 is not fully used, since 2:4 is shorter.
MimiqCircuitsBase.numbits — Method
numbits(insts::Vector{<:Instruction})
numbits(c::Circuit) -> IntCompute the highest index of c-targets in the given circuit.
Examples
julia> c = Circuit()
empty circuit
julia> push!(c, Measure(), 1:2, 1:2)
2-qubit, 2-bit circuit with 2 instructions:
├── M @ q[1], c[1]
└── M @ q[2], c[2]
julia> numbits(c)
2
MimiqCircuitsBase.numqubits — Method
numqubits(insts::Vector{<:Instruction})
numqubits(c::Circuit) -> IntCompute the highest index of q-targets in the given vector of instructions or circuit.
Examples
julia> c = Circuit()
empty circuit
julia> push!(c, Measure(), 1:2, 1:2)
2-qubit, 2-bit circuit with 2 instructions:
├── M @ q[1], c[1]
└── M @ q[2], c[2]
julia> numqubits(c)
2
MimiqCircuitsBase.numzvars — Method
numzvars(insts::Vector{<:Instruction})
numzvars(c::Circuit) -> IntCompute the highest index of z-targets in the given circuit.
Examples
julia> c = Circuit()
empty circuit
julia> push!(c, Amplitude(bs"01"), 1:2)
2-vars circuit with 2 instructions:
├── Amplitude(bs"01") @ z[1]
└── Amplitude(bs"01") @ z[2]
julia> numzvars(c)
2
MimiqCircuitsBase.depth — Method
depth(circuit)Compute the depth of a quantum circuit.
The depth of a quantum circuit is a metric computing the maximum time (in units of quantum gates application) between the input and output of the circuit.
MimiqCircuitsBase.remove_swaps — Function
remove_swaps(circuit; recursive=false)Remove all SWAP gates from a quantum circuit by tracking qubit permutations and remapping subsequent operations to their correct physical qubits.
Returns a tuple of:
new_circuit: Circuit with SWAP gates removed and operations remappedqubit_permutation: Final permutation wherequbit_permutation[i]gives the physical qubit location of logical qubiti
Arguments
circuit: Input quantum circuitrecursive=false: Iftrue, 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
iandjhave 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
julia> c = Circuit()
push!(c, GateH(), 1)
push!(c, GateSWAP(), 1, 2)
push!(c, GateCX(), 2, 3)
new_c, perm = remove_swaps(c)
new_c
3-qubit circuit with 2 instructions:
├── H @ q[1]
└── CX @ q[1], q[3]
julia> perm # Logical qubit 1 is at physical position 2, logical 2 at position 1
3-element Vector{Int64}:
2
1
3julia> c = Circuit()
push!(c, GateSWAP(), 1, 2)
push!(c, GateSWAP(), 2, 3)
push!(c, GateCX(), 1, 3) # After swaps: logical 1 -> physical 3, logical 3 -> physical 1
new_c, perm = remove_swaps(c)
new_c
3-qubit circuit with 2 instructions:
├── CX @ q[2], q[1]
└── ID @ q[3]
julia> perm
3-element Vector{Int64}:
2
3
1MimiqCircuitsBase.remove_unused — Method
remove_unused(circuit)Removes unused qubits, bits, and zvars from the given quantum circuit.
Returns the modified circuit and the mappings from old indices to new indices.
Example
begin
c = Circuit()
push!(c, GateH(), 1)
push!(c, GateCX(), 1, 3:2:7)
push!(c, Measure(), 1:2:7, 1:2:7)
push!(c, ExpectationValue(GateZ()), 9, 9)
end
cr, qubit_map, bit_map, zvar_map = remove_unused(c)
cr
# output
5-qubit, 4-bit, 1-vars circuit with 9 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── CX @ q[1], q[3]
├── CX @ q[1], q[4]
├── M @ q[1], c[1]
├── M @ q[2], c[2]
├── M @ q[3], c[3]
├── M @ q[4], c[4]
└── ⟨Z⟩ @ q[5], z[1]MimiqCircuitsBase.emplace! — Function
emplace!(circuit, operation, registers...)Emplace an operation at the end of a circuit and applies it to the given registers.
julia> emplace!(Circuit(), control(3, GateSWAP()), [1,2,3], [4,5])
5-qubit circuit with 1 instruction:
└── C₃SWAP @ q[1:3], q[4:5]
julia> QFT()
lazy QFT(?)
julia> emplace!(Circuit(), QFT(), [1,2,3])
3-qubit circuit with 1 instruction:
└── QFT @ q[1:3]
MimiqCircuitsBase.draw — Function
draw(circuit)Draw an ascii representation of a circuit.
NOTE it automatically detects the screen width and will split the circuit if it is too wide.