First Distributed Executionο
Once CUNQA is installed (Module Set Up), the basic workflow to use it is:
Raise the desired QPUs with the command
qraise
.Run circuits on the QPUs:
Connect to the QPUs through the python API.
Define the circuits to execute.
Execute the circuits on the QPUs.
Obtain the results.
Drop the raised QPUs with the command
qdrop
. ..β Important: Please, note that steps 1-4 of the Installation section have to be done every time CUNQA wants to be used.
1. qraise
commandο
The qraise
command raises as many QPUs as desired. Each QPU can be configured by the user to have a personalized backend. There is a help FLAG with a quick guide of how this command works:
qraise --help
The only two mandatory FLAGS of
qraise
are the number of QPUs, set up with-n
or--num_qpus
and the maximum time the QPUs will be raised, set up with-t
or--time
. So, for instance, the command .. code-block:qraise -n 4 -t 01:20:30
will raise four QPUs during at most 1 hour, 20 minutes and 30 seconds. The time format is
hh:mm:ss
.
π Note: By default, all the QPUs will be raised with AerSimulator as the background simulator and IdealAer as the background backend. That is, a backend of 32 qubits, all connected and without noise.
The simulator and the backend configuration can be set by the user through
qraise
FLAGs:
Set simulator:
qraise -n 4 -t 01:20:30 --sim=Munich
The command above changes the default simulator by the mqt-ddsim simulator. Currently, CUNQA only allows two simulators: --sim=Aer
and --sim=Munich
.
Set FakeQmio:
qraise -n 4 -t 01:20:30 --fakeqmio=<path/to/calibrations/file>
The --fakeqmio
FLAG raises the QPUs as simulated QMIOs. If no <path/to/calibrations/file>
is provided, last calibrations of de QMIO are used. With this FLAG, the background simulator is AerSimulator.
Set personalized backend:
qraise -n 4 -t 01:20:30 --backend=<path/to/backend/json>
The personalized backend has to be a json file with the following structure:
{"backend":{"name": "BackendExample", "version": "0.0", "n_qubits": 32,"url": "", "is_simulator": true, "conditional": true, "memory": true, "max_shots": 1000000, "description": "", "basis_gates": [], "custom_instructions": "", "gates": [], "coupling_map": []}, "noise": {}}
π Note: The βnoiseβ key must be filled with a json with noise instructions supported by the chosen simulator.
β Important: Several
qraise
commands can be executed one after another to raise as many QPUs as desired, each one having its own configuration, independently of the previous ones. ThegetQPUs()
method presented in the section below will collect all the raised QPUs.
2. Python Program Exampleο
Once the QPUs are raised, they are ready to execute any quantum circuit. The following script shows a basic workflow.
β οΈ Warning: To execute the following python example it is needed to load the Qiskit module:
In QMIO:
module load qmio/hpc gcc/12.3.0 qiskit/1.2.4-python-3.9.9
In FT3:
module load cesga/2022 gcc/system qiskit/1.2.4
# Python Script Example
import os
import sys
# Adding pyhton folder path to detect modules
INSTALL_PATH = os.getenv("INSTALL_PATH")
sys.path.insert(0, INSTALL_PATH)
# Let's get the raised QPUs
from cunqa.qpu import getQPUs
qpus = getQPUs() # List of raised QPUs
for q in qpus:
print(f"QPU {q.id}, name: {q.backend.name}, backend: {q.backend.simulator}, version: {q.backend.version}.")
# Let's create a circuit to run in our QPUs
from qiskit import QuantumCircuit
N_QUBITS = 2 # Number of qubits
qc = QuantumCircuit(N_QUBITS)
qc.h(0)
qc.cx(0,1)
qc.measure_all()
# Time to run
qpu0 = qpu[0] # Select one of the raise QPUs
job = qpu0.run(qc, transpile = True, shots = 1000)
result = job.result() # Get the result of the execution
counts = result.get_counts()
It is not mandatory to run a QuantumCircuit from Qiskit. The .run
method also supports OpenQASM 2.0 with the following structure:
{"instructions":"OPENQASM 2.0;\ninclude \"qelib1.inc\";\nqreg q[2];\ncreg c[2];\nh q[0];\ncx q[0], q[1];\nmeasure q[0] -> c[0];\nmeasure q[1] -> c[1];" , "num_qubits": 2, "num_clbits": 4, "quantum_registers": {"q": [0, 1]}, "classical_registers": {"c": [0, 1], "other_measure_name": [2], "meas": [3]}}
and json format with the following structure:
{"instructions": [{"name": "h", "qubits": [0], "params": []},{"name": "cx", "qubits": [0, 1], "params": []}, {"name": "rx", "qubits": [0], "params": [0.39528385768119634]}, {"name": "measure", "qubits": [0], "memory": [0]}], "num_qubits": 2, "num_clbits": 4, "quantum_registers": {"q": [0, 1]}, "classical_registers": {"c": [0, 1], "other_measure_name": [2], "meas": [3]}}
3. qdrop
commandο
Once the work is finished, the raised QPUs should be dropped in order to not monopolize computational resources.
The qdrop
command can be used to drop all the QPUs raised with a single qraise
by passing the corresponding qraise SLURM_JOB_ID
:
qdrop SLURM_JOB_ID
Note that the SLURM_JOB_ID
can be obtained, for instance, executing the squeue
command.
To drop all the raised QPUs, just execute:
qdrop --all