Tutorial
In this guide, we will walk you through the fundamental procedures for simulating a quantum circuit using MIMIQ-CIRC product developed by QPerfect. Throughout the tutorial, we will furnish links to detailed documentation and examples that can provide a deeper understanding of each topic.
In order to use MIMIQ-CIRC, we need to first load the MimiqCircuit
Julia module within your workspace by following this step:
using MimiqCircuits
Building a Quantum circuit
The first step in executing quantum algorithm on MIMIQ-CIRC always consists in defining one implementation of the algorithm as a quantum circuit, a sequence of quantum operations (quantum gates, measurements, resets, etc...) that act on a set of qubits. In MIMIQ-CIRC we always start by defining an empty circuit
circuit = Circuit()
empty circuit
In MimiqCircuits
we do not need to specify the number of qubits of a circuit, or a list of quantum registers to use. Qubits will be allocated up to the maximum used index. Since in Julia indices start counting from one, also qubits indices are allowed values between $1$ and $2^{63}-1$.
A circuit is made up of quantum operations. Gates, or unitary operations, are the simplest and most common ones. Lists are provided by the documentation of MimiqCircuitsBase.OPERATIONS
, MimiqCircuitsBase.GATES
and MimiqCircuitsBase.GENERALIZED
, which can also be simply accessed by
?GATES
To add gates to circuits in Julia we will be using the puhs!
function, which takes multiple arguments, but usually: the circuit to add the operation to, the operation to be added, and as many target qubits as possible.
In this first simple example [MimiqCircuitsBase.GateH
], only needs one target
push!(circuit, GateH(), 1)
1-qubit circuit with 1 instructions:
└── H @ q[1]
The text representation H @ q[1]
informs us that there is an instruction which applies the Hadamard gate to the qubit of index 1
.
Multiple gates can be added at once through the same push!
syntax. In the following example we add 9 CX
or control-X
gates between the qubit 1 and all the qubits from 2 to 10.
push!(circuit, GateCX(), 1, 2:10)
10-qubit circuit with 10 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── CX @ q[1], q[3]
├── CX @ q[1], q[4]
├── CX @ q[1], q[5]
├── CX @ q[1], q[6]
├── CX @ q[1], q[7]
├── CX @ q[1], q[8]
├── CX @ q[1], q[9]
└── CX @ q[1], q[10]
This syntax is not dissimilar to the OpenQASM one, and can be seen as equivalent of
for i in 2:10
push!(circuit, GateCX(), 1, i)
end
The same is true for adding operations that act also on classical bits
push!(circuit, Measure(), 1:10, 1:10)
10-qubit circuit with 20 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── CX @ q[1], q[3]
├── CX @ q[1], q[4]
├── CX @ q[1], q[5]
├── CX @ q[1], q[6]
├── CX @ q[1], q[7]
├── CX @ q[1], q[8]
├── CX @ q[1], q[9]
├── CX @ q[1], q[10]
├── Measure @ q[1], c[1]
├── Measure @ q[2], c[2]
├── Measure @ q[3], c[3]
├── Measure @ q[4], c[4]
├── Measure @ q[5], c[5]
├── Measure @ q[6], c[6]
├── Measure @ q[7], c[7]
├── Measure @ q[8], c[8]
├── Measure @ q[9], c[9]
└── Measure @ q[10], c[10]
which is equivalent to
for i in 1:10
push!(circuit, Measure(), i, i)
end
The number of quantum bits and classical bits of a circuit is defined by the maximum index used, so in this case 10 for both.
numqubits(circuit), numbits(circuit)
With these informations, it is already possible to build any quantum circuit. However, for alternative advanced circuit building utilities see the documentation of MimiqCircuitsBase.emplace!
, and MimiqCircuitsBase.Circuit
.
Remote execution on MIMIQ-CIRC
In order to execute the implemented circuit on MIMIQ-CIRC three more steps are required:
- opening a connection to the MIMIQ Remote Services,
- send a circuit for execution,
- retrieve the results of the execution.
Connecting to MIMIQ
In most cases, connecting to MIMIQ can achieved by a single instruction
conn = connect()
Connection:
├── url: https://mimiqfast.qperfect.io/api
└── status: open
For more options please see the documentation of MimiqLink.connect
. If executed without supplemental arguments, connect()
will start a local webpage and will try to open it with your default browser. As an alternative, connect("john.smith@example.com", "jonspassword")
allows to insert directly the username and password of the user.
In order to complete this step you need an active subscription to MIMIQ-CIRC. To obtain one, please contact us or, if your organization already has a subscription, contact the organization account holder.
Executing a circuit on MIMIQ
Once a connection is established an execution can be sent to the remote services.
job = execute(conn, circuit)
Execution
└── 6663ab7ba56f78eacf38e4ce
This will execute a simulation of the given circuit with default parameters. The default choice of algorithm is "auto"
. Generally, there are three available options:
"auto"
for the automatically selecting the best algorithm according to circuit size and complexity,"statevector"
for a highly optimized state vector engine, and"mps"
for a large-scale Matrix Product States (MPS) method.
Check out the documentation of the MimiqCircuits.execute
function, for details.
OpenQASM
OpenQASM files, defining quantum algorithms can be executed on MIMIQ in the same way native circuits can, simply use MimiqCircuits.executeqasm
and provide the path of the file to upload. See the OpenQASM page for more details on how include files are handled.
Retrieving execution results
Once the execution has terminated on MIMIQ, the results can be retrieved via the MimiqCircuits.getresults
function, which returns a MimiqCircuitsBase.QCSResults
structure.
res = getresults(conn, job)
QCSRresults | |
Simulator | |
MIMIQ-StateVector 0.14.1 | |
Timings | |
amplitudes time | 1.65e-7s |
total time | 0.000284724s |
compression time | 7.257e-6s |
sample time | 0.000183326s |
apply time | 2.0865e-5s |
parse time | 0.000154943s |
Fidelity estimate | |
Single run value | 1.0 |
Average multiqubit error estimate | |
Single run value | 0.0 |
Statistics | |
Number of executions | 1 |
Number of samples | 1000 |
Number of amplitudes | 0 |
Samples | |
bs"0000000000" | 517 |
bs"1111111111" | 483 |
Name and version of the simulator, samples, and timings can be retrieved from the aggregated results. For example, to make an histogram out of the retrieved samples, it suffices to execute
histsamples(res)
Dict{BitString, Int64} with 2 entries:
bs"0000000000" => 517
bs"1111111111" => 483
To plot the results (works both with Plots.jl and Makie.jl)
using Plots
plot(res)
Retrieving submitted remote jobs
c, params = getinputs(conn, job)
# showing back the executed circuit, retrieeved from MIMIQ
c
10-qubit circuit with 20 instructions:
├── H @ q[1]
├── CX @ q[1], q[2]
├── CX @ q[1], q[3]
├── CX @ q[1], q[4]
├── CX @ q[1], q[5]
├── CX @ q[1], q[6]
├── CX @ q[1], q[7]
├── CX @ q[1], q[8]
├── CX @ q[1], q[9]
├── CX @ q[1], q[10]
├── Measure @ q[1], c[1]
├── Measure @ q[2], c[2]
├── Measure @ q[3], c[3]
├── Measure @ q[4], c[4]
├── Measure @ q[5], c[5]
├── Measure @ q[6], c[6]
├── Measure @ q[7], c[7]
├── Measure @ q[8], c[8]
├── Measure @ q[9], c[9]
└── Measure @ q[10], c[10]