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.

Note

Only one and two qubits gates are supported.

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 ```

source
MimiqCircuitsBase.Operator — Type
Operator(matrix)

N qubit operator specified by an $2^N \times 2^N$ matrix.

Note

Only one and two qubits operators are supported.

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.0

Operators 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]
source
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.

source
MimiqCircuitsBase.matrix — Function
matrix(operator)

Matrix associated to the given operator.

Note

if the operator is parametric, the matrix elements are wrapped in a Symbolics.Num object. To manipulate expressions use the Symbolics package.

Warning

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.0im
source
MimiqCircuitsBase.unwrappedmatrix — Method
unwrappedmatrix(operator)

Returns the matrix associated to the specified quantum operator without the Symbolics.Num wrapper.

Note

If any of the gate's parameters is symbolic, an error is thrown.

Warning

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.707107
source

Gate 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

GateDecl

source
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

GateCall

source
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.
Info

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]
source

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]
source
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]
source
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]
source
MimiqCircuitsBase.Reset — Type
Reset()

Quantum operation that resets the status of one qubit to the $\ket{0}$ state.

See also Operation, Measure.

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]
source
MimiqCircuitsBase.ResetX — Type
ResetX

Quantum 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]
source
MimiqCircuitsBase.ResetY — Type
ResetY

Quantum 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]
source
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]
source

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]
source
MimiqCircuitsBase.is_full_width_barrier — Method
is_full_width_barrier(inst, nq::Integer) -> Bool

True 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.

source

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.

source
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: If N is 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]
source
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.

See also Reloaded, Loss.

Examples

julia> push!(Circuit(), Lost(), 1)
1-qubit circuit with 1 instruction:
└── Lost @ q[1]
source
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: If N is 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]
source
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]
source
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.

See also Lost, Reload.

Examples

julia> push!(Circuit(), Reloaded(), 1)
1-qubit circuit with 1 instruction:
└── Reloaded @ q[1]
source
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)
source
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()
source
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.0
source

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]
└── 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.1
source
MimiqCircuitsBase.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):
source
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(...)
source
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.

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

source

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 an npoints × nsymbols matrix.
  • ParameterTable(θ => xs, φ => ys) takes one column per symbol. All the columns must have the same length, and point i takes the i-th value of each. For every combination of the values instead, use gridtable.
  • 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.5
source
MimiqCircuitsBase.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]
source
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.0
source

Circuit 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.
source
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.

source