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