Basic Concepts

This section provides an overview of how the circuit simulation works in our framework.

Quantum Circuit

A quantum circuit is a sequence of quantum gates and operations acting on qubits, the basic units of quantum information.

Every computation, in MIMIQ Circuits starts with the construction of a quantum circuit. You can construct the circuit using the tools provided by the "MimiqCircuits" library, including gates and various quantum operations. Once you have built the circuit, you can proceed with the simulation.

A simple circuit with 4 Hadamard gates on 4 different qubits is given by

julia> c = Circuit()
empty circuit

julia> for i in 1:4
           push!(c, GateH(), i)
       end

julia> c
4-qubit circuit with 4 gates:
├── H @ q1
├── H @ q2
├── H @ q3
└── H @ q4

We are using a @ notation to indicates that a quantum operation, in this case a Hadamard gate H is applied to one or many qubits. Hadamard gates are single qubit gates, so they can be applied only to one qubit at a time (e.g. H @ q1 in the example). In this notation we indicate with q1, q2, q3... qubits or quantum bit targets and with c1, c2, c3, ... classical bit targets.

Note

Indexing of targets is different between Julia and Python. Julia indices starts from 1, while Python indices start from 0. Pay extra attention when translating code from Python or to Julia and vice versa (e.g. when importing code from other frameworks).

Quantum circuits can be easily manipulated in MimiqCircuits. Some common operations include:

  • Adding gates or quantum operations (e.g. with push!(circuit, gate_object, target_qubits...)),
julia> c = Circuit()
empty circuit

julia> push!(c, GateX(), 1)
1-qubit circuit with 1 gates:
└── X @ q1

julia> push!(c, GateCX(), 1, 2)
2-qubit circuit with 2 gates:
├── X @ q1
└── CX @ q1, q2

julia> push!(c, Barrier, 1, 2)
2-qubit circuit with 3 gates:
├── X @ q1
├── CX @ q1, q2
└── Barrier @ q1, q2

julia> push!(c, GateCX(), 2, 1)
2-qubit circuit with 4 gates:
├── X @ q1
├── CX @ q1, q2
├── Barrier @ q1, q2
└── CX @ q2, q1
  • Taking the inverse of a circuit or any other object (with inverse(object_to_invert))
julia> c
2-qubit circuit with 4 gates:
├── H @ q1
├── H @ q2
├── CRX(θ=π⋅0.125) @ q1, q2
└── S @ q2

julia> cinv = inverse(c)
2-qubit circuit with 4 gates:
├── SDG @ q2
├── CRX(θ=-π⋅0.125) @ q1, q2
├── H @ q2
└── H @ q1
  • Appending a circuit to another (with append!(circuit_to_append_to, circuit_to_append))
julia> c
4-qubit circuit with 4 gates:
├── H @ q1
├── H @ q2
├── H @ q3
└── H @ q4

julia> c2
4-qubit circuit with 3 gates:
├── CX @ q1, q2
├── CX @ q2, q3
└── CX @ q3, q4

julia> append!(c, c2)
4-qubit circuit with 7 gates:
├── H @ q1
├── H @ q2
├── H @ q3
├── H @ q4
├── CX @ q1, q2
├── CX @ q2, q3
└── CX @ q3, q4

julia> c
4-qubit circuit with 7 gates:
├── H @ q1
├── H @ q2
├── H @ q3
├── H @ q4
├── CX @ q1, q2
├── CX @ q2, q3
└── CX @ q3, q4

For a full list of functions see MimiqCircuitsBase.Circuit.

Bit States

We define bit states as the computational states of a multi qubit system in which each qubit state is determined. These states are often indicated by the notation

\[\ket{01001010\cdots} \equiv \ket{0}\ket{1}\ket{0}\ket{0}\ket{1}\ket{0}\ket{1}\ket{0}\cdots.\]

Each bit state is fully specified a string of zeros and ones, and hence they are sometimes also referred to as "bitstrings".

The set of all the possible bit states form an complete and orthogonal basis for the quantum state of a multiqubit system, meaning that an arbitrary quantum state of such system can be written as a sum over bit states with complex coefficients. For example, for a 2 qubit system:

\[\ket{\psi} = c_0 \ket{00} + c_1 \ket{10} + c_2 \ket{01} + c_3 \ket{11} \qquad \forall c_0, c_1, c_2, c_3, \sum_0^3 c_i^2 = 1.\]

MimiqCircuits provides users with a set of utilities to handle, construct and manipulate bit states.

Few things you can do with BitState:

  • Create a bit state for a $N$-qubit system ($N=4$ in the example)
julia> BitState(4)
4-qubit BitState with 0 non-zero qubits:
└── |0000⟩
  • Create a bit state from a string with the bs"" syntax:
julia> bs"0101"
4-qubit BitState with 2 non-zero qubits:
├── |0101⟩
└── non-zero qubits: [2, 4]
  • Create a bit state from a list of nonzero qubits, or from an integer (converting from its binary representation)
julia> BitState(10, [1, 2, 4, 8, 10])
10-qubit BitState with 5 non-zero qubits:
├── |1101000101⟩
└── non-zero qubits: [1, 2, 4, 8, 10]

julia> BitState(10, 793)
10-qubit BitState with 5 non-zero qubits:
├── |1001100011⟩
└── non-zero qubits: [1, 4, 5, 9, 10]
  • Create a bit state, using a generator (function or lambda function):
julia> BitState(10) do i
           if i % 2 == 0
               return true
           end
           return false
       end
10-qubit BitState with 5 non-zero qubits:
├── |0101010101⟩
└── non-zero qubits: [2, 4, 6, 8, 10]

julia> f = x -> x <= 10
#7 (generic function with 1 method)

julia> BitState(f, 20)
20-qubit BitState with 10 non-zero qubits:
├── |11111111110000000000⟩
└── non-zero qubits: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • Convert a BitState to a string, index or integer (remember in Julia,

indices starts from 1)

julia> to01(bs)
"1001100011"

julia> bitstate_to_index(bs)
794

julia> bitstate_to_integer(bs)
793
  • Get the state of each qubit, the list of nonzero qubits, or even iterate over the qubits
julia> bs[1]
true

julia> nonzeros(bs)
5-element Vector{Int64}:
  1
  4
  5
  9
 10

julia> for i in bs
           println(i)
       end
true
false
false
true
true
false
false
false
true
true

Besides accessing the quantum state amplitudes corresponding to different bit states, with MIMIQ Circuits, you also have the flexibility to sample the final state in much the same way as is the case for a real quantum computer.

Executing the Circuit

The simulation of a circuit is then performed via the MimiqCircuits.execute function, which start a remote job on the MIMIQ Remote Services. When a job is completed, results can be retrieved by the MimiqCircuits.getresults function. Upon execution, you can specify the algorithm used in the simulation, the number of samples to be performed or the bit states for which the amplitudes should be computed.

A simulation performs the computation

\[\ket{\psi} = U \ket{000\cdots}\]

The starting state is always the one where all the qubits are in the $\ket{0}$ state. After the circuit execution, the final state $\ket{\psi}$ is used for extracting amplitudes and for sampling.

Below we show a graphical representation of the circuit execution.

Circuit execution example

Sampling

Sampling is performed by applying a measurements operation to the final state on each and every qubit. With MIMIQ Circuits many samples can be obtained very efficiently without necessarily recomputing the whole circuit. The number of samples to obtain is specified through the samples keyword argument when executing a job.

Note

The maximum number of samples a user can request is of 2^16

Amplitudes

Amplitudes corresponding to the bit states specified when submitting a simulation, are computed just before the sampling process, right after having applied the circuit provided by the user.

Classical Registries and Mid-Circuit Measurements

At present, MIMIQ Circuits does not support classical registers or measurements in the middle of the circuit. However, we are continuously working to enhance our framework with the plan to introduce these features in a coming update.