forked from NVIDIA/cuda-quantum
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobserve_result_tester.cpp
More file actions
165 lines (141 loc) · 5.85 KB
/
observe_result_tester.cpp
File metadata and controls
165 lines (141 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*******************************************************************************
* Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/
#include "CUDAQTestUtils.h"
#include <cudaq/algorithm.h>
// Rotational gates not supported in Stim.
#ifndef CUDAQ_BACKEND_STIM
struct deuteron_n3_ansatz {
void operator()(double x0, double x1) __qpu__ {
cudaq::qvector q(3);
x(q[0]);
ry(x0, q[1]);
ry(x1, q[2]);
x<cudaq::ctrl>(q[2], q[0]);
x<cudaq::ctrl>(q[0], q[1]);
ry(-x0, q[1]);
x<cudaq::ctrl>(q[0], q[1]);
x<cudaq::ctrl>(q[1], q[0]);
}
};
CUDAQ_TEST(ObserveResult, checkSimple) {
cudaq::spin_op h =
5.907 - 2.1433 * cudaq::spin_op::x(0) * cudaq::spin_op::x(1) -
2.1433 * cudaq::spin_op::y(0) * cudaq::spin_op::y(1) +
.21829 * cudaq::spin_op::z(0) - 6.125 * cudaq::spin_op::z(1);
auto ansatz = [](double theta) __qpu__ {
cudaq::qubit q, r;
x(q);
ry(theta, r);
x<cudaq::ctrl>(r, q);
};
double result = cudaq::observe(ansatz, h, 0.59);
EXPECT_NEAR(result, -1.7487, 1e-3);
printf("Get energy directly as double %lf\n", result);
auto obs_res = cudaq::observe(ansatz, h, 0.59);
EXPECT_NEAR(obs_res.expectation(), -1.7487, 1e-3);
printf("Energy from observe_result %lf\n", obs_res.expectation());
// Observe using options w/ noise model. Note that the noise model is only
// honored when using the Density Matrix backend.
int shots = 252;
cudaq::set_random_seed(13);
cudaq::depolarization_channel depol(1.);
cudaq::noise_model noise;
noise.add_channel<cudaq::types::x>({0}, depol);
auto obs_opt =
cudaq::observe({.shots = shots, .noise = noise}, ansatz, h, 0.59);
// Verify that the number of shots requested was honored
auto tmpCounts = obs_opt.raw_data();
for (auto spinOpName : tmpCounts.register_names()) {
if (spinOpName == cudaq::GlobalRegisterName)
continue; // Ignore the global register
std::size_t totalShots = 0;
for (auto &[bitstr, counts] : tmpCounts.to_map(spinOpName))
totalShots += counts;
EXPECT_EQ(totalShots, shots);
}
printf("\n\nLAST ONE!\n");
auto obs_res2 = cudaq::observe(100000, ansatz, h, 0.59);
EXPECT_NEAR(obs_res2.expectation(), -1.7, 1e-1);
printf("Energy from observe_result with shots %lf\n", obs_res2.expectation());
obs_res2.dump();
for (const auto &term : h)
if (!term.is_identity())
printf("Fine-grain data access: %s = %lf\n", term.to_string().data(),
obs_res2.expectation(term));
auto observable = cudaq::spin_op::x(0) * cudaq::spin_op::x(1);
auto x0x1Counts = obs_res2.counts(observable);
x0x1Counts.dump();
EXPECT_TRUE(x0x1Counts.size() == 4);
}
// By default, tensornet backends only compute the overall expectation value in
// observe, i.e., no sub-term calculations.
#ifndef CUDAQ_BACKEND_TENSORNET
CUDAQ_TEST(ObserveResult, checkExpValBug) {
auto kernel = []() __qpu__ {
cudaq::qvector qubits(3);
rx(0.531, qubits[0]);
ry(0.9, qubits[1]);
rx(0.3, qubits[2]);
cz(qubits[0], qubits[1]);
ry(-0.4, qubits[0]);
cz(qubits[1], qubits[2]);
};
auto hamiltonian = cudaq::spin_op::z(0) + cudaq::spin_op::z(1);
auto result = cudaq::observe(kernel, hamiltonian);
auto observable = cudaq::spin_op::z(0);
auto exp = result.expectation(observable);
printf("exp %lf \n", exp);
EXPECT_NEAR(exp, .79, 1e-1);
observable = cudaq::spin_op::z(1);
exp = result.expectation(observable);
printf("exp %lf \n", exp);
EXPECT_NEAR(exp, .62, 1e-1);
// We support retrieval of terms as long as they are equal to the
// terms defined in the spin op passed to observe. A term/operator
// that acts on two degrees is never the same as an operator that
// acts on one degree, even if it only acts with an identity on the
// second degree. While the expectation values generally should be
// the same in this case, the operators are not (e.g. the respective
// kernels/gates defined by the two operators is not the same since
// it acts on a different number of qubits). This is in particular
// also relevant for noise modeling.
}
#endif
CUDAQ_TEST(ObserveResult, checkObserveWithIdentity) {
auto kernel = []() __qpu__ {
cudaq::qvector qubits(5);
cudaq::exp_pauli(1.0, qubits, "XXIIX");
};
const std::string pauliWord = "ZZIIZ";
const std::size_t numQubits = pauliWord.size();
auto pauliOp = cudaq::spin_op::from_word(pauliWord);
// The canonicalized degree list is less than the number of qubits
EXPECT_LT(cudaq::spin_op::canonicalize(pauliOp).degrees().size(), numQubits);
auto expVal = cudaq::observe(kernel, pauliOp);
std::cout << "<" << pauliWord << "> = " << expVal.expectation() << "\n";
EXPECT_NEAR(expVal.expectation(), -0.416147, 1e-6);
}
#ifdef CUDAQ_BACKEND_TENSORNET
CUDAQ_TEST(ObserveResult, checkObserveWithIdentityLarge) {
auto kernel = []() __qpu__ {
cudaq::qvector qubits(50);
cudaq::exp_pauli(1.0, qubits,
"XXIIXXXIIXXXIIXXXIIXXXIIXXXIIXXXIIXXXIIXXXIIXXXIXX");
};
const std::string pauliWord =
"ZZIIZZZIIZZZIIZZZIIZZZIIZZZIIZZZIIZZZIIZZZIIZZZIZZ";
const std::size_t numQubits = pauliWord.size();
auto pauliOp = cudaq::spin_op::from_word(pauliWord);
// The canonicalized degree list is less than the number of qubits
EXPECT_LT(cudaq::spin_op::canonicalize(pauliOp).degrees().size(), numQubits);
auto expVal = cudaq::observe(kernel, pauliOp);
std::cout << "<" << pauliWord << "> = " << expVal.expectation() << "\n";
EXPECT_NEAR(expVal.expectation(), -0.416147, 1e-3);
}
#endif
#endif