Source code for catalyst.cuda
# Copyright 2024 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This module contains a CudaQDevice and the qjit
entry point.
"""
from importlib.metadata import version
from pathlib import Path
import cudaq
import pennylane as qml
def _check_version_compatibility():
installed_version = version("cuda_quantum")
compatible_version = "0.6.0"
if installed_version != compatible_version:
msg = f"Compiling with incompatible version cuda_quantum=={installed_version}. "
msg += f"Please install compatible version cuda_quantum=={compatible_version}."
raise ModuleNotFoundError(msg)
[docs]def cudaqjit(fn=None, **kwargs):
"""A decorator for compiling PennyLane and JAX programs using CUDA Quantum.
.. important::
This feature currently only supports CUDA Quantum version 0.6.
.. note::
Currently, only the following devices are supported:
* :class:`softwareq.qpp <SoftwareQQPP>`: a modern C++ statevector simulator
* :class:`nvidia.statevec <NvidiaCuStateVec>`: The NVIDIA CuStateVec GPU simulator
(with support for multi-gpu)
* :class:`nvidia.tensornet <NvidiaCuTensorNet>`: The NVIDIA CuTensorNet GPU simulator
(with support for matrix product state)
Args:
fn (Callable): the quantum or classical function to compile
Returns:
QJIT object.
**Example**
The compilation is triggered at the call site the
when the quantum function is executed:
.. code-block:: python
dev = qml.device("softwareq.qpp", wires=2)
@cudaqjit
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliY(0))
>>> circuit(jnp.array([0.5, 1.4]))
-0.47244976756708373
From PennyLane, this functionality can also be accessed via
>>> @qml.qjit(compiler="cuda_quantum")
Note that CUDA Quantum compilation currently does not have feature parity with Catalyst
compilation; in particular, AutoGraph, control flow, differentiation, and various measurement
statistics (such as probabilities and variance) are not yet supported.
"""
_check_version_compatibility()
# pylint: disable-next=import-outside-toplevel
from catalyst.cuda.catalyst_to_cuda_interpreter import interpret
if fn is not None:
return interpret(fn, **kwargs)
def wrap_fn(fn):
return interpret(fn, **kwargs)
return wrap_fn
# Do we need to reimplement apply for every child?
# pylint: disable=abstract-method
[docs]class BaseCudaInstructionSet(qml.QubitDevice):
"""Base instruction set for CUDA-Quantum devices"""
pennylane_requires = ">=0.34"
version = "0.1.0"
author = "Xanadu, Inc."
# There are similar lines of code in possibly
# all other list of operations supported by devices.
# At the time of writing, this warning is raised
# due to similar lines of code in the QJITDevice
# pylint: disable=duplicate-code
operations = [
"CNOT",
"CY",
"CZ",
"CRX",
"CRY",
"CRZ",
"PauliX",
"PauliY",
"PauliZ",
"Hadamard",
"S",
"T",
"RX",
"RY",
"RZ",
"SWAP",
"CSWAP",
]
observables = []
config = Path(__file__).parent / "cuda_quantum.toml"
def __init__(self, shots=None, wires=None):
_check_version_compatibility()
super().__init__(wires=wires, shots=shots)
[docs] def apply(self, operations, **kwargs):
"""Unused"""
raise NotImplementedError(
"This device is only supported with `qml.qjit`."
) # pragma: no cover
[docs]class SoftwareQQPP(BaseCudaInstructionSet):
"""The SoftwareQ Q++ statevector simulator.
.. note::
This device currently only supports QNodes compiled with CUDA Quantum. For a
high-performance CPU device with support with other compilers, please use
``lightning.qubit`` or ``lightning.kokkos``.
Args:
shots (None, int): Number of shots to use for measurments and statistics.
``None`` corresponds to exact statistics.
wires (int): Number of wires present on the device.
**Example**
.. code-block:: python
dev = qml.device("softwareq.qpp", wires=2)
@catalyst.cuda.cudaqjit
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliY(0))
>>> circuit(jnp.array([0.5, 1.4]))
-0.47244976756708373
"""
short_name = "softwareq.qpp"
@property
def name(self):
"""Target name"""
return "qpp-cpu"
[docs]class NvidiaCuStateVec(BaseCudaInstructionSet):
"""The NVIDIA CuStateVec GPU simulator (with support for multi-gpu).
.. note::
This device currently only supports QNodes compiled with CUDA Quantum. For a multi-GPU
device with support with other compilers, please use ``lightning.gpu``.
Args:
shots (None, int): Number of shots to use for measurments and statistics.
``None`` corresponds to exact statistics.
wires (int): Number of wires present on the device.
multi_gpu (bool): Whether to utilize multiple GPUs.
**Example**
.. code-block:: python
dev = qml.device("nvidia.custatevec", wires=2)
@catalyst.cuda.cudaqjit
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliY(0))
>>> circuit(jnp.array([0.5, 1.4]))
-0.47244976756708373
"""
short_name = "nvidia.custatevec"
def __init__(self, shots=None, wires=None, multi_gpu=False): # pragma: no cover
self.multi_gpu = multi_gpu
super().__init__(wires=wires, shots=shots)
@property
def name(self): # pragma: no cover
"""Target name"""
option = "-mgpu" if self.multi_gpu else ""
return f"nvidia{option}"
[docs]class NvidiaCuTensorNet(BaseCudaInstructionSet):
"""The NVIDIA CuTensorNet GPU simulator (with support for matrix product state)
.. note::
This device currently only supports QNodes compiled with CUDA Quantum.
Args:
shots (None, int): Number of shots to use for measurments and statistics.
``None`` corresponds to exact statistics.
wires (int): Number of wires present on the device.
mps (bool): Whether to use matrix product state approximations.
**Example**
.. code-block:: python
dev = qml.device("nvidia.cutensornet", wires=2)
@catalyst.cuda.cudaqjit
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliY(0))
>>> circuit(jnp.array([0.5, 1.4]))
-0.47244976756708373
"""
short_name = "nvidia.cutensornet"
def __init__(self, shots=None, wires=None, mps=False): # pragma: no cover
self.mps = mps
super().__init__(wires=wires, shots=shots)
@property
def name(self): # pragma: no cover
"""Target name"""
option = "-mps" if self.mps else ""
return f"tensornet{option}"
__all__ = [
"cudaqjit",
"BaseCudaInstructionSet",
"SoftwareQQPP",
"NvidiaCuStateVec",
"NvidiaCuTensorNet",
]
_modules/catalyst/cuda
Download Python script
Download Notebook
View on GitHub