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:

  1. opening a connection to the MIMIQ Remote Services,
  2. send a circuit for execution,
  3. 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.

Note

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 time1.65e-7s
total time0.000284724s
compression time7.257e-6s
sample time0.000183326s
apply time2.0865e-5s
parse time0.000154943s
Fidelity estimate
Single run value1.0
Average multiqubit error estimate
Single run value0.0
Statistics
Number of executions1
Number of samples1000
Number of amplitudes0
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)
Example block output

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]