Maestro 0.3.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
QCSimPauliPropagatorSimulator.h
Go to the documentation of this file.
1#pragma once
2#if defined(INCLUDED_BY_FACTORY)
5#define _USE_MATH_DEFINES
6#include <math.h>
7
9{
10// pauli_propagator backend. Owns exactly one native QCSim implementation.
11class QCSimPauliPropagatorSimulator : public ImmediateQCSimSimulator
12{
13 public:
14 QCSimPauliPropagatorSimulator()
15 {
16 configuration.SetConfiguration("method", "pauli_propagator");
17 }
18
19 const char *MethodName() const override
20 {
21 return "pauli_propagator";
22 }
23
24 void Initialize() override
25 {
26 if (nrQubits != 0)
27 {
28 {
29 pp = std::make_unique<Simulators::QcsimPauliPropagator>();
30 pp->SetNrQubits(static_cast<int>(nrQubits));
31 }
32 SetMultithreading(enableMultithreading);
33 for (const auto &[key, value] : configuration.GetConfigMap())
34 if (key != "method")
35 Configure(key.c_str(), value.c_str());
36 }
37 }
38
39 void Reset() override
40 {
41 if (pp)
42 pp->ClearOperations();
43 upcomingGateIndex = 0;
44 }
45
46 void Configure(const char *key, const char *value) override
47 {
48 if (std::string("method") == key)
49 {
50 ValidateMethod(value);
51 configuration.SetConfiguration(key, value);
52 return;
53 }
54 if (!configuration.WasApplied(key, value))
55 configuration.SetConfiguration(key, value);
56 if (std::string("seed") == key)
57 {
58 const uint64_t seed = std::stoull(value);
59 SeedAuxiliaryRng(seed);
60 nextSeedStream = 0;
61 rng.seed(seed);
62 if (pp)
63 SeedBackend(pp.get(), seed);
64 return;
65 }
66 if (std::string(key) == "pauli_propagator_workers")
67 {
68 const auto workers = configuration.GetConfigurationAsUnsigned(key);
69 if (workers > 1024)
70 throw std::invalid_argument("pauli_propagator_workers exceeds 1024");
71 pauliWorkerCount = static_cast<size_t>(workers);
72 if (pp && enableMultithreading)
73 pp->EnableParallel(pauliWorkerCount);
74 }
75 if (pp)
76 {
77 if (std::string(key) == "pauli_propagator_sampling_cache_nodes")
78 {
79 pp->SetSamplingCacheMaxNodes(configuration.GetConfigurationAsUnsigned(key));
80 }
81 else if (std::string(key) == "pauli_propagator_coefficient_threshold")
82 {
83 pp->SetCoefficientThreshold(configuration.GetConfigurationAsDouble(key));
84 }
85 else if (std::string(key) == "pauli_propagator_pauli_weight_threshold")
86 {
87 pp->SetPauliWeightThreshold(configuration.GetConfigurationAsUnsigned(key));
88 }
89 else if (std::string(key) == "pauli_propagator_steps_between_trims")
90 {
91 pp->SetStepsBetweenTrims(configuration.GetConfigurationAsInt(key));
92 }
93 else if (std::string(key) == "pauli_propagator_num_gates_between_deduplications")
94 {
95 pp->SetStepsBetweenDeduplication(configuration.GetConfigurationAsInt(key));
96 }
97 }
98 }
99
100 size_t AllocateQubits(size_t num_qubits) override
101 {
102 const size_t oldNrQubits = nrQubits;
103 nrQubits += num_qubits;
104 if (pp)
105 pp->SetNrQubits(static_cast<int>(nrQubits));
106 return oldNrQubits;
107 }
108
109 void Clear() override
110 {
111 pp = nullptr;
112 nrQubits = 0;
113 upcomingGateIndex = 0;
114 upcomingGates.clear();
115 }
116
117 size_t Measure(const Types::qubits_vector &qubits) override
118 {
119 if (qubits.size() > sizeof(size_t) * 8)
120 std::cerr << "Warning: The number of qubits to measure is larger than the "
121 "number of bits in the size_t type, the outcome will be undefined"
122 << std::endl;
123 size_t mask = 1ULL;
124 DontNotify();
125 {
126 std::vector<int> qubitsInt;
127 qubitsInt.reserve(qubits.size());
128 for (const auto q : qubits)
129 qubitsInt.push_back(static_cast<int>(q));
130 const auto res = pp->Measure(qubitsInt);
131 Types::qubit_t result = 0;
132 for (size_t i = 0; i < res.size(); ++i)
133 {
134 if (res[i])
135 result |= mask;
136 mask <<= 1;
137 }
138 return result;
139 }
140 }
141
142 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
143 {
144 std::vector<bool> res(qubits.size(), false);
145 DontNotify();
146 {
147 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
148 res = pp->Measure(qubitsInt);
149 }
150 Notify();
151 NotifyObservers(qubits);
152 return res;
153 }
154
155 void ApplyReset(const Types::qubits_vector &qubits) override
156 {
157 DontNotify();
158 {
159 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
160 const auto res = pp->Measure(qubitsInt);
161 for (size_t i = 0; i < res.size(); ++i)
162 {
163 if (res[i])
164 pp->ApplyX(qubitsInt[i]);
165 }
166 }
167 Notify();
168 NotifyObservers(qubits);
169 }
170
171 double Probability(Types::qubit_t outcome) override
172 {
173 return pp->Probability(outcome);
174 }
175
176 std::vector<double> AllProbabilities() override
177 {
178 const size_t nrBasisStates = 1ULL << GetNumberOfQubits();
179 std::vector<double> result(nrBasisStates);
180 for (size_t i = 0; i < nrBasisStates; ++i)
181 result[i] = pp->Probability(i);
182 return result;
183 }
184
185 std::vector<double> Probabilities(const Types::qubits_vector &qubits) override
186 {
187 std::vector<double> result(qubits.size());
188 {
189 for (int i = 0; i < static_cast<int>(qubits.size()); ++i)
190 result[i] = pp->Probability(qubits[i]);
191 }
192 return result;
193 }
194
195 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
196 {
197 if (qubits.empty() || shots == 0)
198 return {};
199 if (qubits.size() > sizeof(size_t) * 8)
200 std::cerr << "Warning: The number of qubits to measure is larger than the "
201 "number of bits in the size_t type, the outcome will be undefined"
202 << std::endl;
203 std::unordered_map<Types::qubit_t, Types::qubit_t> result;
204 DontNotify();
205 {
206 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
207 if (qubits.size() > 64)
208 throw std::invalid_argument("Use SampleCountsMany for more than 64 measured qubits");
209 const auto counts = pp->SampleCounts(qubitsInt, shots);
210 for (const auto &[bits, count] : counts)
211 {
212 size_t meas = 0;
213 for (size_t i = 0; i < bits.size(); ++i)
214 if (bits[i])
215 meas |= (1ULL << i);
216 result[meas] += count;
217 }
218 }
219 Notify();
220 NotifyObservers(qubits);
221 return result;
222 }
223
224 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
225 {
226 if (qubits.empty() || shots == 0)
227 return {};
228 std::unordered_map<std::vector<bool>, Types::qubit_t> result;
229 DontNotify();
230 {
231 std::vector<int> qubitsInt(qubits.begin(), qubits.end());
232 const auto counts = pp->SampleCounts(qubitsInt, shots);
233 for (const auto &[bits, count] : counts)
234 result[bits] += count;
235 }
236 Notify();
237 NotifyObservers(qubits);
238 return result;
239 }
240
241 double ExpectationValue(const std::string &pauliStringOrig) override
242 {
243 if (pauliStringOrig.empty())
244 return 1.0;
245 std::string pauliString = pauliStringOrig;
246 if (pauliString.size() > GetNumberOfQubits())
247 {
248 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i)
249 {
250 const auto pauliOp = toupper(pauliString[i]);
251 if (pauliOp != 'I' && pauliOp != 'Z')
252 return 0.0;
253 }
254 pauliString.resize(GetNumberOfQubits());
255 }
256 return pp->ExpectationValue(pauliString);
257 }
258
259 SimulationType GetSimulationType() const override
260 {
261 return SimulationType::kPauliPropagator;
262 }
263
264 void SaveState() override
265 {
266 pp->SaveState();
267 }
268
269 void RestoreState() override
270 {
271 pp->RestoreState();
272 }
273
274 void SetMultithreading(bool multithreading = true) override
275 {
276 enableMultithreading = multithreading;
277 if (pp)
278 {
279 if (multithreading)
280 pp->EnableParallel(pauliWorkerCount);
281 else
282 pp->DisableParallel();
283 }
284 }
285
287 {
288 if (GetNumberOfQubits() > sizeof(Types::qubit_t) * 8)
289 std::cerr << "Warning: The number of qubits to measure is larger than the "
290 "number of bits in the Types::qubit_t type, the outcome will be "
291 "undefined"
292 << std::endl;
293 {
294 std::vector<int> qubitsInt(GetNumberOfQubits());
295 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
296 const auto res = pp->Sample(qubitsInt);
297 Types::qubit_t result = 0;
298 for (size_t i = 0; i < res.size(); ++i)
299 {
300 if (res[i])
301 result |= (1ULL << i);
302 }
303 return result;
304 }
305 }
306
307 std::vector<bool> MeasureNoCollapseMany() override
308 {
309 std::vector<int> qubitsInt(GetNumberOfQubits());
310 std::iota(qubitsInt.begin(), qubitsInt.end(), 0);
311 return pp->Sample(qubitsInt);
312 }
313
314 void ApplyP(Types::qubit_t qubit, double lambda) override
315 {
316 pp->ApplyP(static_cast<unsigned int>(qubit), lambda);
317 NotifyObservers({qubit});
318 }
319
320 void ApplyX(Types::qubit_t qubit) override
321 {
322 pp->ApplyX(static_cast<unsigned int>(qubit));
323 NotifyObservers({qubit});
324 }
325
326 void ApplyY(Types::qubit_t qubit) override
327 {
328 pp->ApplyY(static_cast<unsigned int>(qubit));
329 NotifyObservers({qubit});
330 }
331
332 void ApplyZ(Types::qubit_t qubit) override
333 {
334 pp->ApplyZ(static_cast<unsigned int>(qubit));
335 NotifyObservers({qubit});
336 }
337
338 void ApplyH(Types::qubit_t qubit) override
339 {
340 pp->ApplyH(static_cast<unsigned int>(qubit));
341 NotifyObservers({qubit});
342 }
343
344 void ApplyS(Types::qubit_t qubit) override
345 {
346 pp->ApplyS(static_cast<unsigned int>(qubit));
347 NotifyObservers({qubit});
348 }
349
350 void ApplySDG(Types::qubit_t qubit) override
351 {
352 pp->ApplySDG(static_cast<unsigned int>(qubit));
353 NotifyObservers({qubit});
354 }
355
356 void ApplyT(Types::qubit_t qubit) override
357 {
358 pp->ApplyT(static_cast<unsigned int>(qubit));
359 NotifyObservers({qubit});
360 }
361
362 void ApplyTDG(Types::qubit_t qubit) override
363 {
364 pp->ApplyTDG(static_cast<unsigned int>(qubit));
365 NotifyObservers({qubit});
366 }
367
368 void ApplySx(Types::qubit_t qubit) override
369 {
370 pp->ApplySX(static_cast<unsigned int>(qubit));
371 NotifyObservers({qubit});
372 }
373
374 void ApplySxDAG(Types::qubit_t qubit) override
375 {
376 pp->ApplySXDG(static_cast<unsigned int>(qubit));
377 NotifyObservers({qubit});
378 }
379
380 void ApplyK(Types::qubit_t qubit) override
381 {
382 pp->ApplyK(static_cast<unsigned int>(qubit));
383 NotifyObservers({qubit});
384 }
385
386 void ApplyRx(Types::qubit_t qubit, double theta) override
387 {
388 pp->ApplyRX(static_cast<unsigned int>(qubit), theta);
389 NotifyObservers({qubit});
390 }
391
392 void ApplyRy(Types::qubit_t qubit, double theta) override
393 {
394 pp->ApplyRY(static_cast<unsigned int>(qubit), theta);
395 NotifyObservers({qubit});
396 }
397
398 void ApplyRz(Types::qubit_t qubit, double theta) override
399 {
400 pp->ApplyRZ(static_cast<unsigned int>(qubit), theta);
401 NotifyObservers({qubit});
402 }
403
404 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda, double gamma) override
405 {
406 pp->ApplyU(static_cast<unsigned int>(qubit), theta, phi, lambda, gamma);
407 NotifyObservers({qubit});
408 }
409
410 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
411 {
412 pp->ApplyCX(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
413 NotifyObservers({tgt_qubit, ctrl_qubit});
414 }
415
416 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
417 {
418 pp->ApplyCY(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
419 NotifyObservers({tgt_qubit, ctrl_qubit});
420 }
421
422 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
423 {
424 pp->ApplyCZ(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
425 NotifyObservers({tgt_qubit, ctrl_qubit});
426 }
427
428 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double lambda) override
429 {
430 pp->ApplyCP(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit), lambda);
431 NotifyObservers({tgt_qubit, ctrl_qubit});
432 }
433
434 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
435 {
436 pp->ApplyCRX(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit), theta);
437 NotifyObservers({tgt_qubit, ctrl_qubit});
438 }
439
440 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
441 {
442 pp->ApplyCRY(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit), theta);
443 NotifyObservers({tgt_qubit, ctrl_qubit});
444 }
445
446 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta) override
447 {
448 pp->ApplyCRZ(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit), theta);
449 NotifyObservers({tgt_qubit, ctrl_qubit});
450 }
451
452 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
453 {
454 pp->ApplyCH(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
455 NotifyObservers({tgt_qubit, ctrl_qubit});
456 }
457
458 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
459 {
460 pp->ApplyCSX(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
461 NotifyObservers({tgt_qubit, ctrl_qubit});
462 }
463
464 void ApplyCSxDAG(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override
465 {
466 pp->ApplyCSXDAG(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit));
467 NotifyObservers({tgt_qubit, ctrl_qubit});
468 }
469
470 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override
471 {
472 pp->ApplySWAP(static_cast<unsigned int>(qubit0), static_cast<unsigned int>(qubit1));
473 NotifyObservers({qubit1, qubit0});
474 }
475
476 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1, Types::qubit_t qubit2) override
477 {
478 pp->ApplyCCX(static_cast<unsigned int>(qubit0), static_cast<unsigned int>(qubit1), static_cast<unsigned int>(qubit2));
479 NotifyObservers({qubit2, qubit1, qubit0});
480 }
481
482 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0, Types::qubit_t qubit1) override
483 {
484 pp->ApplyCSwap(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(qubit0), static_cast<unsigned int>(qubit1));
485 NotifyObservers({qubit1, qubit0, ctrl_qubit});
486 }
487
488 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit, double theta, double phi, double lambda, double gamma) override
489 {
490 pp->ApplyCU(static_cast<unsigned int>(ctrl_qubit), static_cast<unsigned int>(tgt_qubit), theta, phi, lambda, gamma);
491 NotifyObservers({tgt_qubit, ctrl_qubit});
492 }
493
494 std::unique_ptr<ISimulator> Clone() override
495 {
496 auto cloned = std::make_unique<QCSimPauliPropagatorSimulator>();
497 cloned->nrQubits = nrQubits;
498 cloned->enableMultithreading = enableMultithreading;
499 cloned->pauliWorkerCount = pauliWorkerCount;
500 cloned->upcomingGates = upcomingGates;
501 cloned->upcomingGateIndex = upcomingGateIndex;
502 if (pp)
503 cloned->pp = pp->Clone();
504 for (const auto &[key, value] : configuration.GetConfigMap())
505 cloned->Configure(key.c_str(), value.c_str());
506 if (configuration.IsSet("seed"))
507 cloned->SetSeed(DeriveSeed(std::stoull(configuration.GetConfiguration("seed")), nextSeedStream++));
508 return cloned;
509 }
510
511 protected:
512 std::unique_ptr<QcsimPauliPropagator> pp;
513
514 size_t pauliWorkerCount = 0;
515};
516} // namespace Simulators::Private
517#endif
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
unsigned long int GetNumberOfQubits(void *sim)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
int GetSimulationType(void *sim)
unsigned long long int * SampleCounts(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits, unsigned long int shots)
int ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int SaveState(void *sim)
SimulationType
The type of simulation.
Definition State.h:109
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:24
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:23