Other Operations
Custom gates
MimiqCircuitsBase.GateCustom — Type
struct GateCustom{N,T} <: AbstractGate{N}N qubit gate specified by a $2^N \times 2^N$ matrix with elements of type T.
Use this to construct your own gates based on unitary matrices.
MIMIQ uses textbook convention for specifying gates.
One qubit gate matrices are defined in the basis $|0\rangle$, $|1\rangle$ e.g.,
\[\operatorname{Z} = \begin{pmatrix} 1&0\\ 0&-1 \end{pmatrix}\]
Two qubit gate matrices are defined in the basis $|00\rangle$, $|01\rangle$>, $|10\rangle$, $|11\rangle$ where the left-most qubit is the first to appear in the target list e.g.,
\[\operatorname{CNOT} = \begin{pmatrix} 1&0&0&0\\ 0&1&0&0\\ 0&0&0&1\\ 0&0&1&0 \end{pmatrix}\]
julia> CNOT = [1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0]
4×4 Matrix{Int64}:
1 0 0 0
0 1 0 0
0 0 0 1
0 0 1 0
julia> # CNOT gate with control on q1 and target on q2
julia> Instruction(GateCustom(CNOT), 1, 2)
GateCustom([1 0 0 0; 0 1 0 0; 0 0 0 1; 0 0 1 0]) @ q1, q2
# Examples
jldoctest julia> g = GateCustom([1 0; 0 1]) Custom([1.0 0.0; 0.0 1.0])
julia> push!(Circuit(), g, 1) 1-qubit circuit with 1 instructions: └── Custom([1.0 0.0; 0.0 1.0]) @ q1 ```
MimiqCircuitsBase.Operator — Type
Operator(matrix)N qubit operator specified by an $2^N \times 2^N$ matrix.
This operator doesn't have to be unitary.
See also AbstractOperator, ExpectationValue, and Kraus.
Examples
julia> Operator([1 2; 3 4])
1-qubit Operator:
├── 1.0 2.0
└── 3.0 4.0
julia> Operator([1 0 0 1; 0 0 0 0; 0 0 0 0; 1 0 0 1])
2-qubit Operator:
├── 1.0 0.0 0.0 1.0
├── 0.0 0.0 0.0 0.0
├── 0.0 0.0 0.0 0.0
└── 1.0 0.0 0.0 1.0Operators can be used for expectation values:
julia> push!(Circuit(), ExpectationValue(Operator([0 1; 0 0])), 1, 1)
1-qubit, 1-vars circuit with 1 instruction:
└── ⟨Operator([0.0 1.0; 0.0 0.0])⟩ @ q[1], z[1]MimiqCircuitsBase.AbstractOperator — Type
AbstractOperator{N} <: Operation{N,0,0}Supertype for all the N-qubit operators.
Note that objects of type AbstractOperator do not need to be unitary.
Operators can be used to define Kraus channels (noise) AbstractKrausChannel, or to compute expectation values ExpectationValue. However, they will return an error if we attempt to directly apply them to states.
MimiqCircuitsBase.matrix — Function
matrix(operator)Matrix associated to the given operator.
if the operator is parametric, the matrix elements are wrapped in a Symbolics.Num object. To manipulate expressions use the Symbolics package.
Examples
julia> matrix(GateH())
2×2 Matrix{Float64}:
0.707107 0.707107
0.707107 -0.707107
julia> matrix(GateRX(π/2))
2×2 Matrix{ComplexF64}:
0.707107+0.0im 0.0-0.707107im
0.0-0.707107im 0.707107+0.0imMimiqCircuitsBase.opsquared — Method
opsquared(op)Compute $A^\dagger A$ for an operator $A$.
MimiqCircuitsBase.rescale — Method
oprescale(op, a)Compute $a * A$ for an operator $A$ and rescaling factor $a$.
MimiqCircuitsBase.unwrappedmatrix — Method
unwrappedmatrix(operator)Returns the matrix associated to the specified quantum operator without the Symbolics.Num wrapper.
See also matrix.
Examples
julia> unwrappedmatrix(GateRX(π/2))
2×2 Matrix{ComplexF64}:
0.707107+0.0im 0.0-0.707107im
0.0-0.707107im 0.707107+0.0im
julia> unwrappedmatrix(GateH())
2×2 Matrix{Float64}:
0.707107 0.707107
0.707107 -0.707107Gate definitions
MimiqCircuitsBase.GateCall — Type
GateCall(decl, args...)Gate corresponding to a call to a GateDecl definition.
It is created by calling a GateDecl with the proper number of arguments.
Examples
julia> @gatedecl ansatz(θ) begin
@on GateX() q=1
@on GateRX(θ) q=2
end
gate ansatz(θ) =
├── X @ q[1]
└── RX(θ) @ q[2]
julia> @variables λ;
julia> ansatz(λ)
ansatz(λ)
See also
MimiqCircuitsBase.GateDecl — Type
GateDecl(name, args, circuit)Define a new gate of given name, arguments and circuit.
Examples
A simple gate declaration, via the @gatedecl macro:
julia> @gatedecl ansatz(θ) begin
@on GateX() q=1
@on GateRX(θ) q=2
end
gate ansatz(θ) =
├── X @ q[1]
└── RX(θ) @ q[2]
julia> @variables λ;
julia> decompose_step(ansatz(λ))
2-qubit circuit with 2 instructions:
├── X @ q[1]
└── RX(λ) @ q[2]
See also
MimiqCircuitsBase.Block — Type
Block() Block(circuit) Block(instructions) Block(numqubits, numbits, num_variables[, instructions])
A block is a collection of instructions grouped together. It can be used to encapsulate a set of operations that can be reused in different contexts.
- Instructions can be added to a block via
Base.push!in the same way as for a circuit. - Once created a block has a definite number of qubits, bits and z variables.
- Adding operationsthat use more then the specified number of qubits, bits or z variables will throw an error.
Circuits or vector of instructions are copied (in the sense of copy) when passed to a block. This helps ensuring that the underlying instructions vectror is not modified outside of what allowed by Block.
Examples
julia> shorcode = let c = Circuit()
push!(c, GateCX(), 1, 2:3)
push!(c, MeasureZZ(), 1, 2, 1)
push!(c, MeasureZZ(), 2, 3, 2)
push!(c, IfStatement(GateX(), bs"10"), 1, 1, 2)
push!(c, IfStatement(GateX(), bs"11"), 2, 1, 2)
push!(c, IfStatement(GateX(), bs"01"), 3, 1, 2)
Block(c)
end
3-qubit, 2-bit block 2k6pu37sbxdcb with 7 instructions:
├── CX @ q[1], q[2]
├── CX @ q[1], q[3]
├── MZZ @ q[1:2], c[1]
├── MZZ @ q[2:3], c[2]
├── IF(c==10) X @ q[1], condition[1:2]
├── IF(c==11) X @ q[2], condition[1:2]
└── IF(c==01) X @ q[3], condition[1:2]
julia> c = Circuit()
empty circuit
julia> begin
c = Circuit()
push!(c, shorcode, 1,2,3,1,2)
push!(c, parallel(3, GateX()), 1:3...)
push!(c, PauliX(0.1), 1:3)
push!(c, shorcode, 1,2,3,1,2)
push!(c, Measure(), 1:3, 1:3)
end
3-qubit, 3-bit circuit with 9 instructions:
├── block 2ona3chfcmcqp @ q[1:3], c[1:2]
├── ⨷ ³ X @ q[1], q[2], q[3]
├── PauliX(0.1) @ q[1]
├── PauliX(0.1) @ q[2]
├── PauliX(0.1) @ q[3]
├── block 2ona3chfcmcqp @ q[1:3], c[1:2]
├── M @ q[1], c[1]
├── M @ q[2], c[2]
└── M @ q[3], c[3]Non-unitary operations
MimiqCircuitsBase.MeasureReset — Type
MeasureReset()This operation measures a qubit q, stores the value in a classical bit c, then applies a X operation to the qubit if the measured value is 1, effectively resetting the qubit to the :math:\\ket{0} state.
See also MeasureResetX, MeasureResetY, IfStatement, GateH.
Examples
julia> MeasureReset()
MR
julia> decompose(MeasureReset())
1-qubit, 1-bit circuit with 2 instructions:
├── M @ q[1], c[1]
└── Reset @ q[1]
julia> c = push!(Circuit(), MeasureReset(), 1, 1)
1-qubit, 1-bit circuit with 1 instruction:
└── MR @ q[1], c[1]
julia> push!(c, MeasureReset(), 3, 4)
3-qubit, 4-bit circuit with 2 instructions:
├── MR @ q[1], c[1]
└── MR @ q[3], c[4]MimiqCircuitsBase.MeasureResetX — Type
MeasureResetX()The MeasureResetX operation first applies a Hadamard gate (H) to the qubit, performs a measurement and reset operation similar to the MeasureReset operation, and then applies another Hadamard gate. This sequence effectively measures the qubit in the X-basis and resets it to the |+> state.
See also MeasureReset, MeasureResetY, IfStatement, GateH.
Examples
julia> MeasureResetX()
MRX
julia> decompose(MeasureResetX())
1-qubit, 1-bit circuit with 4 instructions:
├── U(π/2,0,π) @ q[1]
├── M @ q[1], c[1]
├── Reset @ q[1]
└── U(π/2,0,π) @ q[1]
julia> c = push!(Circuit(), MeasureResetX(), 1, 1)
1-qubit, 1-bit circuit with 1 instruction:
└── MRX @ q[1], c[1]
julia> push!(c, MeasureResetX(), 3, 4)
3-qubit, 4-bit circuit with 2 instructions:
├── MRX @ q[1], c[1]
└── MRX @ q[3], c[4]MimiqCircuitsBase.MeasureResetY — Type
MeasureResetY()The MeasureResetY operation applies (HYZ) gate to the qubit, performs a MeasureReset operation, and then applies another HYZ gate. This sequence effectively measures the qubit in the Y-basis.
See aclso MeasureResetX, MeasureReset, IfStatement, GateHYZ.
Examples
julia> MeasureResetY()
MRY
julia> decompose(MeasureResetY())
1-qubit, 1-bit circuit with 12 instructions:
├── U(π/2,0,π) @ q[1]
├── U(0,0,π/2) @ q[1]
├── U(π/2,0,π) @ q[1]
├── U(0,0,π) @ q[1]
├── U(0,0,0,-1π/4) @ q[1]
├── M @ q[1], c[1]
├── Reset @ q[1]
├── U(π/2,0,π) @ q[1]
├── U(0,0,π/2) @ q[1]
├── U(π/2,0,π) @ q[1]
├── U(0,0,π) @ q[1]
└── U(0,0,0,-1π/4) @ q[1]
julia> c = push!(Circuit(), MeasureResetY(), 1, 1)
1-qubit, 1-bit circuit with 1 instruction:
└── MRY @ q[1], c[1]
julia> push!(c, MeasureResetY(), 3, 4)
3-qubit, 4-bit circuit with 2 instructions:
├── MRY @ q[1], c[1]
└── MRY @ q[3], c[4]MimiqCircuitsBase.MeasureResetZ — Type
MeasureResetZ()This operation is an alias for MeasureReset Operation.
MimiqCircuitsBase.Reset — Type
Reset()Quantum operation that resets the status of one qubit to the $\ket{0}$ state.
Examples
julia> Reset()
Reset
julia> c = push!(Circuit(), Reset, 1)
1-qubit circuit with 1 instruction:
└── Reset @ q[1]
julia> push!(c, Reset(), 3)
3-qubit circuit with 2 instructions:
├── Reset @ q[1]
└── Reset @ q[3]MimiqCircuitsBase.ResetX — Type
ResetXQuantum operation that resets the status of one qubit to $\ket{+} = (\ket{0}+\ket{1})/\sqrt{2}$, the +1 eigenstate of the X gate.
This operation is equivalent to the sequence Reset, GateH.
See also Reset, Operation, Measure.
Examples
julia> ResetX()
ResetX
julia> decompose(ResetX())
1-qubit circuit with 1 instruction:
└── ResetX @ q[1]
julia> c = push!(Circuit(), ResetX, 1)
1-qubit circuit with 1 instruction:
└── ResetX @ q[1]
julia> push!(c, ResetX(), 3)
3-qubit circuit with 2 instructions:
├── ResetX @ q[1]
└── ResetX @ q[3]MimiqCircuitsBase.ResetY — Type
ResetYQuantum operation that resets the status of one qubit to $\ket{y+} = (\ket{0}+i\ket{1})/\sqrt{2}$, the +1 eigenstate of the Y gate.
This operation is equivalent to the sequence Reset, GateH, GateS.
See also Reset, Operation, Measure.
Examples
julia> ResetY()
ResetY
julia> decompose(ResetY())
1-qubit circuit with 1 instruction:
└── ResetY @ q[1]
julia> c = push!(Circuit(), ResetY, 1)
1-qubit circuit with 1 instruction:
└── ResetY @ q[1]
julia> push!(c, ResetY(), 3)
3-qubit circuit with 2 instructions:
├── ResetY @ q[1]
└── ResetY @ q[3]MimiqCircuitsBase.ResetZ — Type
ResetZ()See Reset.
No-ops
MimiqCircuitsBase.Barrier — Type
Barrier(numqubits)No-op operation that does not affect the quantum state or the execution of a circuit, but prevents compression or optimization across it.
Examples
julia> Barrier(1)
Barrier
julia> Barrier(2)
Barrier
julia> c = push!(Circuit(), Barrier(1), 1)
1-qubit circuit with 1 instruction:
└── Barrier @ q[1]
julia> push!(c, Barrier(1), 1:3)
3-qubit circuit with 4 instructions:
├── Barrier @ q[1]
├── Barrier @ q[1]
├── Barrier @ q[2]
└── Barrier @ q[3]
julia> push!(c, Barrier(3), 1,2,3)
3-qubit circuit with 5 instructions:
├── Barrier @ q[1]
├── Barrier @ q[1]
├── Barrier @ q[2]
├── Barrier @ q[3]
└── Barrier @ q[1:3]Annotations
MimiqCircuitsBase.AbstractAnnotation — Type
AbstractAnnotation{N, M, L} <: Operation{N, M, L}An abstract type representing a general annotation operation for use in quantum circuits. This type supports annotations that are not strictly quantum operations but may carry metadata or extra circuit information.
MimiqCircuitsBase.Detector — Type
Detector{N}(N::Integer, notes::AbstractVector)An annotation class representing a detector in a quantum circuit. The Detector checks the parity of measurement results over N classical bits, where the parity should be deterministic under noiseless execution.
Detector monitors the results of a specific set of measurements and verifies that their combined parity (even or odd) remains consistent. This consistency is expected under ideal, noiseless conditions. If noise or errors disrupt the circuit, the Detector can identify this because the parity will change unexpectedly, signaling a potential error in the measurement outcomes. This helps in error detection by revealing inconsistencies that arise due to unintended disturbances.
See Also QubitCoordinates, ShiftCoordinates, or Tick
Arguments
N::Integer: The number of classical bits.notes::AbstractVector: A vector of floating-point values that contain measurement notes.
Throws
ArgumentError: IfNis zero or negative.
Examples
julia> detector = Detector(2, [1.0, 0.5])
Detector(1.0, 0.5)
julia> c = Circuit()
empty circuit
julia> push!(c,detector,1,2)
2-bit circuit with 1 instruction:
└── Detector(1.0,0.5) @ c[1:2]MimiqCircuitsBase.ObservableInclude — Type
ObservableInclude{N}(N::Integer, notes::AbstractVector)An annotation class for adding measurement records to a specified logical observable within a quantum circuit. Observables are sets of measurements expected to produce a deterministic result, used to track specific logical qubit states across operations.
The ObservableInclude class tags a group of measurement records as a logical observable, representing a consistent, predictable result under noiseless conditions. This grouping allows for tracking the state of logical qubits across circuit operations, which is crucial for error correction. Logical observables monitor encoded qubit states by combining multiple measurements, providing robustness against noise and helping to identify any deviations that indicate potential errors.
See Also QubitCoordinates, Detector, or Tick
Arguments
N::Integer: The number of classical bits observed in this logical observable.notes::AbstractVector: A vector of integers identifying measurement records.
Throws
ArgumentError: IfNis zero or negative.
Examples
julia> obs_include = ObservableInclude(2, [1, 2])
ObservableInclude(1, 2)
julia> c = Circuit()
empty circuit
julia> push!(c, obs_include,1,2)
2-bit circuit with 1 instruction:
└── ObservableInclude(1,2) @ c[1:2]MimiqCircuitsBase.QubitCoordinates — Type
QubitCoordinates(coordinates...)An annotation class used to specify the spatial location of a qubit in a quantum circuit. Coordinates do not affect simulation results but are useful for visualizing and organizing qubit layouts within the circuit.
See Also Detector, ShiftCoordinates, or Tick
Arguments
coordinates: A variable number of floating-point values representing the coordinates of the qubit.
Examples
julia> coords = QubitCoordinates(0.0, 1.0)
QubitCoordinates(0.0, 1.0)
julia> c = Circuit()
empty circuit
julia> push!(c,coords,1)
1-qubit circuit with 1 instruction:
└── QubitCoordinates(0.0,1.0) @ q[1]MimiqCircuitsBase.ShiftCoordinates — Type
ShiftCoordinates(coordinates...)An annotation class used to apply a shift to the spatial coordinates of subsequent qubit or detector annotations in a quantum circuit. ShiftCoordinates accumulates offsets that adjust the position of related circuit components, aiding in visualization without affecting the simulation.
See Also QubitCoordinates, ShiftCoordinates, or Tick
Arguments
coordinates: A variable number of floating-point values representing the shift offsets for each coordinate.
Examples
julia> shift = ShiftCoordinates(1.0, 2.0)
ShiftCoordinates(1.0, 2.0)
julia> c = Circuit()
empty circuit
julia> push!(c, shift)
circuit with 1 instruction:
└── ShiftCoordinates(1.0,2.0)MimiqCircuitsBase.Tick — Type
Tick()An annotation class representing a timing marker or layer boundary in a quantum circuit. Tick does not affect simulation but provides structure by separating operations into distinct time steps, which is useful for visualization and analysis.
See Also QubitCoordinates, ShiftCoordinates, or Detector
Examples
julia> tick = Tick()
Tick()
julia> c = Circuit()
empty circuit
julia> push!(c, tick)
circuit with 1 instruction:
└── Tick()Classical registers
Optimization
MimiqCircuitsBase.OptimizationExperiment — Type
OptimizationExperiment(circuit, initparams[; optimizer = "COBYLA"][, label = "optexp_julia"][, maxiters = nothing][, zregister = 1])Represents a variational quantum optimization experiment that can be executed by MIMIQ.
Defines the quantum circuit, initial parameter values, optimizer, and other metadata needed to launch a parameter optimization run using classical optimizers.
Parameters
- circuit: The parametric quantum circuit that evaluates the cost function.
- initparams: Dictionary of initial parameter values.
- optimizer: Optimization method to use. Must be one of: "BFGS", "LBFGS", "CG", "NELDERMEAD", "NEWTON", "COBYLA", "CMAES". Default is "COBYLA".
- label: Label for this experiment. Default is "optexp_julia".
- maxiters: Maximum number of iterations. Default is nothing (unlimited).
- zregister: Index of the Z-register variable storing the value of the cost function. Default is 1.
Examples
julia> c = Circuit()
empty circuit
julia> @variables x
1-element Vector{Symbolics.Num}:
x
julia> h = Hamiltonian()
empty hamiltonian
julia> push!(c, GateRZ(x), 2)
2-qubit circuit with 1 instruction:
└── RZ(x) @ q[2]
julia> push!(h, 0.0910, PauliString("XY"), 1, 2)
2-qubit hamiltonian with 1 terms:
+
└── 0.091 * XY @ q[1:2]
julia> push_expval!(c, h, 1, 2)
2-qubit, 1-vars circuit with 4 instructions:
├── RZ(x) @ q[2]
├── ⟨XY⟩ @ q[1:2], z[1]
├── z[1] *= 0.091
└── z[1] += 0.0
julia> initparams = Dict(x => 0.1)
Dict{Symbolics.Num, Float64} with 1 entry:
x => 0.1
julia> ex = OptimizationExperiment(c, Dict(x => 0.1), optimizer="COBYLA", label="my_exp", maxiters=100, zregister=1)
OptimizationExperiment:
├── optimizer: COBYLA
├── label: my_exp
├── maxiters: 100
├── zregister: 1
└── initparams:
└── x => 0.1MimiqCircuitsBase.OptimizationResults — Type
OptimizationResults()Container for storing the best optimization result and the full history of runs.
This object captures the optimization trajectory, including the best solution found so far and all intermediate steps.
Parameters
best::OptimizationRun: Best optimization run found.history::Vector{OptimizationRun}: All runs evaluated during optimization.
Examples
julia> results = OptimizationResults()
OptimizationResults:
├── Best Run:
│ OptimizationRun:
│ ├── cost: 0.0
│ ├── parameters:
│ └── results: QCSResults(...)
└── History (0 runs):MimiqCircuitsBase.OptimizationRun — Type
OptimizationRun()Stores the result of a single evaluation of the cost function during optimization.
This struct includes the parameter values used, the final cost value, and the associated QCS results (such as expectation values and samples).
Parameters
cost::Float64: Final cost (objective) value for the parameters.parameters::Dict{String,Float64}: Optimized parameter values.results::QCSResults: Raw execution results from the quantum circuit simulation.
Examples
julia> run = OptimizationRun()
OptimizationRun:
├── cost: 0.0
├── parameters:
└── results: QCSResults(...)