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