CUDA-Q Simulation Backends¶
The simulation backends that are currently available in CUDA-Q are as follows.
State Vector Simulators¶
Single-GPU¶
The nvidia
target provides a state vector simulator accelerated with
the cuStateVec
library.
To execute a program on the nvidia
target, use the following commands:
python3 program.py [...] --target nvidia
The target can also be defined in the application code by calling
cudaq.set_target('nvidia')
If a target is set in the application code, this target will override the --target
command line flag given during program invocation.
nvq++ --target nvidia program.cpp [...] -o program.x
./program.x
By default, this will leverage FP32
floating point types for the simulation. To
switch to FP64
, specify the nvidia-fp64
target instead.
Note
This backend requires an NVIDIA GPU and CUDA runtime libraries. If you do not have these dependencies installed, you may encounter an error stating Invalid simulator requested
. See the section Dependencies and Compatibility for more information about how to install dependencies.
Multi-node multi-GPU¶
The nvidia-mgpu
target provides a state vector simulator accelerated with
the cuStateVec
library but with support for Multi-Node, Multi-GPU distribution of the
state vector, in addition to a single GPU.
The multi-node multi-GPU simulator expects to run within an MPI context.
To execute a program on the nvidia-mgpu
target, use the following commands (adjust the value of the -np
flag as needed to reflect available GPU resources on your system):
mpiexec -np 2 python3 program.py [...] --target nvidia-mgpu
Note
If you installed CUDA-Q via pip
, you will need to install the necessary MPI dependencies separately;
please follow the instructions for installing dependencies in the Project Description.
In addition to using MPI in the simulator, you can use it in your application code by installing mpi4py, and invoking the program with the command
mpiexec -np 2 python3 -m mpi4py program.py [...] --target nvidia-mgpu
The target can also be defined in the application code by calling
cudaq.set_target('nvidia-mgpu')
If a target is set in the application code, this target will override the --target
command line flag given during program invocation.
nvq++ --target nvidia-mgpu program.cpp [...] -o program.x
mpiexec -np 2 ./program.x
Note
This backend requires an NVIDIA GPU, CUDA runtime libraries, as well as an MPI installation. If you do not have these dependencies installed, you may encounter either an error stating invalid simulator requested
(missing CUDA libraries), or an error along the lines of failed to launch kernel
(missing MPI installation). See the section Dependencies and Compatibility for more information about how to install dependencies.
The nvidia-mgpu
backend has additional performance improvements to
help reduce your simulation runtimes, even on a single GPU. One of the
performance improvements is to fuse multiple gates together during runtime. For
example, x(qubit0)
and x(qubit1)
can be fused together into a
single 4x4 matrix operation on the state vector rather than 2 separate 2x2
matrix operations on the state vector. This fusion reduces memory bandwidth on
the GPU because the state vector is transferred into and out of memory fewer
times. By default, up to 4 gates are fused together for single-GPU simulations,
and up to 6 gates are fused together for multi-GPU simulations. The number of
gates fused can significantly affect performance of some circuits, so users
can override the default fusion level by setting the setting CUDAQ_MGPU_FUSE
environment variable to another integer value as shown below.
CUDAQ_MGPU_FUSE=5 mpiexec -np 2 python3 program.py [...] --target nvidia-mgpu
nvq++ --target nvidia-mgpu program.cpp [...] -o program.x
CUDAQ_MGPU_FUSE=5 mpiexec -np 2 ./program.x
OpenMP CPU-only¶
This target provides a state vector simulator based on the CPU-only, OpenMP threaded Q++ library. This is the default target when running on CPU-only systems.
To execute a program on the qpp-cpu
target even if a GPU-accelerated backend is available,
use the following commands:
python3 program.py [...] --target qpp-cpu
The target can also be defined in the application code by calling
cudaq.set_target('qpp-cpu')
If a target is set in the application code, this target will override the --target
command line flag given during program invocation.
nvq++ --target qpp-cpu program.cpp [...] -o program.x
./program.x
Tensor Network Simulators¶
CUDA-Q provides a couple of tensor-network simulator targets accelerated with
the cuTensorNet
library.
These backends are available for use from both C++ and Python.
Tensor network-based simulators are suitable for large-scale simulation of certain classes of quantum circuits involving many qubits beyond the memory limit of state vector based simulators. For example, computing the expectation value of a Hamiltonian via cudaq::observe
can be performed efficiently, thanks to cuTensorNet
contraction optimization capability. On the other hand, conditional circuits, i.e., those with mid-circuit measurements or reset, despite being supported by both backends, may result in poor performance.
Multi-node multi-GPU¶
The tensornet
backend represents quantum states and circuits as tensor networks in an exact form (no approximation).
Measurement samples and expectation values are computed via tensor network contractions.
This backend supports multi-node, multi-GPU distribution of tensor operations required to evaluate and simulate the circuit.
To execute a program on the tensornet
target using a single GPU, use the following commands:
python3 program.py [...] --target tensornet
The target can also be defined in the application code by calling
cudaq.set_target('tensornet')
If a target is set in the application code, this target will override the --target
command line flag given during program invocation.
nvq++ --target tensornet program.cpp [...] -o program.x
./program.x
If you have multiple GPUs available on your system, you can use MPI to automatically distribute parallelization across the visible GPUs.
Note
If you installed the CUDA-Q Python wheels, distribution across multiple GPUs is currently not supported for this backend. We will add support for it in future releases. For more information, see this GitHub issue.
Use the following commands to enable distribution across multiple GPUs (adjust the value of the -np
flag as needed to reflect available GPU resources on your system):
mpiexec -np 2 python3 program.py [...] --target tensornet
In addition to using MPI in the simulator, you can use it in your application code by installing mpi4py, and invoking the program with the command
mpiexec -np 2 python3 -m mpi4py program.py [...] --target tensornet
nvq++ --target tensornet program.cpp [...] -o program.x
mpiexec -np 2 ./program.x
Note
If the CUTENSORNET_COMM_LIB
environment variable is not set, MPI parallelization on the tensornet
backend may fail.
If you are using a CUDA-Q container, this variable is pre-configured and no additional setup is needed. If you are customizing your installation or have built CUDA-Q from source, please follow the instructions for activating the distributed interface for the cuTensorNet
library. This requires
installing CUDA development dependencies, and setting the CUTENSORNET_COMM_LIB
environment variable to the newly built libcutensornet_distributed_interface_mpi.so
library.
Specific aspects of the simulation can be configured by setting the following of environment variables:
`CUDA_VISIBLE_DEVICES=X`: Makes the process only see GPU X on multi-GPU nodes. Each MPI process must only see its own dedicated GPU. For example, if you run 8 MPI processes on a DGX system with 8 GPUs, each MPI process should be assigned its own dedicated GPU via
CUDA_VISIBLE_DEVICES
when invokingmpiexec
(ormpirun
) commands.`OMP_PLACES=cores`: Set this environment variable to improve CPU parallelization.
`OMP_NUM_THREADS=X`: To enable CPU parallelization, set X to
NUMBER_OF_CORES_PER_NODE/NUMBER_OF_GPUS_PER_NODE
.
Note
This backend requires an NVIDIA GPU and CUDA runtime libraries.
If you do not have these dependencies installed, you may encounter an error stating Invalid simulator requested
.
See the section Dependencies and Compatibility for more information about how to install dependencies.
Note
Setting random seed, via cudaq::set_random_seed
, is not supported for this backend due to a limitation of the cuTensorNet
library. This will be fixed in future release once this feature becomes available.
Matrix product state¶
The tensornet-mps
backend is based on the matrix product state (MPS) representation of the state vector/wave function, exploiting the sparsity in the tensor network via tensor decomposition techniques such as QR and SVD. As such, this backend is an approximate simulator, whereby the number of singular values may be truncated to keep the MPS size tractable.
The tensornet-mps
backend only supports single-GPU simulation. Its approximate nature allows the tensornet-mps
backend to handle a large number of qubits for certain classes of quantum circuits on a relatively small memory footprint.
To execute a program on the tensornet-mps
target, use the following commands:
python3 program.py [...] --target tensornet-mps
The target can also be defined in the application code by calling
cudaq.set_target('tensornet-mps')
If a target is set in the application code, this target will override the --target
command line flag given during program invocation.
nvq++ --target tensornet-mps program.cpp [...] -o program.x
./program.x
Specific aspects of the simulation can be configured by defining the following environment variables:
`CUDAQ_MPS_MAX_BOND=X`: The maximum number of singular values to keep (fixed extent truncation). Default: 64.
`CUDAQ_MPS_ABS_CUTOFF=X`: The cutoff for the largest singular value during truncation. Eigenvalues that are smaller will be trimmed out. Default: 1e-5.
`CUDAQ_MPS_RELATIVE_CUTOFF=X`: The cutoff for the maximal singular value relative to the largest eigenvalue. Eigenvalues that are smaller than this fraction of the largest singular value will be trimmed out. Default: 1e-5
Note
This backend requires an NVIDIA GPU and CUDA runtime libraries.
If you do not have these dependencies installed, you may encounter an error stating Invalid simulator requested
.
See the section Dependencies and Compatibility for more information about how to install dependencies.
Note
Setting random seed, via cudaq::set_random_seed
, is not supported for this backend due to a limitation of the cuTensorNet
library. This will be fixed in future release once this feature becomes available.
Default Simulator¶
If no explicit target is set, i.e. if the code is compiled without any --target
flags, then CUDA-Q makes a default choice for the simulator.
If an NVIDIA GPU and CUDA runtime libraries are available, the default target is set to nvidia
. This will utilize the cuQuantum single-GPU state vector simulator.
On CPU-only systems, the default target is set to qpp-cpu
which uses the OpenMP CPU-only simulator.
The default simulator can be overridden by the environment variable CUDAQ_DEFAULT_SIMULATOR
. If no target is explicitly specified and the environment variable has a valid value, then it will take effect.
This environment variable can be set to any non-hardware backend. Any invalid value is ignored.
For CUDA-Q Python API, the environment variable at the time when cudaq
module is imported is relevant, not the value of the environment variable at the time when the simulator is invoked.
For example,
CUDAQ_DEFAULT_SIMULATOR=density-matrix-cpu python3 program.py [...]
CUDAQ_DEFAULT_SIMULATOR=density-matrix-cpu nvq++ program.cpp [...] -o program.x
./program.x
This will use the density matrix simulator target.
Note
To use targets that require an NVIDIA GPU and CUDA runtime libraries, the dependencies must be installed, else you may encounter an error stating Invalid simulator requested
. See the section Dependencies and Compatibility for more information about how to install dependencies.