计算张量网络状态期望值¶
以下代码示例说明了如何定义张量网络状态、张量网络算符,然后计算张量网络算符在张量网络状态上的期望值。完整代码可以在 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}