Examples

In this section, we show how to implement quantum computing simulation using cuStateVec. First, we describe how to install the library and how to compile it. Then, we present an example code to perform common steps in cuStateVec.

Compilation

Assuming cuQuantum has been extracted in CUQUANTUM_ROOT, we update the library path accordingly:

export LD_LIBRARY_PATH=${CUQUANTUM_ROOT}/lib:${LD_LIBRARY_PATH}

We can compile the sample code we will discuss below (statevec_example.cu) via the following command:

nvcc statevec_example.cu -I${CUQUANTUM_ROOT}/include -L${CUQUANTUM_ROOT}/lib -lcustatevec -o statevec_example

Note

Depending on the source of the cuQuantum package, you may need to replace lib above by lib64.

Code Example

The following code example shows the common steps to use cuStateVec. Here we apply a Toffoli gate, which inverts the third bit when the first two bits are both 1.

../_images/toffoli.png
#include <cuda_runtime_api.h> // cudaMalloc, cudaMemcpy, etc.
#include <cuComplex.h>        // cuDoubleComplex
#include <custatevec.h>       // custatevecApplyMatrix
#include <stdio.h>            // printf
#include <stdlib.h>           // EXIT_FAILURE

int main(void) {

   const int nIndexBits = 3;
   const int nSvSize    = (1 << nIndexBits);
   const int nTargets   = 1;
   const int nControls  = 2;
   const int adjoint    = 0;

   int targets[]  = {2};
   int controls[] = {0, 1};

   cuDoubleComplex h_sv[]        = {{ 0.0, 0.0}, { 0.0, 0.1}, { 0.1, 0.1},
                                    { 0.1, 0.2}, { 0.2, 0.2}, { 0.3, 0.3},
                                    { 0.3, 0.4}, { 0.4, 0.5}};
   cuDoubleComplex h_sv_result[] = {{ 0.0, 0.0}, { 0.0, 0.1}, { 0.1, 0.1},
                                    { 0.4, 0.5}, { 0.2, 0.2}, { 0.3, 0.3},
                                    { 0.3, 0.4}, { 0.1, 0.2}};
   cuDoubleComplex matrix[] = {{0.0, 0.0}, {1.0, 0.0},
                               {1.0, 0.0}, {0.0, 0.0}};


   cuDoubleComplex *d_sv;
   cudaMalloc((void**)&d_sv, nSvSize * sizeof(cuDoubleComplex));

   cudaMemcpy(d_sv, h_sv, nSvSize * sizeof(cuDoubleComplex),
              cudaMemcpyHostToDevice);

   //--------------------------------------------------------------------------

   // custatevec handle initialization
   custatevecHandle_t handle;

   custatevecCreate(&handle);

   void* extraWorkspace = nullptr;
   size_t extraWorkspaceSizeInBytes = 0;

   // check the size of external workspace
   custatevecApplyMatrixGetWorkspaceSize(
       handle, CUDA_C_64F, nIndexBits, matrix, CUDA_C_64F,
       CUSTATEVEC_MATRIX_LAYOUT_ROW, adjoint, nTargets, nControls,
       CUSTATEVEC_COMPUTE_64F, &extraWorkspaceSizeInBytes);

   // allocate external workspace if necessary
   if (extraWorkspaceSizeInBytes > 0)
       cudaMalloc(&extraWorkspace, extraWorkspaceSizeInBytes);

   // apply gate
   custatevecApplyMatrix(
       handle, d_sv, CUDA_C_64F, nIndexBits, matrix, CUDA_C_64F,
       CUSTATEVEC_MATRIX_LAYOUT_ROW, adjoint, targets, nTargets, controls,
       nullptr, nControls, CUSTATEVEC_COMPUTE_64F,
       extraWorkspace, extraWorkspaceSizeInBytes);

   // destroy handle
   custatevecDestroy(handle);

   //--------------------------------------------------------------------------

   cudaMemcpy(h_sv, d_sv, nSvSize * sizeof(cuDoubleComplex),
              cudaMemcpyDeviceToHost);

   bool correct = true;
   for (int i = 0; i < nSvSize; i++) {
       if ((h_sv[i].x != h_sv_result[i].x) ||
           (h_sv[i].y != h_sv_result[i].y)) {
           correct = false;
           break;
       }
   }

   if (correct)
       printf("example PASSED\n");
   else
       printf("example FAILED: wrong result\n");

   cudaFree(d_sv);
   if (extraWorkspaceSizeInBytes)
       cudaFree(extraWorkspace);

   return EXIT_SUCCESS;
}

More samples can be found in the NVIDIA/cuQuantum repository.

Useful tips

  • For debugging, the environment variable CUSTATEVEC_LOG_LEVEL=n can be set. The level n = 0, 1, …, 5 corresponds to the logger level as described and used in custatevecLoggerSetLevel(). The environment variable CUSTATEVEC_LOG_FILE=<filepath> can be used to direct the log output to a custom file at <filepath> instead of stdout.