Noisy distributed simulation with DNoisy¶

Simulation of quantum circuits on a HPC cluster

This notebook aims at introducing how distributed simulations of noisy quantum processors can be run on a HPC cluster in Qaptiva HPC. In the current version of Qaptiva HPC, the only available noisy distributed simulator is DNoisy, a generic simulator based on linear algebra. It can be used to parallelize simulations by distributing the density matrix of the quantum state on several compute nodes, enabling the exact simulation of larger circuits with more qubits.

Note that in the current version of Qaptiva HPC, DNoisy only supports deterministic simulations: while the DLinAlg simulator represents pure quantum states with state vectors composed of complex amplitudes, the DNoisy deterministic simulator represents the full density matrix of the qubits' state, either in double or single precision. As a result, DNoisy in the deterministic mode is both a noisy QPU and an exact QPU

  • Noisy, because it can simulate hardware noise
  • Exact, because the quantum state is stored in such a way that its representation does not induce any approximation other than the precision of floating point types

Since we change the representation of the quantum state with $n$ qubits from a state vector (containing $2^n$ complex numbers) to a density matrix (containing $2^n \times 2^n$ complex numbers), the complexity of simulating $n$ noisy qubits using DNoisy is the same as simulating $2n$ perfect qubits using DLinAlg. Hence, this approach demands an amount of memory that grows exponentially with the number of qubits, and quadratically worse than the noiseless simulation using DLinAlg. Each floating-point complex amplitude requires 16 (8) bytes to be stored in double (single) precision, which means that the total memory required to simulate a quantum circuit with $\textrm{nbqbits}$ is $2^{2 \, \textrm{nbqbits} +4}$ ($2^{2 \, \textrm{nbqbits} +3}$) bytes.

For example, a 20 qubits double precision noisy simulation with DNoisy would require $2^{44}$ bytes, which is 16 TiB. Assuming that the compute nodes have at least 256 GiB of memory each, we will then need 64 compute nodes to perform the distributed simulation.

Example simulation

In order to submit a distributed job on a cluster in a SLURM environment, we will follow the steps described below:

  1. Describe the quantum program and the hardware model in a python script, for example the file qaoa_dnoisy.py in the following cell
  2. Write a SLURM batch script that may contain options preceded with "#SBATCH", to define the resources to be used on the cluster to run the job
  3. Submit the SLURM batch script using the sbatch command
  4. Read the result of the quantum program from the output file generated by the SLURM command

Preparing a python script describing the quantum program and hardware model

The python script that runs a DNoisy distributed simulation ressembles scripts employed with other simulators. In the example below, we demonstrate the preparation of the maximally entangled state $\frac{\ket{0}^{\otimes n} + \ket{1}^{\otimes n}}{\sqrt{2}}$ circuit on noisy quantum hardware that is distributed to one MPI processes. For simplicity, we will create this state on only 8 qubits. However, it is important to note that this simulator is not intended for such small-scale use cases.

Please note that the DNoisy distributed simulator can work with or without SLURM. Under a SLURM environment, it will automatically deduce some options available in the environment as default values if they are not passed explicitly, for instance the number of process and threads to run the simulation on. Without SLURM, it will instead use the mpirun command, and all the options have to be passed by the user to the constructor of the simulator. For all the available options, please refer to the documentation of the DNoisy simulator.

In [1]:
%%writefile ghz_dnoisy.py

import numpy as np
import matplotlib.pyplot as plt

from qat.lang.AQASM import Program, H, CNOT

# Generate the circuit on 8 qbits
nqbits = 8

prog = Program()
qbits = prog.qalloc(nqbits)
prog.apply(H, qbits[0])
for i in range(0, nqbits - 1):
    prog.apply(CNOT, [qbits[i], qbits[i + 1]])

circ = prog.to_circ()
Writing ghz_dnoisy.py

The following cell is not required when submitting a real simulation, but it serves as an example to display the circuit in this notebook. It is not submitted to the compute nodes, and will run on the node where the notebook is executed.

In [2]:
%run ghz_dnoisy.py

circ.display()
No description has been provided for this image

Then, we need to create a HardwareModel to represent the noisy hardware. Here, we add depolarizing noise after the application of each gate, and add some noise on idle qubits

In [3]:
%%writefile -a ghz_dnoisy.py

from itertools import product
from qat.hardware import HardwareModel, DefaultGatesSpecification
from qat.quops import QuantumChannelKraus, ParametricAmplitudeDamping, ParametricPureDephasing

# Specify appliation time of each gate
gate_times = {
                "CNOT": 10,
                "H": 2, 
             }
# Create the GatesSpecification of the hardware, with perfect gates with nonzero application time
gates_spec = DefaultGatesSpecification(gate_times)

# Create an idle noise model, assuming that each qubit experiences the same AD/PD noise.  
idle_noise = [ParametricAmplitudeDamping(T_1=200), ParametricPureDephasing(T_phi=100)]

# Instantie a noise model representing a depolarizing noise, to be applyed after application of each perfect gate
# with probability 1%
prob = 0.01
Xmat = np.array([[0, 1], [1, 0]])
Ymat = np.array([[0, -1j], [1j, 0]])
Zmat = np.array([[1, 0], [0, -1]])
kraus_ops = [np.sqrt(1-prob)*np.identity(2),
             np.sqrt(prob/3)*Xmat, np.sqrt(prob/3)*Ymat, np.sqrt(prob/3)*Zmat]
noise = QuantumChannelKraus(kraus_ops)

# two-qbit version for the CNOT gate
noise2 = QuantumChannelKraus([np.kron(K1, K2) for K1, K2 in product(noise.kraus_operators, 
                                                                    noise.kraus_operators)])
# for each gate, we specify the noise
# note that the values in this dictionary are lambda functions
# with as many arguments as the gate's number of arguments
gates_noise = {"H": lambda: noise,
               "CNOT": lambda: noise2,
               }

#Create the hardware model, with gate noise and idle noise

hardware_model = HardwareModel(gates_spec, gates_noise, idle_noise)
Appending to ghz_dnoisy.py

Once the hardware model is defined, we can write the job that must be executed, and define our DNoisy QPU with the correct hardware model

In [4]:
%%writefile -a ghz_dnoisy.py

from qat.qpus import DNoisy

# The nb_processes here is set to 1, so the simulation will only run on a single node
# This argument is optional and the number of reserved nodes in the SLURM environment will be used by default
nb_processes = 1
# The temp_dir argument must be configured to point to a writable temporary directory shared by all the nodes in the cluster
# if the directory containing this Python script does not have write permissions or is not shared
temp_dir = "/tmp/"
qpu = DNoisy(nb_processes=nb_processes, temp_dir=temp_dir, hardware_model=hardware_model)

job = circ.to_job(nbshots=10000)

res = qpu.submit(job)

for sample in res:
    print(f"Sampled state {sample.state} with probability {sample.probability} and error {sample.err}")
Appending to ghz_dnoisy.py

Writing the SLURM batch script to define the resources needed to run the job

In the slurm batch script defined in the following cell, the partition in the cluster to run the job on is not specified, so the default partition as designated by the system administrator will be used. However, this can be added through the --partition option. For this example, we will only use a single node, and 64 cpus in the nodes will be used for the simulation. A time limit can be set on the slurm job through the --time option, and it is recommended to do so. Please refer to the official slurm documentation for additional options and examples.

In [5]:
%%file submit.sh
#!/bin/sh

#SBATCH --nodes 1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=64
#SBATCH --time 00:03:00
#SBATCH --job-name ghz_dnoisy
#SBATCH --output slurm_output/ghz_dnoisy.out
#SBATCH --error slurm_output/ghz_dnoisy.err
#SBATCH --exclusive

# Load the python3 module to be used on the cluster
# module load python3

echo "Loading the qaptiva-hpc module environment"
module load qaptiva-hpc

# Load other modules if necessary such as openmpi
# module load openmpi

echo "Running the DNoisy simulation"
python3 ghz_dnoisy.py
Writing submit.sh

Submitting the SLURM job on the cluster¶

To submit the simulation job to the compute nodes in the cluster, we simply use the sbatch SLURM command, as shown in the following cell. If we are not in a SLURM environment, we can also use the mpirun command to run the python script, but the resources needed by the simulation job will need to be specified in the command.

In [6]:
%%bash
sbatch --wait --deadline=now+4minutes submit.sh
Submitted batch job 1740

Reading the result from the SLURM output file¶

When the simulation job is done, we can read the output file of the SLURM job to retrieve simulation result, or the error file to check if the simulation job is successfully run.

In [7]:
%%bash
cat slurm_output/ghz_dnoisy.out
Loading the qaptiva-hpc module environment
Running the DNoisy simulation
Sampled state |00000000> with probability 0.4636 and error 0.004986982153477182
Sampled state |00000001> with probability 0.0028 and error 0.000528435354979622
Sampled state |00000010> with probability 0.0032 and error 0.0005648078441293809
Sampled state |00000011> with probability 0.0031 and error 0.0005559405606633672
Sampled state |00000100> with probability 0.002 and error 0.00044678849805718994
Sampled state |00000111> with probability 0.0043 and error 0.0006543652033703643
Sampled state |00001000> with probability 0.0027 and error 0.0005189392351643681
Sampled state |00001011> with probability 0.0004 and error 0.00019996999474891224
Sampled state |00001100> with probability 0.0001 and error 0.0001
Sampled state |00001110> with probability 0.0001 and error 0.0001
Sampled state |00001111> with probability 0.0028 and error 0.000528435354979622
Sampled state |00010000> with probability 0.0027 and error 0.0005189392351643681
Sampled state |00010111> with probability 0.0009 and error 0.00029987996398439146
Sampled state |00011000> with probability 0.0001 and error 0.0001
Sampled state |00011011> with probability 0.0004 and error 0.00019996999474891224
Sampled state |00011101> with probability 0.0001 and error 0.0001
Sampled state |00011111> with probability 0.0063 and error 0.0007912607720346916
Sampled state |00100000> with probability 0.0029 and error 0.0005377619536485258
Sampled state |00100111> with probability 0.0008 and error 0.00028274369029111855
Sampled state |00101000> with probability 0.0001 and error 0.0001
Sampled state |00101100> with probability 0.0001 and error 0.0001
Sampled state |00101110> with probability 0.0001 and error 0.0001
Sampled state |00101111> with probability 0.0026 and error 0.0005092641112364972
Sampled state |00110111> with probability 0.0022 and error 0.0004685487741904567
Sampled state |00111001> with probability 0.0001 and error 0.0001
Sampled state |00111011> with probability 0.0017 and error 0.00041198054905211204
Sampled state |00111100> with probability 0.0002 and error 0.00014141428428549925
Sampled state |00111101> with probability 0.0001 and error 0.0001
Sampled state |00111110> with probability 0.0002 and error 0.00014141428428549925
Sampled state |00111111> with probability 0.0196 and error 0.0013862814207864728
Sampled state |01000000> with probability 0.0037 and error 0.0006071802588907833
Sampled state |01000100> with probability 0.0001 and error 0.0001
Sampled state |01000111> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01001000> with probability 0.0001 and error 0.0001
Sampled state |01001011> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01001111> with probability 0.0017 and error 0.00041198054905211204
Sampled state |01010000> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01010010> with probability 0.0001 and error 0.0001
Sampled state |01010111> with probability 0.0018 and error 0.0004239032547609651
Sampled state |01011000> with probability 0.0001 and error 0.0001
Sampled state |01011011> with probability 0.001 and error 0.00031608541725157126
Sampled state |01011100> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01011110> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01011111> with probability 0.0137 and error 0.0011624827468025756
Sampled state |01100000> with probability 0.0008 and error 0.00028274369029111855
Sampled state |01100111> with probability 0.0008 and error 0.00028274369029111855
Sampled state |01101000> with probability 0.0004 and error 0.00019996999474891224
Sampled state |01101010> with probability 0.0001 and error 0.0001
Sampled state |01101011> with probability 0.0009 and error 0.00029987996398439146
Sampled state |01101101> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01101111> with probability 0.011 and error 0.001043076603553204
Sampled state |01110000> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01110011> with probability 0.0005 and error 0.00022356206744392033
Sampled state |01110111> with probability 0.006 and error 0.0007723079994177171
Sampled state |01111000> with probability 0.0006 and error 0.00024488772325231
Sampled state |01111001> with probability 0.0001 and error 0.0001
Sampled state |01111011> with probability 0.0031 and error 0.0005559405606633672
Sampled state |01111100> with probability 0.0002 and error 0.00014141428428549925
Sampled state |01111101> with probability 0.0001 and error 0.0001
Sampled state |01111110> with probability 0.0004 and error 0.00019996999474891224
Sampled state |01111111> with probability 0.0574 and error 0.0023261696211821377
Sampled state |10000000> with probability 0.0052 and error 0.0007192688890626907
Sampled state |10000010> with probability 0.0001 and error 0.0001
Sampled state |10000100> with probability 0.0001 and error 0.0001
Sampled state |10000111> with probability 0.0005 and error 0.00022356206744392033
Sampled state |10001000> with probability 0.0001 and error 0.0001
Sampled state |10001111> with probability 0.0021 and error 0.0004577990366916466
Sampled state |10010011> with probability 0.0002 and error 0.00014141428428549925
Sampled state |10010111> with probability 0.001 and error 0.00031608541725157126
Sampled state |10011110> with probability 0.0003 and error 0.00017318775765030273
Sampled state |10011111> with probability 0.0106 and error 0.001024143001191088
Sampled state |10100000> with probability 0.0011 and error 0.00033149658972438903
Sampled state |10100001> with probability 0.0001 and error 0.0001
Sampled state |10100010> with probability 0.0001 and error 0.0001
Sampled state |10100111> with probability 0.0006 and error 0.00024488772325231
Sampled state |10101000> with probability 0.0001 and error 0.0001
Sampled state |10101011> with probability 0.0006 and error 0.00024488772325231
Sampled state |10101110> with probability 0.0001 and error 0.0001
Sampled state |10101111> with probability 0.0075 and error 0.0008628148381573502
Sampled state |10110000> with probability 0.0005 and error 0.00022356206744392033
Sampled state |10110010> with probability 0.0001 and error 0.0001
Sampled state |10110011> with probability 0.0004 and error 0.00019996999474891224
Sampled state |10110100> with probability 0.0001 and error 0.0001
Sampled state |10110111> with probability 0.0046 and error 0.0006767051004531427
Sampled state |10111000> with probability 0.0004 and error 0.00019996999474891224
Sampled state |10111011> with probability 0.0036 and error 0.0005989489744438992
Sampled state |10111100> with probability 0.0002 and error 0.00014141428428549925
Sampled state |10111101> with probability 0.0002 and error 0.00014141428428549925
Sampled state |10111110> with probability 0.0003 and error 0.00017318775765030273
Sampled state |10111111> with probability 0.0476 and error 0.0021292903483881138
Sampled state |11000000> with probability 0.0018 and error 0.0004239032547609651
Sampled state |11000001> with probability 0.0001 and error 0.0001
Sampled state |11000011> with probability 0.0003 and error 0.00017318775765030273
Sampled state |11000111> with probability 0.0007 and error 0.00026449573871724277
Sampled state |11001000> with probability 0.0001 and error 0.0001
Sampled state |11001011> with probability 0.0005 and error 0.00022356206744392033
Sampled state |11001101> with probability 0.0001 and error 0.0001
Sampled state |11001110> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11001111> with probability 0.0052 and error 0.0007192688890626907
Sampled state |11010000> with probability 0.0003 and error 0.00017318775765030273
Sampled state |11010001> with probability 0.0001 and error 0.0001
Sampled state |11010011> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11010111> with probability 0.0038 and error 0.0006152998126002791
Sampled state |11011000> with probability 0.0003 and error 0.00017318775765030273
Sampled state |11011001> with probability 0.0001 and error 0.0001
Sampled state |11011011> with probability 0.0019 and error 0.00043549737748530665
Sampled state |11011101> with probability 0.0004 and error 0.00019996999474891224
Sampled state |11011110> with probability 0.0003 and error 0.00017318775765030273
Sampled state |11011111> with probability 0.0392 and error 0.001940802069059884
Sampled state |11100000> with probability 0.0015 and error 0.00038702710369934015
Sampled state |11100110> with probability 0.0001 and error 0.0001
Sampled state |11100111> with probability 0.0034 and error 0.0005821321909916843
Sampled state |11101000> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11101011> with probability 0.0014 and error 0.0003739224275142905
Sampled state |11101100> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11101110> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11101111> with probability 0.0279 and error 0.0016469457316573314
Sampled state |11110000> with probability 0.0014 and error 0.0003739224275142905
Sampled state |11110001> with probability 0.0001 and error 0.0001
Sampled state |11110011> with probability 0.0011 and error 0.00033149658972438903
Sampled state |11110111> with probability 0.0184 and error 0.0013439957709990187
Sampled state |11111000> with probability 0.0009 and error 0.00029987996398439146
Sampled state |11111001> with probability 0.0002 and error 0.00014141428428549925
Sampled state |11111010> with probability 0.0001 and error 0.0001
Sampled state |11111011> with probability 0.0115 and error 0.0010662498247919607
Sampled state |11111100> with probability 0.0007 and error 0.00026449573871724277
Sampled state |11111101> with probability 0.0015 and error 0.00038702710369934015
Sampled state |11111110> with probability 0.001 and error 0.00031608541725157126
Sampled state |11111111> with probability 0.1518 and error 0.003588448647587093