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.GateCustomDiagonal — Type
struct GateCustomDiagonal{N} <: AbstractGate{N}N qubit diagonal gate, stored as its $2^N$ diagonal entries.
Same role as GateCustom, but for gates that are diagonal in the computational basis: only the diagonal is kept, so both the memory and the cost of composing two such gates grow as $2^N$ instead of $4^N$.
The entries are ordered as the diagonal of the dense matrix, in the basis $|0\dots0\rangle, \dots, |1\dots1\rangle$ with the first target qubit as the most significant bit — the same convention as GateCustom.
Unitarity requires every entry to be a phase, $|d_i| = 1$.
Examples
julia> g = GateCustomDiagonal([1, -1])
1-qubit CustomDiagonal:
├── 1.0
└── -1.0
julia> matrix(g)
2×2 Matrix{ComplexF64}:
1.0+0.0im 0.0+0.0im
0.0+0.0im -1.0+0.0im
julia> push!(Circuit(), GateCustomDiagonal([1, 1, 1, -1]), 1, 2)
2-qubit circuit with 1 instruction:
└── CustomDiagonal(…) @ q[1:2]See also GateCustom.
MimiqCircuitsBase.diagonal — Method
diagonal(g::GateCustomDiagonal)The diagonal entries of g, as stored (possibly symbolic).
See also unwrappeddiagonal.
MimiqCircuitsBase.unwrappeddiagonal — Method
unwrappeddiagonal(g::GateCustomDiagonal)The diagonal entries of g without the Symbolics.Num wrapper. Throws if any entry is symbolic.
See also diagonal, unwrappedmatrix.
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.
For an operator without parameters the returned matrix may be shared by every call, so mutating it changes the operator's matrix for the whole session. Call copy on it before modifying it.
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
rescale(op, a)Return the operator $a A$ for an operator $A$ and a scalar $a$.
op is not modified. A gate becomes a RescaledGate.
MimiqCircuitsBase.unwrappedmatrix — Method
unwrappedmatrix(operator)Returns the matrix associated to the specified quantum operator without the Symbolics.Num wrapper.
For an operator without parameters the returned matrix may be shared by every call, so mutating it changes the operator's matrix for the whole session. Call copy on it before modifying it.
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()Measures a qubit in the Z basis, stores the outcome in a classical bit, then applies GateX to the qubit if the outcome is 1, which leaves it in the $\ket{0}$ state. It decomposes into Measure followed by an IfStatement of GateX on that bit.
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]
└── IF(c==1) U(π,0,π) @ q[1], condition[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]
├── IF(c==1) U(π,0,π) @ q[1], condition[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]
├── IF(c==1) U(π,0,π) @ q[1], condition[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.
MimiqCircuitsBase.PrepareStateT — Type
PrepareStateT()Prepare one qubit in the magic state $T\ket{+} = (\ket{0} + e^{i\pi/4}\ket{1})/\sqrt{2}$, whatever its previous state.
A magic state is the resource consumed by a $\pi/8$ Pauli rotation when the rotation is carried out with Pauli product measurements (see pauli_based_computation). Fault-tolerant architectures produce these states separately, by distillation, so a dedicated operation lets them be counted and scheduled.
The operation is a Kraus channel with operators $T\ket{+}\bra{0}$ and $T\ket{+}\bra{1}$, and is equivalent to Reset, GateH, GateT. It cannot be inverted.
See also Reset, ResetX, MeasurePauli.
Examples
julia> c = push!(Circuit(), PrepareStateT(), 1)
1-qubit circuit with 1 instruction:
└── PrepareStateT @ q[1]The canonical decomposition resets the qubit to $\ket{0}$, then applies H and T:
julia> decompose_step(PrepareStateT())
1-qubit circuit with 3 instructions:
├── Reset @ q[1]
├── H @ q[1]
└── T @ q[1]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.
On some backends (e.g. MPSSim.MPSSimulator), a full-width Barrier — one whose targets cover every qubit in the circuit — additionally acts as a compile-time segment seam: the compiler may pre-compress everything before such a barrier (or after it) into a reusable MPO that is shared across trajectories. Adding such a barrier can therefore materially change memory use in noisy circuits; remove it if you do not want this effect. See is_full_width_barrier.
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]MimiqCircuitsBase.is_full_width_barrier — Method
is_full_width_barrier(inst, nq::Integer) -> BoolTrue iff inst is a single Barrier instruction whose targets cover every qubit index in 1:nq. Conservative reading — a consecutive run of single-qubit Barrier{1}s that together span 1:nq is not recognised here; insert a Barrier(nq) over q[1:nq] if you want a full-width barrier.
Used by backends to identify "safe cut points" where a deterministic prefix can be pre-compressed (or a deterministic suffix can begin) without risk of qubit-shape drift across the seam.
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.
Annotations have no effect on the state, so isunitary is true for all of them.
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.Lost — Type
Lost()A passive marker recording that a qubit became lost at this point.
Lost carries no effect on execution. It is emitted by lower_losses so the resolved circuit still shows where losses landed, and it may be added by hand for documentation.
Examples
julia> push!(Circuit(), Lost(), 1)
1-qubit circuit with 1 instruction:
└── Lost @ q[1]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.Reloaded — Type
Reloaded()A passive marker recording that a qubit was reloaded at this point.
Reloaded carries no effect on execution. It is emitted by lower_losses next to the Reset that performs the reload, and may be added by hand for documentation.
Examples
julia> push!(Circuit(), Reloaded(), 1)
1-qubit circuit with 1 instruction:
└── Reloaded @ 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()MimiqCircuitsBase.getnotes — Method
getnotes(annotation)Return the values attached to an annotation, as a vector.
These are the notes of Detector and ObservableInclude, and the coordinates of QubitCoordinates and ShiftCoordinates. Tick and the loss annotations carry none, so they return an empty vector.
Examples
julia> getnotes(Detector(2, [1.0, 0.5]))
2-element Vector{Float64}:
1.0
0.5
julia> getnotes(QubitCoordinates(1.0, 2.0))
2-element Vector{Float64}:
1.0
2.0Classical 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]
└── z[1] = z[1]
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(...)MimiqCircuitsBase.changelistofparameters — Method
changelistofparameters(experiment::OptimizationExperiment, θ::AbstractVector)Return a copy of the experiment with new initial parameter values given as a vector. The values are assigned to the symbols of the circuit sorted by name.
Throws an error if θ does not have one value per symbol of the circuit.
See also changeparameters.
MimiqCircuitsBase.changeparameters — Method
changeparameters(experiment::OptimizationExperiment, d::Dict)Return a copy of the experiment whose initial parameters take the values in d. Parameters not in d keep their value, and keys of d that are not parameters of the experiment are ignored.
See also changelistofparameters.
MimiqCircuitsBase.costhistory — Method
costhistory(results::OptimizationResults)Return the cost of every run of the optimization, in the order they were evaluated.
MimiqCircuitsBase.getbest — Method
getbest(results::OptimizationResults)Return the OptimizationRun with the best cost.
MimiqCircuitsBase.getcost — Method
getcost(run::OptimizationRun)Return the value of the cost function for the parameters of the run.
MimiqCircuitsBase.getresultofbest — Method
getresultofbest(run::OptimizationRun)
getresultofbest(results::OptimizationResults)Return the QCSResults of a run, or of the best run of an optimization.
MimiqCircuitsBase.getresultsofhistory — Method
getresultsofhistory(results::OptimizationResults)Return the QCSResults of every run of the optimization, in the order they were evaluated.
Parameter sweeps
MimiqCircuitsBase.ParameterTable — Type
ParameterTable(symbols, values)
ParameterTable(symbol => values, ...)
ParameterTable(points)Values for the symbolic parameters of a circuit, one row per point of a sweep.
In a sweep every point binds the same symbols, so a table stores them once, in a fixed order, together with a matrix of values: row i holds the values of point i, and column j the values of symbols[j].
ParameterTable(symbols, values)takes the symbols and annpoints × nsymbolsmatrix.ParameterTable(θ => xs, φ => ys)takes one column per symbol. All the columns must have the same length, and pointitakes thei-th value of each. For every combination of the values instead, usegridtable.ParameterTable(points)takes a vector of dictionaries, one per point. Every dictionary must bind the same symbols.
Every symbol must be a plain variable, such as one created with @variables, and appear only once. Values are stored as Float64.
Tables compose: a * b is the Cartesian product, hcat(a, b) pairs the points of two tables of the same length, and vcat(a, b) appends the points of b after those of a.
length(t) is the number of points, t[i] is point i as a dictionary ready for evaluate, and t[1:2] or t[[1, 3]] is the table of the selected points. listvars gives the symbols, and Matrix(t) the values. Iterating over a table gives its points. Two tables are equal when they have the same symbols, in the same order, and the same values.
See also gridtable, SweepExperiment.
Examples
julia> @variables θ φ;
julia> t = ParameterTable(θ => [0.0, 0.5, 1.0], φ => [0.0, 0.25, 0.5])
ParameterTable with 3 points over (θ, φ):
├── θ = 0.0, φ = 0.0
├── θ = 0.5, φ = 0.25
└── θ = 1.0, φ = 0.5
julia> t[2][φ]
0.25
julia> ParameterTable(θ => [0.0, 1.0]) * ParameterTable(φ => [0.0, 0.5])
ParameterTable with 4 points over (θ, φ):
├── θ = 0.0, φ = 0.0
├── θ = 0.0, φ = 0.5
├── θ = 1.0, φ = 0.0
└── θ = 1.0, φ = 0.5MimiqCircuitsBase.SweepExperiment — Type
SweepExperiment(circuit, table)A parameter sweep: a symbolic circuit and the ParameterTable of values to run it with, one execution per point.
The table must give values for exactly the free symbols of the circuit, no more and no fewer, so a missing or misspelled symbol is caught when the experiment is built rather than when the sweep runs.
length(ex) is the number of points, ex[i] is the circuit evaluated at point i, ex[1:2] is the experiment over the selected points only, and iterating over the experiment gives the evaluated circuits in order. Executing it on a backend returns one result per point.
See also ParameterTable, gridtable.
Examples
julia> @variables θ;
julia> c = push!(Circuit(), GateRX(θ), 1);
julia> ex = SweepExperiment(c, ParameterTable(θ => [0.0, π / 2, π]))
SweepExperiment:
├── circuit: 1-qubit circuit with 1 instruction
└── table: ParameterTable(3 points over (θ))
julia> ex[2]
1-qubit circuit with 1 instruction:
└── RX(π/2) @ q[1]MimiqCircuitsBase.gridtable — Method
gridtable(symbol => values, ...)Build the ParameterTable of every combination of the given values, the Cartesian product of the columns.
The first symbol varies slowest and the last one fastest, so gridtable(θ => [0, 1], φ => [0, 2, 4]) has 6 points: θ = 0 with each φ, then θ = 1 with each φ. It is the same as multiplying the one-column tables with *.
Examples
julia> @variables θ φ;
julia> gridtable(θ => [0.0, 1.0], φ => [0.0, 2.0, 4.0])
ParameterTable with 6 points over (θ, φ):
├── θ = 0.0, φ = 0.0
├── θ = 0.0, φ = 2.0
├── θ = 0.0, φ = 4.0
├── θ = 1.0, φ = 0.0
├── θ = 1.0, φ = 2.0
└── θ = 1.0, φ = 4.0Circuit testing
MimiqCircuitsBase.CircuitTesterExperiment — Type
CircuitTesterExperiment(c1::Circuit, c2::Circuit; method::String="samples")Represents a circuit equivalence checking experiment.
Arguments
c1::Circuit: First circuit.c2::Circuit: Second circuit.method::String: Verification method. Can be "samples" (default) or "amplitudes".- "samples": Measures the final state.
- "amplitudes": Computes the amplitude of the all-zero state.
MimiqCircuitsBase.build_circuit — Method
build_circuit(ex::CircuitTesterExperiment)Constructs the circuit for the circuit tester experiment.
MimiqCircuitsBase.interpret_results — Method
interpret_results(ex::CircuitTesterExperiment, results::QCSResults)Verifies the results of the circuit tester experiment. Returns the probability of the all-zero state.