Quantum Circuits and Instructions

MimiqCircuitsBase.InstructionType
Instruction(op, qtargets, ctargets) <: AbstractInstruction

Representation 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

AbstractInstruction, Operation

source
MimiqCircuitsBase.InstructionMethod
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]
source
MimiqCircuitsBase.getztargetFunction
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

getbit, getqubit, getbit

source
MimiqCircuitsBase.getztargetsFunction
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

getbit, getqubits, getbits

source
MimiqCircuitsBase.CircuitType
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)
end

Notice how the range 4:10 is not fully used, since 2:4 is shorter.

source
MimiqCircuitsBase.numbitsMethod
numbits(insts::Vector{<:Instruction})
numbits(c::Circuit) -> Int

Compute 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
source
MimiqCircuitsBase.numqubitsMethod
numqubits(insts::Vector{<:Instruction})
numqubits(c::Circuit) -> Int

Compute 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
source
MimiqCircuitsBase.numzvarsMethod
numzvars(insts::Vector{<:Instruction})
numzvars(c::Circuit) -> Int

Compute 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
source
MimiqCircuitsBase.depthMethod
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.

source
MimiqCircuitsBase.remove_swapsFunction
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 remapped
  • qubit_permutation: Final permutation where qubit_permutation[i] gives the physical qubit location of logical qubit i

Arguments

  • circuit: Input quantum circuit
  • recursive=false: 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:

  1. The qubit mapping is updated to track that logical qubits i and j have exchanged physical positions
  2. 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
 3
julia> 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
 1
source
MimiqCircuitsBase.remove_unusedMethod
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]
source
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]
source
MimiqCircuitsBase.drawFunction
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.

source