Skip to content

Commit

Permalink
Merge branch 'main' into py-cpp-interop
Browse files Browse the repository at this point in the history
  • Loading branch information
amccaskey authored Oct 7, 2024
2 parents 2b1e6ba + ebaf920 commit a38ee71
Show file tree
Hide file tree
Showing 168 changed files with 5,600 additions and 716 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ env:
NGC_QUANTUM_TEAM: cuda-quantum
NVQC_FUNCTION_ID: 3bfa0342-7d2a-4f1b-8e81-b6608d28ca7d
# <Backend>:<GPU Type>:<Instance Type>:<Min Instances>:<Max Instances>
NGC_NVQC_DEPLOYMENT_SPEC: GFN:L40:gl40_1.br20_2xlarge:1:1
NGC_NVQC_DEPLOYMENT_SPEC: GFN:L40S:gl40s_1.br25_2xlarge:1:1
# If vars below are changed, it is recommended to also update the
# workflow_dispatch defaults above so they stay in sync.
cudaq_test_image: nvcr.io/nvidia/nightly/cuda-quantum:latest
Expand Down Expand Up @@ -607,8 +607,8 @@ jobs:
for filename in `find targettests/Remote-Sim -name '*.cpp'`; do
# unsupport_args and compile_errors are compile error tests
# nested_vectors: Compiler fails to synthesize nested vector parameters (https://github.com/NVIDIA/cuda-quantum/issues/2001)
# state_init: New argument synthesis is not executed for nvqc (https://github.com/NVIDIA/cuda-quantum/issues/2146)
if [[ "$filename" != *"unsupport_args"* ]] && [[ "$filename" != *"compile_errors"* ]] && [[ "$filename" != *"nested_vectors"* ]] && [[ "$filename" != *"qvector_init_from_state"* ]]; then
# qvector_init_from_state, qvector_init_from_state_lazy, test_trotter: New argument synthesis is not executed for nvqc (https://github.com/NVIDIA/cuda-quantum/issues/2146)
if [[ "$filename" != *"unsupport_args"* ]] && [[ "$filename" != *"compile_errors"* ]] && [[ "$filename" != *"nested_vectors"* ]] && [[ "$filename" != *"qvector_init_from_state"* ]] && [[ "$filename" != *"qvector_init_from_state_lazy"* ]] && [[ "$filename" != *"test_trotter"* ]]; then
echo "$filename"
nvqc_config=""
# Look for a --remote-mqpu-auto-launch to determine the number of QPUs
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/nvqc_regression_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ jobs:
# nested_vectors: related to vector of pauli_words (https://github.com/NVIDIA/cuda-quantum/issues/1957)
# custom_operation: https://github.com/NVIDIA/cuda-quantum/issues/1985
# return_values: only supported in 0.8 NVQC service.
# state_init: only supported in 0.8 NVQC service.
if [[ "$filename" != *"unsupport_args"* ]] && [[ "$filename" != *"state_overlap"* ]] && [[ "$filename" != *"compile_errors"* ]] && [[ "$filename" != *"nested_vectors"* ]] && [[ "$filename" != *"pauli_word"* ]] && [[ "$filename" != *"custom_operation"* ]] && [[ "$filename" != *"return_values"* ]] && [[ "$filename" != *"qvector_init_from_state"* ]] && [[ "$filename" != *"qvector_init_from_vector"* ]]; then
# qvector_init_from_vector: only supported in 0.8 NVQC service.
# qvector_init_from_state, qvector_init_from_state_lazy, test_trotter: not supported yet on nvqc: https://github.com/NVIDIA/cuda-quantum/issues/2146
if [[ "$filename" != *"unsupport_args"* ]] && [[ "$filename" != *"state_overlap"* ]] && [[ "$filename" != *"compile_errors"* ]] && [[ "$filename" != *"nested_vectors"* ]] && [[ "$filename" != *"pauli_word"* ]] && [[ "$filename" != *"custom_operation"* ]] && [[ "$filename" != *"return_values"* ]] && [[ "$filename" != *"qvector_init_from_state"* ]] && [[ "$filename" != *"qvector_init_from_state_lazy"* ]] && [[ "$filename" != *"qvector_init_from_vector"* ]] && [[ "$filename" != *"test_trotter"* ]]; then
echo "$filename"
nvqc_config=""
# Look for a --remote-mqpu-auto-launch to determine the number of QPUs
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@
path = tpls/Crow
url = https://github.com/CrowCpp/Crow.git
ignore = dirty
[submodule "tpls/Stim"]
path = tpls/Stim
url = https://github.com/quantumlib/Stim
8 changes: 8 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,11 @@ Tweedledum - MIT License
License at <https://github.com/boschmitt/tweedledum/blob/master/LICENSE>

----------------------------------------------------------------

Stim - Apache License 2.0
<https://github.com/quantumlib/Stim>

The incorporated source code and its license can be found as a submodule on the CUDA-Q repository.
License at <https://github.com/quantumlib/Stim/blob/main/LICENSE>

----------------------------------------------------------------
1 change: 1 addition & 0 deletions docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ if (CUDAQ_ENABLE_PYTHON)
add_pycudaq_test(DepolarizingNoise noise_depolarization.py)
add_pycudaq_test(PhaseFlipNoise noise_phase_flip.py)
add_pycudaq_test(KrausNoise noise_kraus_operator.py)
add_pycudaq_test(NoiseCallback noise_callback.py)

if (CUTENSORNET_ROOT AND CUDA_FOUND)
# This example uses tensornet backend.
Expand Down
67 changes: 67 additions & 0 deletions docs/sphinx/examples/cpp/basics/noise_callback.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Compile and run with:
// ```
// nvq++ noise_callback.cpp --target density-matrix-cpu -o dyn.x
// && ./dyn.x
// ```
//
// Note: You must set the target to a density matrix backend for the noise
// to successfully impact the system.

#include <cudaq.h>
#include <iostream>

// CUDA-Q supports several different models of noise. In this
// case, we will examine the dynamic noise channel specified as a callback
// function.

int main() {

// We will begin by defining an empty noise model that we will add
// our channel to.
cudaq::noise_model noise;
// Noise model callback function
const auto rx_noise = [](const auto &qubits,
const auto &params) -> cudaq::kraus_channel {
// Model a pulse-length based rotation gate:
// the bigger the angle, the longer the pulse, i.e., more amplitude damping.
auto angle = params[0];
// Normalize the angle into the [0, 2*pi] range
while (angle > 2. * M_PI)
angle -= 2. * M_PI;

while (angle < 0)
angle += 2. * M_PI;
// Damping rate is linearly proportional to the angle
const auto damping_rate = angle / (2. * M_PI);
std::cout << "Angle = " << params[0]
<< ", amplitude damping rate = " << damping_rate << "\n";
return cudaq::amplitude_damping_channel(damping_rate);
};

// Bind the noise model callback function to the `rx` gate
noise.add_channel<cudaq::types::rx>(rx_noise);

auto kernel = [](double angle) __qpu__ {
cudaq::qubit q;
rx(angle, q);
mz(q);
};

// Now let's set the noise and we're ready to run the simulation!
cudaq::set_noise(noise);

// Our results should show measurements in both the |0> and |1> states,
// indicating that the noise has successfully impacted the system. Note: a
// `rx(pi)` is equivalent to a Pauli X gate, and thus, it should be in the |1>
// state if no noise is present.
auto noisy_counts = cudaq::sample(kernel, M_PI);
std::cout << "Noisy result:\n";
noisy_counts.dump();

// To confirm this, we can run the simulation again without noise.
cudaq::unset_noise();
auto noiseless_counts = cudaq::sample(kernel, M_PI);
std::cout << "Noiseless result:\n";
noiseless_counts.dump();
return 0;
}
5 changes: 3 additions & 2 deletions docs/sphinx/examples/cpp/other/trotter_kernel_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ int SPINS = 11; // set to around 25 qubits for `nvidia` target
int STEPS = 10; // set to around 100 for `nvidia` target

// Compile and run with:
// clang-format off
// ```
// nvq++ --enable-mlir -v trotter_kernel_mode.cpp -o trotter.x -target nvidia &&
// ./trotter.x
// nvq++ --enable-mlir -v trotter_kernel_mode.cpp -o trotter.x --target nvidia && ./trotter.x
// ```
// clang-format off

// Alternating up/down spins
struct initState {
Expand Down
45 changes: 45 additions & 0 deletions docs/sphinx/examples/python/noise_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import cudaq
import numpy as np

# Set the target to our density matrix simulator.
cudaq.set_target('density-matrix-cpu')

noise = cudaq.NoiseModel()


# Noise model callback function
def rx_noise(qubits, params):
# Model a pulse-length based rotation gate:
# the bigger the angle, the longer the pulse, i.e., more amplitude damping.
angle = params[0]
angle = angle % (2 * np.pi)
# Damping rate is linearly proportional to the angle
damping_rate = np.abs(angle / (2 * np.pi))
print(f"Angle = {angle}, amplitude damping rate = {damping_rate}.")
return cudaq.AmplitudeDampingChannel(damping_rate)


# Bind the noise model callback function to the `rx` gate
noise.add_channel('rx', rx_noise)


@cudaq.kernel
def kernel(angle: float):
qubit = cudaq.qubit()
rx(angle, qubit)
mz(qubit)


# Now we're ready to run the noisy simulation of our kernel.
# Note: We must pass the noise model to sample via keyword.
noisy_result = cudaq.sample(kernel, np.pi, noise_model=noise)
print(noisy_result)

# Our results should show measurements in both the |0> and |1> states, indicating
# that the noise has successfully impacted the system.
# Note: a `rx(pi)` is equivalent to a Pauli X gate, and thus, it should be
# in the |1> state if no noise is present.

# To confirm this, we can run the simulation again without noise.
noiseless_result = cudaq.sample(kernel, np.pi)
print(noiseless_result)
3 changes: 3 additions & 0 deletions docs/sphinx/examples/python/providers/photonics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ def photonicsKernel():

counts = cudaq.sample(photonicsKernel)
print(counts)

state = cudaq.get_state(photonicsKernel)
print(state)
3 changes: 3 additions & 0 deletions docs/sphinx/examples/python/providers/photonics_tbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ def TBI(
loop_lengths,
shots_count=1000000)
counts.dump()

state = cudaq.get_state(TBI, bs_angles, ps_angles, input_state, loop_lengths)
state.dump()
1 change: 1 addition & 0 deletions docs/sphinx/using/backends/backends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ CUDA-Q Backends
* :ref:`qpp-cpu <qpp-cpu-backend>`
* :ref:`quantinuum <quantinuum-backend>`
* :ref:`remote-mqpu <mqpu-platform>`
* :ref:`stim <stim-backend>`
* :ref:`tensornet <tensor-backends>`
* :ref:`tensornet-mps <tensor-backends>`

Expand Down
51 changes: 43 additions & 8 deletions docs/sphinx/using/backends/simulators.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ setting the target.
- Number of CPU threads used for circuit processing. The default value is `8`.
* - ``CUDAQ_MAX_CPU_MEMORY_GB``
- non-negative integer, or `NONE`
- CPU memory size (in GB) allowed for state-vector migration. `NONE` means unlimited (up to physical memory constraints). Default is 0 (disabled).
- CPU memory size (in GB) allowed for state-vector migration. `NONE` means unlimited (up to physical memory constraints). Default is 0GB (disabled, variable is not set to any value).
* - ``CUDAQ_MAX_GPU_MEMORY_GB``
- positive integer, or `NONE`
- GPU memory (in GB) allowed for on-device state-vector allocation. As the state-vector size exceeds this limit, host memory will be utilized for migration. `NONE` means unlimited (up to physical memory constraints). This is the default.
- GPU memory (in GB) allowed for on-device state-vector allocation. As the state-vector size exceeds this limit, host memory will be utilized for migration. `NONE` means unlimited (up to physical memory constraints). This is the default.

.. deprecated:: 0.8
The :code:`nvidia-fp64` targets, which is equivalent setting the `fp64` option on the :code:`nvidia` target,
Expand Down Expand Up @@ -169,10 +169,11 @@ To execute a program on the multi-node multi-GPU NVIDIA target, use the followin
If a target is set in the application code, this target will override the :code:`--target` command line flag given during program invocation.

.. note::
(1) The order of the option settings are interchangeable.
For example, `cudaq.set_target('nvidia', option='mgpu,fp64')` is equivalent to `cudaq.set_target('nvidia', option='fp64.mgpu')`.

* The order of the option settings are interchangeable.
For example, `cudaq.set_target('nvidia', option='mgpu,fp64')` is equivalent to `cudaq.set_target('nvidia', option='fp64,mgpu')`.

(2) The `nvidia` target has single-precision as the default setting. Thus, using `option='mgpu'` implies that `option='mgpu,fp32'`.
* The `nvidia` target has single-precision as the default setting. Thus, using `option='mgpu'` implies that `option='mgpu,fp32'`.

.. tab:: C++

Expand Down Expand Up @@ -300,6 +301,40 @@ use the following commands:
./program.x
Clifford-Only Simulation (CPU)
++++++++++++++++++++++++++++++++++

.. _stim-backend:

This target provides a fast simulator for circuits containing *only* Clifford
gates. Any non-Clifford gates (such as T gates and Toffoli gates) are not
supported. This simulator is based on the `Stim <https://github.com/quantumlib/Stim>`_
library.

To execute a program on the :code:`stim` target, use the following commands:

.. tab:: Python

.. code:: bash
python3 program.py [...] --target stim
The target can also be defined in the application code by calling

.. code:: python
cudaq.set_target('stim')
If a target is set in the application code, this target will override the :code:`--target` command line flag given during program invocation.

.. tab:: C++

.. code:: bash
nvq++ --target stim program.cpp [...] -o program.x
./program.x
Tensor Network Simulators
==================================

Expand All @@ -309,7 +344,7 @@ CUDA-Q provides a couple of tensor-network simulator targets accelerated with
the :code:`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 :code:`cudaq::observe` can be performed efficiently, thanks to :code:`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.
Tensor network 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 :code:`cudaq::observe` can be performed efficiently, thanks to :code:`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
+++++++++++++++++++++++++++++++++++
Expand Down Expand Up @@ -442,14 +477,14 @@ Specific aspects of the simulation can be configured by defining the following e

.. note::
The parallelism of Jacobi method (the default `CUDAQ_MPS_SVD_ALGO` setting) gives GPU better performance on small and medium size matrices.
If you expect the a large number of singular values (e.g., increasing the `CUDAQ_MPS_MAX_BOND` setting), please adjust the `CUDAQ_MPS_SVD_ALGO` setting accordingly.
If you expect a large number of singular values (e.g., increasing the `CUDAQ_MPS_MAX_BOND` setting), please adjust the `CUDAQ_MPS_SVD_ALGO` setting accordingly.

Default Simulator
==================================

.. _default-simulator:

If no explicit target is set, i.e. if the code is compiled without any :code:`--target` flags, then CUDA-Q makes a default choice for the simulator.
If no explicit target is set, i.e., if the code is compiled without any :code:`--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 :ref:`cuQuantum single-GPU state vector simulator <cuQuantum single-GPU>`.
On CPU-only systems, the default target is set to `qpp-cpu` which uses the :ref:`OpenMP CPU-only simulator <OpenMP CPU-only>`.
Expand Down
61 changes: 59 additions & 2 deletions docs/sphinx/using/extending/_noise.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,67 @@ constructor should validate the completeness (CPTP) relation.
A :code:`cudaq::noise_model` encapsulates a mapping of quantum operation names to a
vector of :code:`kraus_channel` that is to be applied after invocation of that
quantum operation. A :code:`noise_model` can be constructed with a nullary constructor, and
:code:`kraus_channels` can be added via a templated :code:`add_channel` method, where the
template type is the quantum operation the channel applies to (e.g. :code:`model.add_channel\<cudaq::types::h\>(channel)`). Clients (e.g. simulator backends) can retrieve the :code:`kraus_channel` to
:code:`kraus_channels` can be added via :code:`add_channel` and :code:`add_all_qubit_channel` methods with
the operation given as a string or as a template argument.
The operation name or the template type specifies the quantum operation the channel applies to
(e.g. :code:`model.add_channel\<cudaq::types::h\>(channel)` or :code:`model.add_channel("h", channel)`).
Clients (e.g. simulator backends) can retrieve the :code:`kraus_channel` to
apply to the simulated state via a :code:`noise_model::get_channel(...)` call.

When adding an error channel to a noise model for a quantum operation
we can assign the noise channel to instances of that operation on specific qubit operands or
to any occurrence of the operation, regardless of which qubits it acts on.

.. tab:: Python

.. code-block:: python
# Add a noise channel to z gate on qubit 0
noise.add_channel('z', [0], noise_channel)
# Add a noise channel to x gate, regardless of qubit operands.
noise.add_all_qubit_channel('x', noise_channel)
.. tab:: C++

.. code-block:: cpp
// Add a noise channel to z gate on qubit 0
noise.add_channel("z", {0}, noise_channel);
// Add a noise channel to x gate, regardless of qubit operands.
noise.add_all_qubit_channel("x", noise_channel)
In addition to static noise channels, users can also define a noise channel as a
callback function, which returns a concrete channel definition in terms of Kraus matrices
depending on the gate operands and gate parameters if any.

.. tab:: Python

.. code-block:: python
# Noise channel callback function
def noise_cb(qubits, params):
# Construct a channel based on specific operands and parameters
...
return noise_channel
# Add a dynamic noise channel to the 'rx' gate.
noise.add_channel('rx', noise_cb)
.. tab:: C++

.. code-block:: cpp
// Add a dynamic noise channel to the 'rx' gate.
noise.add_channel("rx",
[](const auto &qubits, const auto &params) -> cudaq::kraus_channel {
// Construct a channel based on specific operands and parameters
...
return noiseChannel;
});
Noise models can be constructed via the :code:`cudaq::noise_model` and specified for
execution via a public :code:`cudaq::set_noise(cudaq::noise_model&)` function. This function
should forward the :code:`noise_model` to the current :code:`quantum_platform` which can attach it
Expand Down
2 changes: 1 addition & 1 deletion docs/sphinx/using/install/data_center_install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ Python-specific tools:
in the way as you installed Python itself. If you installed Python via the package manager
for your system, you may need to install an additional package to get the development headers.
The package name is usually your python version followed by either a `-dev` or `-devel` suffix.
If you are using a `Conda environment <https://conda.io/projects/conda/en/latest/user-guide/getting-started.html#managing-python>`__,
If you are using a `Conda environment <https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-python.html>`__,
the necessary headers should already be installed.
- Pip package manager: Make sure the `pip` module is enable for your Python version.
We refer to the Python `documentation <https://pip.pypa.io/en/stable/installation/>`__ for
Expand Down
3 changes: 3 additions & 0 deletions include/cudaq/Frontend/nvqpp/ASTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,9 @@ class QuakeBridgeVisitor
bool isItaniumCXXABI();

private:
/// Check that the value on the top of the stack is an entry-point kernel.
bool hasTOSEntryKernel();

/// Map the block arguments to the names of the function parameters.
void addArgumentSymbols(mlir::Block *entryBlock,
mlir::ArrayRef<clang::ParmVarDecl *> parameters);
Expand Down
Loading

0 comments on commit a38ee71

Please sign in to comment.