计算张量网络状态期望值

以下代码示例说明了如何定义张量网络状态、张量网络算符,然后计算张量网络算符在张量网络状态上的期望值。完整代码可以在 NVIDIA/cuQuantum 仓库中找到(此处)。

头文件和错误处理

 7#include <cstdlib>
 8#include <cstdio>
 9#include <cassert>
10#include <complex>
11#include <vector>
12#include <bitset>
13#include <iostream>
14
15#include <cuda_runtime.h>
16#include <cutensornet.h>
17
18
19#define HANDLE_CUDA_ERROR(x) \
20{ const auto err = x; \
21  if( err != cudaSuccess ) \
22  { printf("CUDA error %s in line %d\n", cudaGetErrorString(err), __LINE__); fflush(stdout); std::abort(); } \
23};
24
25#define HANDLE_CUTN_ERROR(x) \
26{ const auto err = x; \
27  if( err != CUTENSORNET_STATUS_SUCCESS ) \
28  { printf("cuTensorNet error %s in line %d\n", cutensornetGetErrorString(err), __LINE__); fflush(stdout); std::abort(); } \
29};
30
31
32int main()
33{
34  static_assert(sizeof(size_t) == sizeof(int64_t), "Please build this sample on a 64-bit architecture!");
35
36  constexpr std::size_t fp64size = sizeof(double);

定义张量网络状态

让我们定义一个对应于 16 量子比特量子线路的张量网络状态。

40  // Quantum state configuration
41  constexpr int32_t numQubits = 16; // number of qubits
42  const std::vector<int64_t> qubitDims(numQubits,2); // qubit dimensions
43  std::cout << "Quantum circuit: " << numQubits << " qubits\n";

初始化 cuTensorNet 库句柄

47  // Initialize the cuTensorNet library
48  HANDLE_CUDA_ERROR(cudaSetDevice(0));
49  cutensornetHandle_t cutnHandle;
50  HANDLE_CUTN_ERROR(cutensornetCreate(&cutnHandle));
51  std::cout << "Initialized cuTensorNet library on GPU 0\n";

在 GPU 上定义量子门

55  // Define necessary quantum gate tensors in Host memory
56  const double invsq2 = 1.0 / std::sqrt(2.0);
57  //  Hadamard gate
58  const std::vector<std::complex<double>> h_gateH {{invsq2, 0.0},  {invsq2, 0.0},
59                                                   {invsq2, 0.0}, {-invsq2, 0.0}};
60  //  Pauli X gate
61  const std::vector<std::complex<double>> h_gateX {{0.0, 0.0}, {1.0, 0.0},
62                                                   {1.0, 0.0}, {0.0, 0.0}};
63  //  Pauli Y gate
64  const std::vector<std::complex<double>> h_gateY {{0.0, 0.0}, {0.0, -1.0},
65                                                   {0.0, 1.0}, {0.0, 0.0}};
66  //  Pauli Z gate
67  const std::vector<std::complex<double>> h_gateZ {{1.0, 0.0}, {0.0, 0.0},
68                                                   {0.0, 0.0}, {-1.0, 0.0}};
69  //  CX gate
70  const std::vector<std::complex<double>> h_gateCX {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0},
71                                                    {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0},
72                                                    {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {1.0, 0.0},
73                                                    {0.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}};
74
75  // Copy quantum gates to Device memory
76  void *d_gateH{nullptr}, *d_gateX{nullptr}, *d_gateY{nullptr}, *d_gateZ{nullptr}, *d_gateCX{nullptr};
77  HANDLE_CUDA_ERROR(cudaMalloc(&d_gateH, 4 * (2 * fp64size)));
78  HANDLE_CUDA_ERROR(cudaMalloc(&d_gateX, 4 * (2 * fp64size)));
79  HANDLE_CUDA_ERROR(cudaMalloc(&d_gateY, 4 * (2 * fp64size)));
80  HANDLE_CUDA_ERROR(cudaMalloc(&d_gateZ, 4 * (2 * fp64size)));
81  HANDLE_CUDA_ERROR(cudaMalloc(&d_gateCX, 16 * (2 * fp64size)));
82  std::cout << "Allocated quantum gate memory on GPU\n";
83  HANDLE_CUDA_ERROR(cudaMemcpy(d_gateH, h_gateH.data(), 4 * (2 * fp64size), cudaMemcpyHostToDevice));
84  HANDLE_CUDA_ERROR(cudaMemcpy(d_gateX, h_gateX.data(), 4 * (2 * fp64size), cudaMemcpyHostToDevice));
85  HANDLE_CUDA_ERROR(cudaMemcpy(d_gateY, h_gateY.data(), 4 * (2 * fp64size), cudaMemcpyHostToDevice));
86  HANDLE_CUDA_ERROR(cudaMemcpy(d_gateZ, h_gateZ.data(), 4 * (2 * fp64size), cudaMemcpyHostToDevice));
87  HANDLE_CUDA_ERROR(cudaMemcpy(d_gateCX, h_gateCX.data(), 16 * (2 * fp64size), cudaMemcpyHostToDevice));
88  std::cout << "Copied quantum gates to GPU memory\n";

在 GPU 上分配暂存缓冲区

92  // Query the free memory on Device
93  std::size_t freeSize{0}, totalSize{0};
94  HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeSize, &totalSize));
95  const std::size_t scratchSize = (freeSize - (freeSize % 4096)) / 2; // use half of available memory with alignment
96  void *d_scratch{nullptr};
97  HANDLE_CUDA_ERROR(cudaMalloc(&d_scratch, scratchSize));
98  std::cout << "Allocated " << scratchSize << " bytes of scratch memory on GPU\n";

创建纯张量网络状态

现在让我们为 16 量子比特量子线路创建一个纯张量网络状态。

102  // Create the initial quantum state
103  cutensornetState_t quantumState;
104  HANDLE_CUTN_ERROR(cutensornetCreateState(cutnHandle, CUTENSORNET_STATE_PURITY_PURE, numQubits, qubitDims.data(),
105                    CUDA_C_64F, &quantumState));
106  std::cout << "Created the initial quantum state\n";

应用量子门

让我们通过应用相应的量子门来构建 GHZ 量子线路。

110  // Construct the final quantum circuit state (apply quantum gates) for the GHZ circuit
111  int64_t id;
112  HANDLE_CUTN_ERROR(cutensornetStateApplyTensorOperator(cutnHandle, quantumState, 1, std::vector<int32_t>{{0}}.data(),
113                    d_gateH, nullptr, 1, 0, 1, &id));
114  for(int32_t i = 1; i < numQubits; ++i) {
115    HANDLE_CUTN_ERROR(cutensornetStateApplyTensorOperator(cutnHandle, quantumState, 2, std::vector<int32_t>{{i-1,i}}.data(),
116                      d_gateCX, nullptr, 1, 0, 1, &id));
117  }
118  std::cout << "Applied quantum gates\n";

构建张量网络算符

现在让我们为 16 量子比特状态创建一个空的张量网络算符,然后向其附加三个组件,其中每个组件都是 Pauli 矩阵的直积,并按一些复系数缩放(如 Jordan-Wigner 表示)。

122  // Create an empty tensor network operator
123  cutensornetNetworkOperator_t hamiltonian;
124  HANDLE_CUTN_ERROR(cutensornetCreateNetworkOperator(cutnHandle, numQubits, qubitDims.data(), CUDA_C_64F, &hamiltonian));
125  // Append component (0.5 * Z1 * Z2) to the tensor network operator
126  {
127    const int32_t numModes[] = {1, 1}; // Z1 acts on 1 mode, Z2 acts on 1 mode
128    const int32_t modesZ1[] = {1}; // state modes Z1 acts on
129    const int32_t modesZ2[] = {2}; // state modes Z2 acts on
130    const int32_t * stateModes[] = {modesZ1, modesZ2}; // state modes (Z1 * Z2) acts on
131    const void * gateData[] = {d_gateZ, d_gateZ}; // GPU pointers to gate data
132    HANDLE_CUTN_ERROR(cutensornetNetworkOperatorAppendProduct(cutnHandle, hamiltonian, cuDoubleComplex{0.5,0.0},
133                      2, numModes, stateModes, NULL, gateData, &id));
134  }
135  // Append component (0.25 * Y3) to the tensor network operator
136  {
137    const int32_t numModes[] = {1}; // Y3 acts on 1 mode
138    const int32_t modesY3[] = {3}; // state modes Y3 acts on
139    const int32_t * stateModes[] = {modesY3}; // state modes (Y3) acts on
140    const void * gateData[] = {d_gateY}; // GPU pointers to gate data
141    HANDLE_CUTN_ERROR(cutensornetNetworkOperatorAppendProduct(cutnHandle, hamiltonian, cuDoubleComplex{0.25,0.0},
142                      1, numModes, stateModes, NULL, gateData, &id));
143  }
144  // Append component (0.13 * Y0 X2 Z3) to the tensor network operator
145  {
146    const int32_t numModes[] = {1, 1, 1}; // Y0 acts on 1 mode, X2 acts on 1 mode, Z3 acts on 1 mode
147    const int32_t modesY0[] = {0}; // state modes Y0 acts on
148    const int32_t modesX2[] = {2}; // state modes X2 acts on
149    const int32_t modesZ3[] = {3}; // state modes Z3 acts on
150    const int32_t * stateModes[] = {modesY0, modesX2, modesZ3}; // state modes (Y0 * X2 * Z3) acts on
151    const void * gateData[] = {d_gateY, d_gateX, d_gateZ}; // GPU pointers to gate data
152    HANDLE_CUTN_ERROR(cutensornetNetworkOperatorAppendProduct(cutnHandle, hamiltonian, cuDoubleComplex{0.13,0.0},
153                      3, numModes, stateModes, NULL, gateData, &id));
154  }
155  std::cout << "Constructed a tensor network operator: (0.5 * Z1 * Z2) + (0.25 * Y3) + (0.13 * Y0 * X2 * Z3)" << std::endl;

创建期望值对象

一旦构建了量子线路和张量网络算符,让我们创建期望值对象,它将计算指定张量网络算符在指定量子线路的最终状态上的期望值。

159  // Specify the quantum circuit expectation value
160  cutensornetStateExpectation_t expectation;
161  HANDLE_CUTN_ERROR(cutensornetCreateExpectation(cutnHandle, quantumState, hamiltonian, &expectation));
162  std::cout << "Created the specified quantum circuit expectation value\n";

配置期望值计算

现在我们可以通过设置张量网络收缩路径查找器要使用的超样本数来配置期望值对象。

166  // Configure the computation of the specified quantum circuit expectation value
167  const int32_t numHyperSamples = 8; // desired number of hyper samples used in the tensor network contraction path finder
168  HANDLE_CUTN_ERROR(cutensornetExpectationConfigure(cutnHandle, expectation,
169                    CUTENSORNET_EXPECTATION_CONFIG_NUM_HYPER_SAMPLES, &numHyperSamples, sizeof(numHyperSamples)));

准备期望值计算

让我们创建一个工作区描述符并准备计算所需的期望值。

173  // Prepare the specified quantum circuit expectation value for computation
174  cutensornetWorkspaceDescriptor_t workDesc;
175  HANDLE_CUTN_ERROR(cutensornetCreateWorkspaceDescriptor(cutnHandle, &workDesc));
176  std::cout << "Created the workspace descriptor\n";
177  HANDLE_CUTN_ERROR(cutensornetExpectationPrepare(cutnHandle, expectation, scratchSize, workDesc, 0x0));
178  std::cout << "Prepared the specified quantum circuit expectation value\n";
179  double flops {0.0};
180  HANDLE_CUTN_ERROR(cutensornetExpectationGetInfo(cutnHandle, expectation,
181                    CUTENSORNET_EXPECTATION_INFO_FLOPS, &flops, sizeof(flops)));
182  std::cout << "Total flop count = " << (flops/1e9) << " GFlop\n";

设置工作区

现在我们可以设置所需的工作区缓冲区。

186  // Attach the workspace buffer
187  int64_t worksize {0};
188  HANDLE_CUTN_ERROR(cutensornetWorkspaceGetMemorySize(cutnHandle,
189                                                      workDesc,
190                                                      CUTENSORNET_WORKSIZE_PREF_RECOMMENDED,
191                                                      CUTENSORNET_MEMSPACE_DEVICE,
192                                                      CUTENSORNET_WORKSPACE_SCRATCH,
193                                                      &worksize));
194  std::cout << "Required scratch GPU workspace size (bytes) = " << worksize << std::endl;
195  if(worksize <= scratchSize) {
196    HANDLE_CUTN_ERROR(cutensornetWorkspaceSetMemory(cutnHandle, workDesc, CUTENSORNET_MEMSPACE_DEVICE,
197                      CUTENSORNET_WORKSPACE_SCRATCH, d_scratch, worksize));
198  }else{
199    std::cout << "ERROR: Insufficient workspace size on Device!\n";
200    std::abort();
201  }
202  std::cout << "Set the workspace buffer\n";

计算请求的期望值

一旦一切都设置好,我们就可以计算请求的期望值并打印它。请注意,返回的期望值未归一化。张量网络状态的 2-范数作为单独的参数返回。

206  // Compute the specified quantum circuit expectation value
207  std::complex<double> expectVal{0.0,0.0}, stateNorm2{0.0,0.0};
208  HANDLE_CUTN_ERROR(cutensornetExpectationCompute(cutnHandle, expectation, workDesc,
209                    static_cast<void*>(&expectVal), static_cast<void*>(&stateNorm2), 0x0));
210  std::cout << "Computed the specified quantum circuit expectation value\n";
211  expectVal /= stateNorm2;
212  std::cout << "Expectation value = (" << expectVal.real() << ", " << expectVal.imag() << ")\n";
213  std::cout << "Squared 2-norm of the state = (" << stateNorm2.real() << ", " << stateNorm2.imag() << ")\n";

释放资源

217  // Destroy the workspace descriptor
218  HANDLE_CUTN_ERROR(cutensornetDestroyWorkspaceDescriptor(workDesc));
219  std::cout << "Destroyed the workspace descriptor\n";
220
221  // Destroy the quantum circuit expectation value
222  HANDLE_CUTN_ERROR(cutensornetDestroyExpectation(expectation));
223  std::cout << "Destroyed the quantum circuit state expectation value\n";
224
225  // Destroy the tensor network operator
226  HANDLE_CUTN_ERROR(cutensornetDestroyNetworkOperator(hamiltonian));
227  std::cout << "Destroyed the tensor network operator\n";
228
229  // Destroy the quantum circuit state
230  HANDLE_CUTN_ERROR(cutensornetDestroyState(quantumState));
231  std::cout << "Destroyed the quantum circuit state\n";
232
233  HANDLE_CUDA_ERROR(cudaFree(d_scratch));
234  HANDLE_CUDA_ERROR(cudaFree(d_gateCX));
235  HANDLE_CUDA_ERROR(cudaFree(d_gateZ));
236  HANDLE_CUDA_ERROR(cudaFree(d_gateY));
237  HANDLE_CUDA_ERROR(cudaFree(d_gateX));
238  HANDLE_CUDA_ERROR(cudaFree(d_gateH));
239  std::cout << "Freed memory on GPU\n";
240
241  // Finalize the cuTensorNet library
242  HANDLE_CUTN_ERROR(cutensornetDestroy(cutnHandle));
243  std::cout << "Finalized the cuTensorNet library\n";
244
245  return 0;
246}