pennylane-qrack 0.24.1__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pennylane_qrack/QrackAceDeviceConfig.toml +108 -0
- pennylane_qrack/QrackDeviceConfig.toml +117 -0
- pennylane_qrack/QrackStabilizerDeviceConfig.toml +99 -0
- pennylane_qrack/__init__.py +16 -0
- pennylane_qrack/_version.py +19 -0
- pennylane_qrack/qrack_ace_device.py +447 -0
- pennylane_qrack/qrack_device.cpp +875 -0
- pennylane_qrack/qrack_device.py +744 -0
- pennylane_qrack/qrack_stabilizer_device.py +313 -0
- pennylane_qrack-0.24.1.dist-info/LICENSE +201 -0
- pennylane_qrack-0.24.1.dist-info/METADATA +146 -0
- pennylane_qrack-0.24.1.dist-info/RECORD +15 -0
- pennylane_qrack-0.24.1.dist-info/WHEEL +5 -0
- pennylane_qrack-0.24.1.dist-info/entry_points.txt +4 -0
- pennylane_qrack-0.24.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
#include <regex>
|
|
2
|
+
#include <stdexcept>
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <QuantumDevice.hpp>
|
|
5
|
+
|
|
6
|
+
#define CL_HPP_TARGET_OPENCL_VERSION 300
|
|
7
|
+
#include "qrack/qfactory.hpp"
|
|
8
|
+
|
|
9
|
+
#define QSIM_CONFIG(numQubits) Qrack::CreateArrangedLayersFull(nw, md, sd, sh, bdt, pg, tn, hy, oc, numQubits, Qrack::ZERO_BCI, nullptr, Qrack::CMPLX_DEFAULT_ARG, false, true, hp, sp)
|
|
10
|
+
|
|
11
|
+
std::string trim(std::string s)
|
|
12
|
+
{
|
|
13
|
+
// Cut leading, trailing, and extra spaces
|
|
14
|
+
// (See https://stackoverflow.com/questions/1798112/removing-leading-and-trailing-spaces-from-a-string#answer-1798170)
|
|
15
|
+
return std::regex_replace(s, std::regex("^ +| +$|( ) +"), "$1");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
struct QrackObservable {
|
|
19
|
+
std::vector<Qrack::Pauli> obs;
|
|
20
|
+
std::vector<bitLenInt> wires;
|
|
21
|
+
QrackObservable()
|
|
22
|
+
{
|
|
23
|
+
// Intentionally left blank
|
|
24
|
+
}
|
|
25
|
+
QrackObservable(std::vector<Qrack::Pauli> o, std::vector<bitLenInt> w)
|
|
26
|
+
: obs(o)
|
|
27
|
+
, wires(w)
|
|
28
|
+
{
|
|
29
|
+
// Intentionally left blank
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
struct QrackDevice final : public Catalyst::Runtime::QuantumDevice {
|
|
34
|
+
bool tapeRecording;
|
|
35
|
+
bool sh;
|
|
36
|
+
bool tn;
|
|
37
|
+
bool sd;
|
|
38
|
+
bool md;
|
|
39
|
+
bool bdt;
|
|
40
|
+
bool oc;
|
|
41
|
+
bool pg;
|
|
42
|
+
bool hy;
|
|
43
|
+
bool hp;
|
|
44
|
+
bool sp;
|
|
45
|
+
bool nw;
|
|
46
|
+
size_t shots;
|
|
47
|
+
Qrack::real1_f noise_param;
|
|
48
|
+
Qrack::QInterfacePtr qsim;
|
|
49
|
+
std::map<QubitIdType, bitLenInt> qubit_map;
|
|
50
|
+
std::vector<QrackObservable> obs_cache;
|
|
51
|
+
std::vector<Qrack::QInterfaceEngine> simulatorType;
|
|
52
|
+
|
|
53
|
+
// static constants for RESULT values
|
|
54
|
+
static constexpr bool QRACK_RESULT_TRUE_CONST = true;
|
|
55
|
+
static constexpr bool QRACK_RESULT_FALSE_CONST = false;
|
|
56
|
+
|
|
57
|
+
inline void reverseWires()
|
|
58
|
+
{
|
|
59
|
+
const bitLenInt end = qsim->GetQubitCount() - 1U;
|
|
60
|
+
const bitLenInt mid = qsim->GetQubitCount() >> 1U;
|
|
61
|
+
for (bitLenInt i = 0U; i < mid; ++i) {
|
|
62
|
+
qsim->Swap(i, end - i);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
inline auto getDeviceWires(const std::vector<QubitIdType> &wires) -> std::vector<bitLenInt>
|
|
67
|
+
{
|
|
68
|
+
std::vector<bitLenInt> res;
|
|
69
|
+
res.reserve(wires.size());
|
|
70
|
+
std::transform(wires.begin(), wires.end(), std::back_inserter(res), [this](auto w) {
|
|
71
|
+
const auto& it = qubit_map.find(w);
|
|
72
|
+
if (it == qubit_map.end()) {
|
|
73
|
+
throw std::invalid_argument("Qubit ID not in wire map: " + std::to_string(w));
|
|
74
|
+
}
|
|
75
|
+
return it->second;
|
|
76
|
+
});
|
|
77
|
+
return res;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
inline auto wiresToMask(const std::vector<bitLenInt> &wires) -> bitCapInt
|
|
81
|
+
{
|
|
82
|
+
bitCapInt mask = Qrack::ZERO_BCI;
|
|
83
|
+
for (const bitLenInt& target : wires) {
|
|
84
|
+
mask = mask | Qrack::pow2(target);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return mask;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void applyNamedOperation(const std::string &name, const std::vector<bitLenInt> &wires,
|
|
91
|
+
const bool& inverse, const std::vector<double> ¶ms)
|
|
92
|
+
{
|
|
93
|
+
if (name == "PauliX") {
|
|
94
|
+
// Self-adjoint, so ignore "inverse"
|
|
95
|
+
if (wires.size() > 1U) {
|
|
96
|
+
qsim->XMask(wiresToMask(wires));
|
|
97
|
+
} else {
|
|
98
|
+
qsim->X(wires[0U]);
|
|
99
|
+
}
|
|
100
|
+
} else if (name == "PauliY") {
|
|
101
|
+
// Self-adjoint, so ignore "inverse"
|
|
102
|
+
if (wires.size() > 1U) {
|
|
103
|
+
qsim->YMask(wiresToMask(wires));
|
|
104
|
+
} else {
|
|
105
|
+
qsim->Y(wires[0U]);
|
|
106
|
+
}
|
|
107
|
+
} else if (name == "PauliZ") {
|
|
108
|
+
// Self-adjoint, so ignore "inverse"
|
|
109
|
+
if (wires.size() > 1U) {
|
|
110
|
+
qsim->ZMask(wiresToMask(wires));
|
|
111
|
+
} else {
|
|
112
|
+
qsim->Z(wires[0U]);
|
|
113
|
+
}
|
|
114
|
+
} else if (name == "SX") {
|
|
115
|
+
for (const bitLenInt& target : wires) {
|
|
116
|
+
if (inverse) {
|
|
117
|
+
qsim->ISqrtX(target);
|
|
118
|
+
} else {
|
|
119
|
+
qsim->SqrtX(target);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} else if (name == "MultiRZ") {
|
|
123
|
+
for (const bitLenInt& target : wires) {
|
|
124
|
+
qsim->RZ(inverse ? -params[0U] : params[0U], target);
|
|
125
|
+
}
|
|
126
|
+
} else if (name == "Hadamard") {
|
|
127
|
+
for (const bitLenInt& target : wires) {
|
|
128
|
+
qsim->H(target);
|
|
129
|
+
}
|
|
130
|
+
} else if (name == "S") {
|
|
131
|
+
for (const bitLenInt& target : wires) {
|
|
132
|
+
if (inverse) {
|
|
133
|
+
qsim->IS(target);
|
|
134
|
+
} else {
|
|
135
|
+
qsim->S(target);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} else if (name == "T") {
|
|
139
|
+
for (const bitLenInt& target : wires) {
|
|
140
|
+
if (inverse) {
|
|
141
|
+
qsim->IT(target);
|
|
142
|
+
} else {
|
|
143
|
+
qsim->T(target);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else if (name == "SWAP") {
|
|
147
|
+
if (wires.size() != 2U) {
|
|
148
|
+
throw std::invalid_argument("SWAP must have exactly two target qubits!");
|
|
149
|
+
}
|
|
150
|
+
qsim->Swap(wires[0U], wires[1U]);
|
|
151
|
+
} else if (name == "ISWAP") {
|
|
152
|
+
if (wires.size() != 2U) {
|
|
153
|
+
throw std::invalid_argument("ISWAP must have exactly two target qubits!");
|
|
154
|
+
}
|
|
155
|
+
if (inverse) {
|
|
156
|
+
qsim->ISwap(wires[0U], wires[1U]);
|
|
157
|
+
} else {
|
|
158
|
+
qsim->IISwap(wires[0U], wires[1U]);
|
|
159
|
+
}
|
|
160
|
+
} else if (name == "PSWAP") {
|
|
161
|
+
if (wires.size() != 2U) {
|
|
162
|
+
throw std::invalid_argument("PSWAP must have exactly two target qubits!");
|
|
163
|
+
}
|
|
164
|
+
const std::vector<bitLenInt> c { wires[0U] };
|
|
165
|
+
qsim->CU(c, wires[1U], ZERO_R1, ZERO_R1, inverse ? -params[0U] : params[0U]);
|
|
166
|
+
qsim->Swap(wires[0U], wires[1U]);
|
|
167
|
+
qsim->CU(c, wires[1U], ZERO_R1, ZERO_R1, inverse ? -params[0U] : params[0U]);
|
|
168
|
+
} else if (name == "PhaseShift") {
|
|
169
|
+
const Qrack::complex bottomRight = exp(Qrack::I_CMPLX * (Qrack::real1)(inverse ? -params[0U] : params[0U]));
|
|
170
|
+
for (const bitLenInt& target : wires) {
|
|
171
|
+
qsim->Phase(Qrack::ONE_CMPLX, bottomRight, target);
|
|
172
|
+
}
|
|
173
|
+
} else if (name == "RX") {
|
|
174
|
+
for (const bitLenInt& target : wires) {
|
|
175
|
+
qsim->RX(inverse ? -params[0U] : params[0U], target);
|
|
176
|
+
}
|
|
177
|
+
} else if (name == "RY") {
|
|
178
|
+
for (const bitLenInt& target : wires) {
|
|
179
|
+
qsim->RY(inverse ? -params[0U] : params[0U], target);
|
|
180
|
+
}
|
|
181
|
+
} else if (name == "RZ") {
|
|
182
|
+
for (const bitLenInt& target : wires) {
|
|
183
|
+
qsim->RZ(inverse ? -params[0U] : params[0U], target);
|
|
184
|
+
}
|
|
185
|
+
} else if (name == "Rot") {
|
|
186
|
+
const Qrack::real1 phi = inverse ? -params[2U] : params[0U];
|
|
187
|
+
const Qrack::real1 theta = inverse ? -params[1U] : params[1U];
|
|
188
|
+
const Qrack::real1 omega = inverse ? -params[0U] : params[2U];
|
|
189
|
+
const Qrack::real1 cos0 = (Qrack::real1)cos(theta / 2);
|
|
190
|
+
const Qrack::real1 sin0 = (Qrack::real1)sin(theta / 2);
|
|
191
|
+
const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) * HALF_R1);
|
|
192
|
+
const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) * HALF_R1);
|
|
193
|
+
const Qrack::complex mtrx[4U]{
|
|
194
|
+
cos0 / expP, -sin0 * expM,
|
|
195
|
+
sin0 / expM, cos0 * expP
|
|
196
|
+
};
|
|
197
|
+
for (const bitLenInt& target : wires) {
|
|
198
|
+
qsim->Mtrx(mtrx, target);
|
|
199
|
+
}
|
|
200
|
+
} else if (name == "U3") {
|
|
201
|
+
for (const bitLenInt& target : wires) {
|
|
202
|
+
if (inverse) {
|
|
203
|
+
qsim->U(target, -params[0U], -params[2U], -params[1U]);
|
|
204
|
+
} else {
|
|
205
|
+
qsim->U(target, params[0U], params[1U], params[2U]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} else if (name != "Identity") {
|
|
209
|
+
throw std::domain_error("Unrecognized gate name: " + name);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
void applyNamedOperation(const std::string &name, const std::vector<bitLenInt> &control_wires,
|
|
214
|
+
const std::vector<bool> &control_values,
|
|
215
|
+
const std::vector<bitLenInt> &wires, const bool& inverse,
|
|
216
|
+
const std::vector<double> ¶ms)
|
|
217
|
+
{
|
|
218
|
+
bitCapInt controlPerm = Qrack::ZERO_BCI;
|
|
219
|
+
for (bitLenInt i = 0U; i < control_values.size(); ++i) {
|
|
220
|
+
if (control_values[i]) {
|
|
221
|
+
controlPerm = controlPerm | Qrack::pow2(i);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
QRACK_CONST Qrack::complex NEG_1_CMPLX(-ONE_R1, ZERO_R1);
|
|
226
|
+
QRACK_CONST Qrack::complex NEG_I_CMPLX(ZERO_R1, ONE_R1);
|
|
227
|
+
QRACK_CONST Qrack::complex SQRT1_2_CMPLX(Qrack::SQRT1_2_R1, ZERO_R1);
|
|
228
|
+
QRACK_CONST Qrack::complex NEG_SQRT1_2_CMPLX(-Qrack::SQRT1_2_R1, ZERO_R1);
|
|
229
|
+
QRACK_CONST Qrack::complex SQRTI_2_CMPLX(ZERO_R1, Qrack::SQRT1_2_R1);
|
|
230
|
+
QRACK_CONST Qrack::complex NEG_SQRTI_2_CMPLX(ZERO_R1, -Qrack::SQRT1_2_R1);
|
|
231
|
+
const Qrack::complex QBRTI_2_CMPLX(ZERO_R1, sqrt(Qrack::SQRT1_2_R1));
|
|
232
|
+
const Qrack::complex NEG_QBRTI_2_CMPLX(ZERO_R1, sqrt(-Qrack::SQRT1_2_R1));
|
|
233
|
+
|
|
234
|
+
QRACK_CONST Qrack::complex pauliX[4U] = { Qrack::ZERO_CMPLX, Qrack::ONE_CMPLX, Qrack::ONE_CMPLX, Qrack::ZERO_CMPLX };
|
|
235
|
+
QRACK_CONST Qrack::complex pauliY[4U] = { Qrack::ZERO_CMPLX, NEG_I_CMPLX, Qrack::I_CMPLX, Qrack::ZERO_CMPLX };
|
|
236
|
+
QRACK_CONST Qrack::complex pauliZ[4U] = { Qrack::ONE_CMPLX, Qrack::ZERO_CMPLX, Qrack::ZERO_CMPLX, NEG_1_CMPLX };
|
|
237
|
+
QRACK_CONST Qrack::complex sqrtX[4U]{ Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX };
|
|
238
|
+
QRACK_CONST Qrack::complex iSqrtX[4U]{ Qrack::HALF_NEG_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_I_HALF_CMPLX, Qrack::HALF_NEG_I_HALF_CMPLX };
|
|
239
|
+
QRACK_CONST Qrack::complex hadamard[4U]{ SQRT1_2_CMPLX, SQRT1_2_CMPLX, SQRT1_2_CMPLX, NEG_SQRT1_2_CMPLX };
|
|
240
|
+
|
|
241
|
+
if ((name == "PauliX") || (name == "CNOT") || (name == "Toffoli") || (name == "MultiControlledX")) {
|
|
242
|
+
// Self-adjoint, so ignore "inverse"
|
|
243
|
+
for (const bitLenInt& target : wires) {
|
|
244
|
+
qsim->UCMtrx(control_wires, pauliX, target, controlPerm);
|
|
245
|
+
}
|
|
246
|
+
} else if ((name == "PauliY") || (name == "CY")) {
|
|
247
|
+
// Self-adjoint, so ignore "inverse"
|
|
248
|
+
for (const bitLenInt& target : wires) {
|
|
249
|
+
qsim->UCMtrx(control_wires, pauliY, target, controlPerm);
|
|
250
|
+
}
|
|
251
|
+
} else if ((name == "PauliZ") || (name == "CZ")) {
|
|
252
|
+
// Self-adjoint, so ignore "inverse"
|
|
253
|
+
for (const bitLenInt& target : wires) {
|
|
254
|
+
qsim->UCMtrx(control_wires, pauliZ, target, controlPerm);
|
|
255
|
+
}
|
|
256
|
+
} else if (name == "SX") {
|
|
257
|
+
for (const bitLenInt& target : wires) {
|
|
258
|
+
qsim->UCMtrx(control_wires, inverse ? iSqrtX : sqrtX, target, controlPerm);
|
|
259
|
+
}
|
|
260
|
+
} else if (name == "MultiRZ") {
|
|
261
|
+
const Qrack::complex bottomRight = exp(Qrack::I_CMPLX * (Qrack::real1)((inverse ? -params[0U] : params[0U]) / 2));
|
|
262
|
+
for (const bitLenInt& target : wires) {
|
|
263
|
+
qsim->UCPhase(control_wires, conj(bottomRight), bottomRight, target, controlPerm);
|
|
264
|
+
}
|
|
265
|
+
} else if (name == "Hadamard") {
|
|
266
|
+
for (const bitLenInt& target : wires) {
|
|
267
|
+
qsim->UCMtrx(control_wires, hadamard, target, controlPerm);
|
|
268
|
+
}
|
|
269
|
+
} else if (name == "S") {
|
|
270
|
+
for (const bitLenInt& target : wires) {
|
|
271
|
+
qsim->UCPhase(control_wires, Qrack::ONE_CMPLX, inverse ? NEG_SQRTI_2_CMPLX : SQRTI_2_CMPLX, target, controlPerm);
|
|
272
|
+
}
|
|
273
|
+
} else if (name == "T") {
|
|
274
|
+
for (const bitLenInt& target : wires) {
|
|
275
|
+
qsim->UCPhase(control_wires, Qrack::ONE_CMPLX, inverse ? NEG_QBRTI_2_CMPLX : QBRTI_2_CMPLX, target, controlPerm);
|
|
276
|
+
}
|
|
277
|
+
} else if ((name == "SWAP") || (name == "CSWAP")) {
|
|
278
|
+
if (wires.size() != 2U) {
|
|
279
|
+
throw std::invalid_argument("SWAP and CSWAP must have exactly two target qubits!");
|
|
280
|
+
}
|
|
281
|
+
for (bitLenInt i = 0U; i < control_wires.size(); ++i) {
|
|
282
|
+
if (!control_values[i]) {
|
|
283
|
+
qsim->X(control_wires[i]);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
qsim->CSwap(control_wires, wires[0U], wires[1U]);
|
|
287
|
+
for (bitLenInt i = 0U; i < control_wires.size(); ++i) {
|
|
288
|
+
if (!control_values[i]) {
|
|
289
|
+
qsim->X(control_wires[i]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} else if (name == "ISWAP") {
|
|
293
|
+
if (wires.size() != 2U) {
|
|
294
|
+
throw std::invalid_argument("ISWAP must have exactly two target qubits!");
|
|
295
|
+
}
|
|
296
|
+
for (bitLenInt i = 0U; i < control_wires.size(); ++i) {
|
|
297
|
+
if (!control_values[i]) {
|
|
298
|
+
qsim->X(control_wires[i]);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
std::vector<bitLenInt> mcp_wires(control_wires);
|
|
302
|
+
mcp_wires.push_back(wires[0U]);
|
|
303
|
+
qsim->MCPhase(mcp_wires, inverse ? -Qrack::I_CMPLX : Qrack::I_CMPLX, Qrack::ONE_CMPLX, wires[1U]);
|
|
304
|
+
qsim->CSwap(control_wires, wires[0U], wires[1U]);
|
|
305
|
+
qsim->MCPhase(mcp_wires, inverse ? -Qrack::I_CMPLX : Qrack::I_CMPLX, Qrack::ONE_CMPLX, wires[1U]);
|
|
306
|
+
for (bitLenInt i = 0U; i < control_wires.size(); ++i) {
|
|
307
|
+
if (!control_values[i]) {
|
|
308
|
+
qsim->X(control_wires[i]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} else if ((name == "PhaseShift") || (name == "ControlledPhaseShift") || (name == "CPhase")) {
|
|
312
|
+
const Qrack::complex bottomRight = exp(Qrack::I_CMPLX * (Qrack::real1)(inverse ? -params[0U] : params[0U]));
|
|
313
|
+
for (const bitLenInt& target : wires) {
|
|
314
|
+
qsim->UCPhase(control_wires, Qrack::ONE_CMPLX, bottomRight, target, controlPerm);
|
|
315
|
+
}
|
|
316
|
+
} else if (name == "PSWAP") {
|
|
317
|
+
std::vector<bitLenInt> c(control_wires);
|
|
318
|
+
c.push_back(wires[0U]);
|
|
319
|
+
qsim->CU(c, wires[1U], ZERO_R1, ZERO_R1, inverse ? -params[0U] : params[0U]);
|
|
320
|
+
qsim->CSwap(control_wires, wires[0U], wires[1U]);
|
|
321
|
+
qsim->CU(c, wires[1U], ZERO_R1, ZERO_R1, inverse ? -params[0U] : params[0U]);
|
|
322
|
+
} else if ((name == "RX") || (name == "CRX")) {
|
|
323
|
+
const Qrack::real1 cosine = (Qrack::real1)cos((inverse ? -params[0U] : params[0U]) / 2);
|
|
324
|
+
const Qrack::real1 sine = (Qrack::real1)sin((inverse ? -params[0U] : params[0U]) / 2);
|
|
325
|
+
const Qrack::complex mtrx[4U] = {
|
|
326
|
+
Qrack::complex(cosine, ZERO_R1), Qrack::complex(ZERO_R1, -sine),
|
|
327
|
+
Qrack::complex(ZERO_R1, -sine), Qrack::complex(cosine, ZERO_R1)
|
|
328
|
+
};
|
|
329
|
+
for (const bitLenInt& target : wires) {
|
|
330
|
+
qsim->UCMtrx(control_wires, mtrx, target, controlPerm);
|
|
331
|
+
}
|
|
332
|
+
} else if ((name == "RY") || (name == "CRY")) {
|
|
333
|
+
const Qrack::real1 cosine = (Qrack::real1)cos((inverse ? -params[0U] : params[0U]) / 2);
|
|
334
|
+
const Qrack::real1 sine = (Qrack::real1)sin((inverse ? -params[0U] : params[0U]) / 2);
|
|
335
|
+
const Qrack::complex mtrx[4U] = {
|
|
336
|
+
Qrack::complex(cosine, ZERO_R1), Qrack::complex(-sine, ZERO_R1),
|
|
337
|
+
Qrack::complex(sine, ZERO_R1), Qrack::complex(cosine, ZERO_R1)
|
|
338
|
+
};
|
|
339
|
+
for (const bitLenInt& target : wires) {
|
|
340
|
+
qsim->UCMtrx(control_wires, mtrx, target, controlPerm);
|
|
341
|
+
}
|
|
342
|
+
} else if ((name == "RZ") || (name == "CRZ")) {
|
|
343
|
+
const Qrack::complex bottomRight = exp(Qrack::I_CMPLX * (Qrack::real1)((inverse ? -params[0U] : params[0U]) / 2));
|
|
344
|
+
for (const bitLenInt& target : wires) {
|
|
345
|
+
qsim->UCPhase(control_wires, conj(bottomRight), bottomRight, target, controlPerm);
|
|
346
|
+
}
|
|
347
|
+
} else if ((name == "Rot") || (name == "CRot")) {
|
|
348
|
+
const Qrack::real1 phi = inverse ? -params[2U] : params[0U];
|
|
349
|
+
const Qrack::real1 theta = inverse ? -params[1U] : params[1U];
|
|
350
|
+
const Qrack::real1 omega = inverse ? -params[0U] : params[2U];
|
|
351
|
+
const Qrack::real1 cos0 = (Qrack::real1)cos(theta / 2);
|
|
352
|
+
const Qrack::real1 sin0 = (Qrack::real1)sin(theta / 2);
|
|
353
|
+
const Qrack::complex expP = exp(Qrack::I_CMPLX * (phi + omega) * HALF_R1);
|
|
354
|
+
const Qrack::complex expM = exp(Qrack::I_CMPLX * (phi - omega) * HALF_R1);
|
|
355
|
+
const Qrack::complex mtrx[4U]{
|
|
356
|
+
cos0 / expP, -sin0 * expM,
|
|
357
|
+
sin0 / expM, cos0 * expP
|
|
358
|
+
};
|
|
359
|
+
for (const bitLenInt& target : wires) {
|
|
360
|
+
qsim->UCMtrx(control_wires, mtrx, target, controlPerm);
|
|
361
|
+
}
|
|
362
|
+
} else if (name == "U3") {
|
|
363
|
+
const Qrack::real1 th = params[0U];
|
|
364
|
+
const Qrack::real1 ph = params[1U];
|
|
365
|
+
const Qrack::real1 lm = params[2U];
|
|
366
|
+
const Qrack::real1 cos0 = (Qrack::real1)cos(th / 2);
|
|
367
|
+
const Qrack::real1 sin0 = (Qrack::real1)sin(th / 2);
|
|
368
|
+
const Qrack::complex mtrx[4U]{
|
|
369
|
+
Qrack::complex(cos0, ZERO_R1), sin0 * Qrack::complex((Qrack::real1)(-cos(lm)),
|
|
370
|
+
(Qrack::real1)(-sin(lm))),
|
|
371
|
+
sin0 * Qrack::complex((Qrack::real1)cos(ph), (Qrack::real1)sin(ph)),
|
|
372
|
+
cos0 * Qrack::complex((Qrack::real1)cos(ph + lm), (Qrack::real1)sin(ph + lm))
|
|
373
|
+
};
|
|
374
|
+
Qrack::complex iMtrx[4U];
|
|
375
|
+
Qrack::inv2x2(mtrx, iMtrx);
|
|
376
|
+
for (const bitLenInt& target : wires) {
|
|
377
|
+
qsim->UCMtrx(control_wires, inverse ? iMtrx : mtrx, target, controlPerm);
|
|
378
|
+
}
|
|
379
|
+
} else if (name != "Identity") {
|
|
380
|
+
throw std::domain_error("Unrecognized gate name: " + name);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
QrackDevice([[maybe_unused]] std::string kwargs = "{}")
|
|
385
|
+
: tapeRecording(false)
|
|
386
|
+
, sh(true)
|
|
387
|
+
, tn(true)
|
|
388
|
+
, sd(true)
|
|
389
|
+
, md(true)
|
|
390
|
+
, bdt(false)
|
|
391
|
+
, oc(true)
|
|
392
|
+
, pg(false)
|
|
393
|
+
, hy(false)
|
|
394
|
+
, hp(false)
|
|
395
|
+
, sp(false)
|
|
396
|
+
, nw(false)
|
|
397
|
+
, shots(1U)
|
|
398
|
+
, noise_param(ZERO_R1_F)
|
|
399
|
+
, qsim(nullptr)
|
|
400
|
+
{
|
|
401
|
+
// Cut leading '{' and trailing '}'
|
|
402
|
+
kwargs.erase(0U, 1U);
|
|
403
|
+
kwargs.erase(kwargs.size() - 1U);
|
|
404
|
+
// Cut leading, trailing, and extra spaces
|
|
405
|
+
kwargs = trim(kwargs);
|
|
406
|
+
|
|
407
|
+
std::map<std::string, int> keyMap;
|
|
408
|
+
keyMap["'is_hybrid_stabilizer'"] = 1;
|
|
409
|
+
keyMap["'is_tensor_network'"] = 2;
|
|
410
|
+
keyMap["'is_schmidt_decomposed'"] = 3;
|
|
411
|
+
keyMap["'is_schmidt_decomposition_parallel'"] = 4;
|
|
412
|
+
keyMap["'is_qbdd'"] = 5;
|
|
413
|
+
keyMap["'is_gpu'"] = 6;
|
|
414
|
+
keyMap["'is_paged'"] = 7;
|
|
415
|
+
keyMap["'is_hybrid_cpu_gpu'"] = 8;
|
|
416
|
+
keyMap["'is_host_pointer'"] =9;
|
|
417
|
+
keyMap["'is_sparse'"] =10;
|
|
418
|
+
keyMap["'noise'"] = 11;
|
|
419
|
+
|
|
420
|
+
size_t pos;
|
|
421
|
+
while ((pos = kwargs.find(":")) != std::string::npos) {
|
|
422
|
+
std::string key = trim(kwargs.substr(0, pos));
|
|
423
|
+
kwargs.erase(0, pos + 1U);
|
|
424
|
+
pos = kwargs.find(",");
|
|
425
|
+
std::string value = trim(kwargs.substr(0, pos));
|
|
426
|
+
kwargs.erase(0, pos + 1U);
|
|
427
|
+
const bool val = (value == "True");
|
|
428
|
+
switch (keyMap[key]) {
|
|
429
|
+
case 1:
|
|
430
|
+
sh = val;
|
|
431
|
+
break;
|
|
432
|
+
case 2:
|
|
433
|
+
tn = val;
|
|
434
|
+
break;
|
|
435
|
+
case 3:
|
|
436
|
+
sd = val;
|
|
437
|
+
break;
|
|
438
|
+
case 4:
|
|
439
|
+
md = val;
|
|
440
|
+
break;
|
|
441
|
+
case 5:
|
|
442
|
+
bdt = val;
|
|
443
|
+
break;
|
|
444
|
+
case 6:
|
|
445
|
+
oc = val;
|
|
446
|
+
break;
|
|
447
|
+
case 7:
|
|
448
|
+
pg = val;
|
|
449
|
+
break;
|
|
450
|
+
case 8:
|
|
451
|
+
hy = val;
|
|
452
|
+
break;
|
|
453
|
+
case 9:
|
|
454
|
+
hp = val;
|
|
455
|
+
break;
|
|
456
|
+
case 10:
|
|
457
|
+
sp = val;
|
|
458
|
+
break;
|
|
459
|
+
case 11:
|
|
460
|
+
noise_param = std::stof(value);
|
|
461
|
+
nw = noise_param > ZERO_R1;
|
|
462
|
+
break;
|
|
463
|
+
default:
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
qsim = QSIM_CONFIG(0U);
|
|
469
|
+
if (noise_param > ZERO_R1) {
|
|
470
|
+
qsim->SetNoiseParameter(noise_param);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
QrackDevice &operator=(const QuantumDevice &) = delete;
|
|
475
|
+
QrackDevice(const QrackDevice &) = delete;
|
|
476
|
+
QrackDevice(QrackDevice &&) = delete;
|
|
477
|
+
QrackDevice &operator=(QuantumDevice &&) = delete;
|
|
478
|
+
|
|
479
|
+
auto AllocateQubit() -> QubitIdType override {
|
|
480
|
+
const QubitIdType label = qsim->GetQubitCount();
|
|
481
|
+
qsim->Allocate(1U);
|
|
482
|
+
qubit_map[label] = (bitLenInt)label;
|
|
483
|
+
return label;
|
|
484
|
+
}
|
|
485
|
+
auto AllocateQubits(size_t num_qubits) -> std::vector<QubitIdType> override {
|
|
486
|
+
std::vector<QubitIdType> ids(num_qubits);
|
|
487
|
+
for (size_t i = 0U; i < num_qubits; ++i) {
|
|
488
|
+
ids[i] = AllocateQubit();
|
|
489
|
+
}
|
|
490
|
+
return ids;
|
|
491
|
+
}
|
|
492
|
+
auto Observable(ObsId id, const std::vector<std::complex<double>> &matrix,
|
|
493
|
+
const std::vector<QubitIdType> &wires) -> ObsIdType override
|
|
494
|
+
{
|
|
495
|
+
RT_FAIL_IF(wires.size() != 1U, "Cannot have observables besides tensor products of Pauli observables");
|
|
496
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
497
|
+
|
|
498
|
+
Qrack::Pauli basis = Qrack::PauliI;
|
|
499
|
+
switch (id) {
|
|
500
|
+
case ObsId::PauliX:
|
|
501
|
+
basis = Qrack::PauliX;
|
|
502
|
+
break;
|
|
503
|
+
case ObsId::PauliY:
|
|
504
|
+
basis = Qrack::PauliY;
|
|
505
|
+
break;
|
|
506
|
+
case ObsId::PauliZ:
|
|
507
|
+
basis = Qrack::PauliZ;
|
|
508
|
+
break;
|
|
509
|
+
default:
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
std::vector<Qrack::Pauli> bases;
|
|
513
|
+
bases.reserve(dev_wires.size());
|
|
514
|
+
for (size_t i = 0U; i < dev_wires.size(); ++i) {
|
|
515
|
+
bases.emplace_back(basis);
|
|
516
|
+
}
|
|
517
|
+
obs_cache.push_back(QrackObservable(bases, dev_wires));
|
|
518
|
+
|
|
519
|
+
return obs_cache.size() - 1U;
|
|
520
|
+
}
|
|
521
|
+
auto TensorObservable(const std::vector<ObsIdType> &obs) -> ObsIdType override
|
|
522
|
+
{
|
|
523
|
+
if (!obs.size()) {
|
|
524
|
+
return -1;
|
|
525
|
+
}
|
|
526
|
+
QrackObservable o;
|
|
527
|
+
for (const ObsIdType& id : obs) {
|
|
528
|
+
if (id >= obs_cache.size()) {
|
|
529
|
+
throw std::invalid_argument("Observable ID not in device cache: " + std::to_string(id));
|
|
530
|
+
}
|
|
531
|
+
const QrackObservable& i = obs_cache[id];
|
|
532
|
+
o.obs.insert(o.obs.end(), i.obs.begin(), i.obs.end());
|
|
533
|
+
o.wires.insert(o.wires.end(), i.wires.begin(), i.wires.end());
|
|
534
|
+
}
|
|
535
|
+
obs_cache.push_back(o);
|
|
536
|
+
|
|
537
|
+
return obs_cache.size() - 1U;
|
|
538
|
+
}
|
|
539
|
+
auto HamiltonianObservable(const std::vector<double> &coeffs, const std::vector<ObsIdType> &obs)
|
|
540
|
+
-> ObsIdType override
|
|
541
|
+
{
|
|
542
|
+
return -1;
|
|
543
|
+
}
|
|
544
|
+
auto Measure(QubitIdType id, std::optional<int> postselect) -> Result override {
|
|
545
|
+
bool *ret = (bool *)malloc(sizeof(bool));
|
|
546
|
+
if (postselect.has_value()) {
|
|
547
|
+
*ret = qsim->ForceM(id, postselect.value());
|
|
548
|
+
} else {
|
|
549
|
+
*ret = qsim->M(id);
|
|
550
|
+
}
|
|
551
|
+
return ret;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
void ReleaseQubit(QubitIdType label) override
|
|
555
|
+
{
|
|
556
|
+
// Measure to prevent denormalization
|
|
557
|
+
const bitLenInt id = qubit_map[label];
|
|
558
|
+
qsim->M(id);
|
|
559
|
+
// Deallocate
|
|
560
|
+
qsim->Dispose(id, 1U);
|
|
561
|
+
}
|
|
562
|
+
void ReleaseQubits(const std::vector<QubitIdType> &qubits) override
|
|
563
|
+
{
|
|
564
|
+
for (const QubitIdType& q : qubits) {
|
|
565
|
+
ReleaseQubit(q);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
[[nodiscard]] auto GetNumQubits() const -> size_t override
|
|
569
|
+
{
|
|
570
|
+
return qsim->GetQubitCount();
|
|
571
|
+
}
|
|
572
|
+
void SetDeviceShots(size_t s) override {
|
|
573
|
+
if ((s > 1U) && (noise_param > ZERO_R1_F)) {
|
|
574
|
+
throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
|
|
575
|
+
}
|
|
576
|
+
shots = s;
|
|
577
|
+
}
|
|
578
|
+
[[nodiscard]] auto GetDeviceShots() const -> size_t override { return shots; }
|
|
579
|
+
void StartTapeRecording() override { tapeRecording = true; }
|
|
580
|
+
void StopTapeRecording() override { tapeRecording = false; }
|
|
581
|
+
void NamedOperation(const std::string &name, const std::vector<double> ¶ms,
|
|
582
|
+
const std::vector<QubitIdType> &wires, bool inverse,
|
|
583
|
+
const std::vector<QubitIdType> &controlled_wires,
|
|
584
|
+
const std::vector<bool> &controlled_values) override
|
|
585
|
+
{
|
|
586
|
+
// Check the validity of number of qubits and parameters
|
|
587
|
+
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), "Controlled wires/values size mismatch");
|
|
588
|
+
|
|
589
|
+
// Convert wires to device wires
|
|
590
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
591
|
+
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);
|
|
592
|
+
std::vector<bool> dev_controlled_values(controlled_values);
|
|
593
|
+
if ((name == "MultiControlledX")
|
|
594
|
+
|| (name == "CNOT")
|
|
595
|
+
|| (name == "CY")
|
|
596
|
+
|| (name == "CZ")
|
|
597
|
+
|| (name == "ControlledPhaseShift")
|
|
598
|
+
|| (name == "CPhase")
|
|
599
|
+
|| (name == "CRX")
|
|
600
|
+
|| (name == "CRY")
|
|
601
|
+
|| (name == "CRZ")
|
|
602
|
+
|| (name == "CRot")
|
|
603
|
+
|| (name == "Toffoli")) {
|
|
604
|
+
const size_t end = dev_wires.size() - 1U;
|
|
605
|
+
dev_controlled_wires.insert(dev_controlled_wires.end(), dev_wires.begin(), dev_wires.begin() + end);
|
|
606
|
+
dev_wires.erase(dev_wires.begin(), dev_wires.begin() + end);
|
|
607
|
+
const std::vector<bool> t(end, true);
|
|
608
|
+
dev_controlled_values.insert(dev_controlled_values.end(), t.begin(), t.end());
|
|
609
|
+
} else if (name == "CSWAP") {
|
|
610
|
+
const size_t end = dev_wires.size() - 2U;
|
|
611
|
+
dev_controlled_wires.insert(dev_controlled_wires.end(), dev_wires.begin(), dev_wires.begin() + end);
|
|
612
|
+
dev_wires.erase(dev_wires.begin(), dev_wires.begin() + end);
|
|
613
|
+
const std::vector<bool> t(end, true);
|
|
614
|
+
dev_controlled_values.insert(dev_controlled_values.end(), t.begin(), t.end());
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// Update the state-vector
|
|
618
|
+
if (dev_controlled_wires.empty()) {
|
|
619
|
+
applyNamedOperation(name, dev_wires, inverse, params);
|
|
620
|
+
} else {
|
|
621
|
+
applyNamedOperation(name, dev_controlled_wires, dev_controlled_values, dev_wires, inverse, params);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
void MatrixOperation(const std::vector<std::complex<double>> &matrix,
|
|
625
|
+
const std::vector<QubitIdType> &wires, bool inverse,
|
|
626
|
+
const std::vector<QubitIdType> &controlled_wires,
|
|
627
|
+
const std::vector<bool> &controlled_values) override
|
|
628
|
+
{
|
|
629
|
+
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), "Controlled wires/values size mismatch");
|
|
630
|
+
RT_FAIL_IF(wires.size() != 1U, "Matrix operation can only have one target qubit!");
|
|
631
|
+
|
|
632
|
+
// Convert wires to device wires
|
|
633
|
+
// with checking validity of wires
|
|
634
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
635
|
+
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);
|
|
636
|
+
const Qrack::complex mtrx[4U] = {
|
|
637
|
+
(Qrack::complex)matrix[0U], (Qrack::complex)matrix[1U],
|
|
638
|
+
(Qrack::complex)matrix[2U], (Qrack::complex)matrix[3U]
|
|
639
|
+
};
|
|
640
|
+
Qrack::complex inv[4U];
|
|
641
|
+
Qrack::inv2x2(mtrx, inv);
|
|
642
|
+
|
|
643
|
+
// Update the state-vector
|
|
644
|
+
if (dev_controlled_wires.empty()) {
|
|
645
|
+
qsim->Mtrx(inverse ? inv : mtrx, dev_wires[0U]);
|
|
646
|
+
} else {
|
|
647
|
+
bitCapInt controlPerm = Qrack::ZERO_BCI;
|
|
648
|
+
for (bitLenInt i = 0U; i < controlled_values.size(); ++i) {
|
|
649
|
+
if (controlled_values[i]) {
|
|
650
|
+
controlPerm = controlPerm | Qrack::pow2(i);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
qsim->UCMtrx(dev_controlled_wires, inverse ? inv : mtrx, dev_wires[0U], controlPerm);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
auto Expval(ObsIdType id) -> double override
|
|
657
|
+
{
|
|
658
|
+
if (id >= obs_cache.size()) {
|
|
659
|
+
throw std::invalid_argument("Observable ID not in device cache: " + std::to_string(id));
|
|
660
|
+
}
|
|
661
|
+
const QrackObservable& obs = obs_cache[id];
|
|
662
|
+
return qsim->ExpectationPauliAll(obs.wires, obs.obs);
|
|
663
|
+
}
|
|
664
|
+
auto Var(ObsIdType id) -> double override
|
|
665
|
+
{
|
|
666
|
+
if (id >= obs_cache.size()) {
|
|
667
|
+
throw std::invalid_argument("Observable ID not in device cache: " + std::to_string(id));
|
|
668
|
+
}
|
|
669
|
+
const QrackObservable& obs = obs_cache[id];
|
|
670
|
+
return qsim->VariancePauliAll(obs.wires, obs.obs);
|
|
671
|
+
}
|
|
672
|
+
void State(DataView<std::complex<double>, 1>& sv) override
|
|
673
|
+
{
|
|
674
|
+
RT_FAIL_IF(sv.size() != (size_t)((uint64_t)qsim->GetMaxQPower()), "Invalid size for the pre-allocated state vector");
|
|
675
|
+
reverseWires();
|
|
676
|
+
#if FPPOW == 6
|
|
677
|
+
qsim->GetQuantumState(&(*(sv.begin())));
|
|
678
|
+
#else
|
|
679
|
+
std::unique_ptr<Qrack::complex> _sv(new Qrack::complex[sv.size()]);
|
|
680
|
+
qsim->GetQuantumState(_sv.get());
|
|
681
|
+
std::copy(_sv.get(), _sv.get() + sv.size(), sv.begin());
|
|
682
|
+
#endif
|
|
683
|
+
reverseWires();
|
|
684
|
+
}
|
|
685
|
+
void Probs(DataView<double, 1>& p) override
|
|
686
|
+
{
|
|
687
|
+
RT_FAIL_IF(p.size() != (size_t)((uint64_t)qsim->GetMaxQPower()), "Invalid size for the pre-allocated probabilities vector");
|
|
688
|
+
reverseWires();
|
|
689
|
+
#if FPPOW == 6
|
|
690
|
+
qsim->GetProbs(&(*(p.begin())));
|
|
691
|
+
#else
|
|
692
|
+
std::unique_ptr<Qrack::real1> _p(new Qrack::real1[p.size()]);
|
|
693
|
+
qsim->GetProbs(_p.get());
|
|
694
|
+
std::copy(_p.get(), _p.get() + p.size(), p.begin());
|
|
695
|
+
#endif
|
|
696
|
+
reverseWires();
|
|
697
|
+
}
|
|
698
|
+
void PartialProbs(DataView<double, 1> &p, const std::vector<QubitIdType> &wires) override
|
|
699
|
+
{
|
|
700
|
+
RT_FAIL_IF((size_t)((uint64_t)Qrack::pow2(wires.size())) != p.size(), "Invalid size for the pre-allocated probabilities vector");
|
|
701
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
702
|
+
std::reverse(dev_wires.begin(), dev_wires.end());
|
|
703
|
+
#if FPPOW == 6
|
|
704
|
+
qsim->ProbBitsAll(dev_wires, &(*(p.begin())));
|
|
705
|
+
#else
|
|
706
|
+
std::unique_ptr<Qrack::real1> _p(new Qrack::real1[p.size()]);
|
|
707
|
+
qsim->ProbBitsAll(dev_wires, _p.get());
|
|
708
|
+
std::copy(_p.get(), _p.get() + p.size(), p.begin());
|
|
709
|
+
#endif
|
|
710
|
+
}
|
|
711
|
+
void _SampleBody(const size_t numQubits, const std::map<bitCapInt, int>& q_samples, DataView<double, 2> &samples)
|
|
712
|
+
{
|
|
713
|
+
auto samplesIter = samples.begin();
|
|
714
|
+
auto q_samplesIter = q_samples.begin();
|
|
715
|
+
for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) {
|
|
716
|
+
const bitCapInt sample = q_samplesIter->first;
|
|
717
|
+
int shots = q_samplesIter->second;
|
|
718
|
+
for (; shots > 0; --shots) {
|
|
719
|
+
for (size_t wire = 0U; wire < numQubits; ++wire) {
|
|
720
|
+
*(samplesIter++) = bi_compare_0((sample >> wire) & 1U) ? 1.0 : 0.0;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
void Sample(DataView<double, 2> &samples) override
|
|
726
|
+
{
|
|
727
|
+
RT_FAIL_IF(samples.size() != shots * qsim->GetQubitCount(), "Invalid size for the pre-allocated samples");
|
|
728
|
+
|
|
729
|
+
if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
|
|
730
|
+
throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (shots == 1U) {
|
|
734
|
+
const bitCapInt rev_sample = qsim->MAll();
|
|
735
|
+
const bitLenInt numQubits = qsim->GetQubitCount();
|
|
736
|
+
bitCapInt sample = Qrack::ZERO_BCI;
|
|
737
|
+
for (bitLenInt i = 0U; i < numQubits; ++i) {
|
|
738
|
+
if (bi_compare_0(rev_sample & Qrack::pow2(i)) != 0) {
|
|
739
|
+
sample = sample | Qrack::pow2(numQubits - (i + 1U));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
const std::map<bitCapInt, int> q_samples{{sample, 1}};
|
|
743
|
+
_SampleBody(numQubits, q_samples, samples);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
std::vector<bitCapInt> qPowers(qsim->GetQubitCount());
|
|
748
|
+
for (bitLenInt i = 0U; i < qPowers.size(); ++i) {
|
|
749
|
+
qPowers[i] = Qrack::pow2(i);
|
|
750
|
+
}
|
|
751
|
+
const std::map<bitCapInt, int> q_samples = qsim->MultiShotMeasureMask(qPowers, shots);
|
|
752
|
+
_SampleBody(qPowers.size(), q_samples, samples);
|
|
753
|
+
}
|
|
754
|
+
void PartialSample(DataView<double, 2> &samples, const std::vector<QubitIdType> &wires) override
|
|
755
|
+
{
|
|
756
|
+
RT_FAIL_IF(samples.size() != shots * wires.size(), "Invalid size for the pre-allocated samples");
|
|
757
|
+
|
|
758
|
+
if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
|
|
759
|
+
throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
763
|
+
|
|
764
|
+
if (shots == 1U) {
|
|
765
|
+
const bitCapInt rev_sample = qsim->MAll();
|
|
766
|
+
const bitLenInt numQubits = dev_wires.size();
|
|
767
|
+
bitCapInt sample = Qrack::ZERO_BCI;
|
|
768
|
+
for (bitLenInt i = 0U; i < numQubits; ++i) {
|
|
769
|
+
if (bi_compare_0(rev_sample & Qrack::pow2(dev_wires[i])) != 0) {
|
|
770
|
+
sample = sample | Qrack::pow2(numQubits - (i + 1U));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const std::map<bitCapInt, int> q_samples{{sample, 1}};
|
|
774
|
+
_SampleBody(numQubits, q_samples, samples);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
std::vector<bitCapInt> qPowers(dev_wires.size());
|
|
779
|
+
for (size_t i = 0U; i < qPowers.size(); ++i) {
|
|
780
|
+
qPowers[i] = Qrack::pow2(dev_wires[i]);
|
|
781
|
+
}
|
|
782
|
+
const std::map<bitCapInt, int> q_samples = qsim->MultiShotMeasureMask(qPowers, shots);
|
|
783
|
+
_SampleBody(qPowers.size(), q_samples, samples);
|
|
784
|
+
}
|
|
785
|
+
void _CountsBody(const size_t numQubits, const std::map<bitCapInt, int>& q_samples, DataView<int64_t, 1> &counts)
|
|
786
|
+
{
|
|
787
|
+
for (auto q_samplesIter = q_samples.begin(); q_samplesIter != q_samples.end(); ++q_samplesIter) {
|
|
788
|
+
const bitCapInt sample = q_samplesIter->first;
|
|
789
|
+
int shots = q_samplesIter->second;
|
|
790
|
+
std::bitset<1U << QBCAPPOW> basisState;
|
|
791
|
+
for (size_t wire = 0; wire < numQubits; wire++) {
|
|
792
|
+
basisState[wire] = bi_compare_0((sample >> wire) & 1U);
|
|
793
|
+
}
|
|
794
|
+
for (; shots > 0; --shots) {
|
|
795
|
+
++counts(static_cast<size_t>(basisState.to_ulong()));
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
void Counts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts) override
|
|
800
|
+
{
|
|
801
|
+
const size_t numQubits = qsim->GetQubitCount();
|
|
802
|
+
const size_t numElements = 1U << numQubits;
|
|
803
|
+
|
|
804
|
+
RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements,
|
|
805
|
+
"Invalid size for the pre-allocated counts");
|
|
806
|
+
|
|
807
|
+
std::map<bitCapInt, int> q_samples;
|
|
808
|
+
if (shots == 1U) {
|
|
809
|
+
const bitCapInt rev_sample = qsim->MAll();
|
|
810
|
+
const bitLenInt numQubits = qsim->GetQubitCount();
|
|
811
|
+
bitCapInt sample = Qrack::ZERO_BCI;
|
|
812
|
+
for (bitLenInt i = 0U; i < numQubits; ++i) {
|
|
813
|
+
if (bi_compare_0(rev_sample & Qrack::pow2(i)) != 0) {
|
|
814
|
+
sample = sample | Qrack::pow2(numQubits - (i + 1U));
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
q_samples[sample] = 1;
|
|
818
|
+
} else {
|
|
819
|
+
std::vector<bitCapInt> qPowers(numQubits);
|
|
820
|
+
for (bitLenInt i = 0U; i < qPowers.size(); ++i) {
|
|
821
|
+
qPowers[i] = Qrack::pow2(i);
|
|
822
|
+
}
|
|
823
|
+
q_samples = qsim->MultiShotMeasureMask(qPowers, shots);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
std::iota(eigvals.begin(), eigvals.end(), 0);
|
|
827
|
+
std::fill(counts.begin(), counts.end(), 0);
|
|
828
|
+
|
|
829
|
+
_CountsBody(numQubits, q_samples, counts);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
void PartialCounts(DataView<double, 1> &eigvals, DataView<int64_t, 1> &counts,
|
|
833
|
+
const std::vector<QubitIdType> &wires) override
|
|
834
|
+
{
|
|
835
|
+
const size_t numQubits = wires.size();
|
|
836
|
+
const size_t numElements = 1U << numQubits;
|
|
837
|
+
|
|
838
|
+
RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements,
|
|
839
|
+
"Invalid size for the pre-allocated counts");
|
|
840
|
+
|
|
841
|
+
if ((shots > 1U) && (noise_param > ZERO_R1_F)) {
|
|
842
|
+
throw std::domain_error("Shots > 1 can't be simulated with noise on the Qrack back end! (Likely, you want to set mcm_method=\"one-shot\" on your qnode, with multiple shots.)");
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
auto &&dev_wires = getDeviceWires(wires);
|
|
846
|
+
|
|
847
|
+
std::map<bitCapInt, int> q_samples;
|
|
848
|
+
if (shots == 1U) {
|
|
849
|
+
const bitCapInt rev_sample = qsim->MAll();
|
|
850
|
+
const bitLenInt numQubits = dev_wires.size();
|
|
851
|
+
bitCapInt sample = Qrack::ZERO_BCI;
|
|
852
|
+
for (bitLenInt i = 0U; i < numQubits; ++i) {
|
|
853
|
+
if (bi_compare_0(rev_sample & Qrack::pow2(dev_wires[i])) != 0) {
|
|
854
|
+
sample = sample | Qrack::pow2(numQubits - (i + 1U));
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
q_samples[sample] = 1;
|
|
858
|
+
} else {
|
|
859
|
+
std::vector<bitCapInt> qPowers(dev_wires.size());
|
|
860
|
+
for (size_t i = 0U; i < qPowers.size(); ++i) {
|
|
861
|
+
qPowers[i] = Qrack::pow2(dev_wires[qPowers.size() - (i + 1U)]);
|
|
862
|
+
}
|
|
863
|
+
q_samples = qsim->MultiShotMeasureMask(qPowers, shots);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
std::iota(eigvals.begin(), eigvals.end(), 0);
|
|
867
|
+
std::fill(counts.begin(), counts.end(), 0);
|
|
868
|
+
|
|
869
|
+
_CountsBody(numQubits, q_samples, counts);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
void Gradient(std::vector<DataView<double, 1>> &, const std::vector<size_t> &) override {}
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
GENERATE_DEVICE_FACTORY(QrackDevice, QrackDevice);
|