Maestro 0.2.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
ExecutionCost.h
Go to the documentation of this file.
1
15
16#pragma once
17
18#ifndef __EXECUTION_COST_H_
19#define __EXECUTION_COST_H_
20
21#include "../Circuit/Factory.h"
24#include "../Utils/LogFile.h"
27
28#include <algorithm>
29#include <cstddef>
30#include <iomanip>
31
32namespace Estimators {
33
34// this is not for time estimation, it can be used for something very, very,
35// VERY rough, to decide just if it would execute in a reasonable time or not it
36// cannot be used to compare runtime for two simulators of the same circuit, but
37// it sort of can be used to compare two circuits in the same simulator
38// (especially if the same number of samples is used for both circuits) for
39// something better we have the estimators which got the constants from
40// benchmarking and rely on implementation details of the simulators, so they
41// are more accurate, but also less general
42
43// Except for pauli propagation, where the operation cost depends on the
44// position in the circuit (if the circuit is non-clifford), using a neural
45// network (or even a simpler regressor) to learn the relation between the
46// estimated cost and the actual execution time (single threaded, multithreading
47// rises some other issues) would be probably possible
49 public:
50 struct CircuitInfo {
51 size_t nrQubits = 0;
52 size_t nrOneQubitOps = 0;
53 size_t nrTwoQubitOps = 0;
54 size_t nrThreeQubitOps = 0;
60
61 CircuitInfo() = default;
62 CircuitInfo(const CircuitInfo& other) = default;
63
64 CircuitInfo& operator=(const CircuitInfo& other) = default;
65
66 double getFieldValue(size_t index) const {
67 switch (index) {
68 case 0:
69 return nrQubits;
70 case 1:
71 return nrOneQubitOps;
72 case 2:
73 return nrTwoQubitOps;
74 case 3:
75 return nrThreeQubitOps;
76 case 4:
78 case 5:
80 case 6:
82 case 7:
84 case 8:
86 default:
87 throw std::out_of_range("Invalid index for CircuitInfo field");
88 }
89 }
90 };
91
92 struct ExecutionInfo : public CircuitInfo {
93 size_t nrSamples = 0;
94 size_t nrQubitsSampled = 0;
95 size_t maxBondDim = 0;
96 size_t nrPauliOps = 0;
97 double executionCost = 0;
98 double runtime = 0;
99
100
101 ExecutionInfo() = default;
102 ExecutionInfo(const ExecutionInfo& other) = default;
103 ExecutionInfo(const CircuitInfo& circuitInfo) : CircuitInfo(circuitInfo) {}
104
105 ExecutionInfo& operator=(const ExecutionInfo& other) = default;
106
107 ExecutionInfo& operator=(const CircuitInfo& circuitInfo) {
108 CircuitInfo::operator=(circuitInfo);
109 return *this;
110 }
111
112 double getFieldValue(size_t index) const {
113 if (index < 9) {
114 return CircuitInfo::getFieldValue(index);
115 } else {
116 switch (index) {
117 case 9:
118 return nrSamples;
119 case 10:
120 return nrQubitsSampled;
121 case 11:
122 return maxBondDim;
123 case 12:
124 return nrPauliOps;
125 case 13:
126 return executionCost;
127 case 14:
128 return runtime;
129 default:
130 throw std::out_of_range("Invalid index for ExecutionInfo field");
131 }
132 }
133 }
134 };
135
137 Simulators::SimulationType method, size_t nrQubits,
138 const std::shared_ptr<Circuits::Circuit<>>& circuit, size_t maxBondDim) {
141 else if (method == Simulators::SimulationType::kStatevector) {
142 const double opOrder = exp2(nrQubits);
143
144 double cost = 0;
145 for (const auto& op : *circuit) {
146 const auto affectedQubits = op->AffectedQubits();
147 if (op->GetType() == Circuits::OperationType::kMeasurement ||
149 cost += opOrder * affectedQubits.size();
150 if (op->GetType() == Circuits::OperationType::kReset)
151 cost += 2. * opOrder * affectedQubits.size();
152 else if (op->GetType() == Circuits::OperationType::kGate ||
154 if (affectedQubits.size() == 1)
155 cost += opOrder;
156 else if (affectedQubits.size() == 2)
157 cost += 4 * opOrder;
158 else if (affectedQubits.size() == 3)
159 cost += 16 * opOrder;
160 }
161 }
162 return cost;
164 const double twoQubitOpOrder = pow(maxBondDim, 3);
165 const double oneQubitOpOrder = pow(maxBondDim, 2);
166
167 double cost = 0;
168 for (const auto& op : *circuit) {
169 const auto affectedQubits = op->AffectedQubits();
170 if (op->GetType() == Circuits::OperationType::kMeasurement ||
172 op->GetType() == Circuits::OperationType::kReset)
173 // it's not that simple at all
174 // a qubit measurement works by applying a projector on the qubit
175 // tensor, which would cost as a one quibit gate but then we need to
176 // propagate the effect of the measurement along the chain, left and
177 // right, which is like applying a two qubit gate (SVD is the costlier
178 // operation there) on all the qubits that are entangled with the
179 // measured one, and in the worst case this can be all the other
180 // qubits if more than one qubits are measured at the same time, then
181 // we can have some optimizations, but let's just say that it's like
182 // measuring them one by one, so the cost is multiplied by the number
183 // of measured qubits
184 cost += twoQubitOpOrder * affectedQubits.size() * nrQubits / 2.;
185 else if (op->GetType() == Circuits::OperationType::kGate ||
187 if (affectedQubits.size() == 1)
188 cost += oneQubitOpOrder;
189 else if (affectedQubits.size() == 2)
190 // I wish it were simple, but applying a gate involves swapping the
191 // qubits next to each other, then applying the gate
192 cost += twoQubitOpOrder * nrQubits / 3.;
193 else if (affectedQubits.size() == 3)
194 // qiskit aer has three qubit ops, qcsim has not, they are
195 // decomposed into one and two qubit gates
196 cost += twoQubitOpOrder * nrQubits * 2; // very rough estimation
197 }
198 }
199 return cost;
200 } else if (method == Simulators::SimulationType::kStabilizer) {
201 const double measOrder = pow(nrQubits, 2);
202 const double opOrder = static_cast<double>(nrQubits);
203
204 double cost = 0;
205 for (const auto& op : *circuit) {
206 const auto affectedQubits = op->AffectedQubits();
207 if (op->GetType() == Circuits::OperationType::kMeasurement ||
209 op->GetType() == Circuits::OperationType::kReset)
210 cost += measOrder * affectedQubits.size();
211 else if (op->GetType() == Circuits::OperationType::kGate ||
213 cost += opOrder * affectedQubits.size();
214 }
215 return cost;
216 } else if (method == Simulators::SimulationType::kPathIntegral) {
217 double cost = 0;
218 double doublingCost = 1;
219
220 for (const auto& op : *circuit) {
221 if (op->IsBranching()) doublingCost *= 2;
222
223 const auto affectedQubits = op->AffectedQubits();
224 if (op->GetType() == Circuits::OperationType::kMeasurement ||
226 op->GetType() == Circuits::OperationType::kReset)
227 cost += doublingCost * affectedQubits.size();
228 else if (op->GetType() == Circuits::OperationType::kGate ||
230 if (affectedQubits.size() == 1)
231 cost += doublingCost;
232 else if (affectedQubits.size() == 2)
233 cost += doublingCost * 2;
234 else if (affectedQubits.size() == 3)
235 cost += doublingCost * 4;
236 }
237 }
238
239 return cost;
240 }
241
242 // for tensor network is hard to guess, it depends on contraction path
243
244 return std::numeric_limits<double>::infinity();
245 }
246
247 static double EstimateSamplingCost(
248 Simulators::SimulationType method, size_t nrQubits,
249 size_t nrQubitsSampled, size_t samples,
250 const std::shared_ptr<Circuits::Circuit<>>& circuit, size_t maxBondDim) {
251 // sampling works very differenty for such circuits
252 Circuits::OperationState dummyState;
253 const bool hasMeasurementsInTheMiddle = circuit->HasOpsAfterMeasurements();
254 const std::vector<bool> executedOps =
255 circuit->ExecuteNonMeasurements(nullptr, dummyState);
256
257 const size_t dif = circuit->size() - executedOps.size();
258
261 circuit, nrQubitsSampled, samples);
262
264 const double opOrder = exp2(nrQubits);
265
266 double cost = 0;
267 for (size_t i = 0; i < circuit->size(); ++i) {
268 if (i >= dif && !executedOps[i - dif]) continue;
269
270 const auto& op = (*circuit)[i];
271 const auto affectedQubits = op->AffectedQubits();
272 if (op->GetType() == Circuits::OperationType::kMeasurement ||
274 cost += opOrder * affectedQubits.size();
275 if (op->GetType() == Circuits::OperationType::kReset)
276 cost += 2. * opOrder * affectedQubits.size();
277 else if (op->GetType() == Circuits::OperationType::kGate ||
279 if (affectedQubits.size() == 1)
280 cost += opOrder;
281 else if (affectedQubits.size() == 2)
282 cost += 4 * opOrder;
283 else if (affectedQubits.size() == 3)
284 cost += 16 * opOrder;
285 }
286 }
287
288 // the sampling cost depends on the simulator
289 // qiskit aer constucts an index gowing over the statevector, qcsim does
290 // something similar to have the samples then O(nrQubits), but in maestro
291 // for qcsim (and quest) this is replaced by alias sampling with O(1) for
292 // each sample
293
294 if (hasMeasurementsInTheMiddle) {
295 double samplingCost = 0;
296 for (size_t i = dif; i < circuit->size(); ++i) {
297 if (executedOps[i - dif]) continue;
298
299 const auto& op = (*circuit)[i];
300 const auto affectedQubits = op->AffectedQubits();
301 if (op->GetType() == Circuits::OperationType::kMeasurement ||
303 samplingCost += opOrder * affectedQubits.size();
304 if (op->GetType() == Circuits::OperationType::kReset)
305 samplingCost += 2. * opOrder * affectedQubits.size();
306 else if (op->GetType() == Circuits::OperationType::kGate ||
308 if (affectedQubits.size() == 1)
309 samplingCost += opOrder;
310 else if (affectedQubits.size() == 2)
311 samplingCost += 4 * opOrder;
312 else if (affectedQubits.size() == 3)
313 samplingCost += 16 * opOrder;
314 }
315 }
316
317 samplingCost += opOrder * nrQubitsSampled;
318
319 return cost + samplingCost * samples;
320 }
321
322 // some dummy cost, it's not going to fit all anyways
323 return cost + 30 * opOrder + samples * nrQubits;
325 const double oneQubitOpOrder = maxBondDim * maxBondDim;
326 const double twoQubitOpOrder = oneQubitOpOrder * maxBondDim;
327
328 double cost = 0;
329 for (size_t i = 0; i < circuit->size(); ++i) {
330 if (i >= dif && !executedOps[i - dif]) continue;
331
332 const auto& op = (*circuit)[i];
333 const auto affectedQubits = op->AffectedQubits();
334 if (op->GetType() == Circuits::OperationType::kMeasurement ||
336 op->GetType() == Circuits::OperationType::kReset)
337 // it's not that simple at all
338 // a qubit measurement works by applying a projector on the qubit
339 // tensor, which would cost as a one quibit gate but then we need to
340 // propagate the effect of the measurement along the chain, left and
341 // right, which is like applying a two qubit gate (SVD is the costlier
342 // operation there) on all the qubits that are entangled with the
343 // measured one, and in the worst case this can be all the other
344 // qubits if more than one qubits are measured at the same time, then
345 // we can have some optimizations, but let's just say that it's like
346 // measuring them one by one, so the cost is multiplied by the number
347 // of measured qubits
348 cost += twoQubitOpOrder * affectedQubits.size() * nrQubits / 2.;
349 else if (op->GetType() == Circuits::OperationType::kGate ||
351 if (affectedQubits.size() == 1)
352 cost += oneQubitOpOrder;
353 else if (affectedQubits.size() == 2)
354 // I wish it were simple, but applying a gate involves swapping the
355 // qubits next to each other, then applying the gate
356 cost += twoQubitOpOrder * nrQubits / 3.;
357 else if (affectedQubits.size() == 3)
358 // qiskit aer has three qubit ops, qcsim has not, they are
359 // decomposed into one and two qubit gates
360 cost += twoQubitOpOrder * nrQubits * 2; // very rough estimation
361 }
362 }
363
364 if (hasMeasurementsInTheMiddle) {
365 double samplingCost = 0;
366 for (size_t i = dif; i < circuit->size(); ++i) {
367 if (executedOps[i - dif]) continue;
368
369 const auto& op = (*circuit)[i];
370 const auto affectedQubits = op->AffectedQubits();
371 if (op->GetType() == Circuits::OperationType::kMeasurement ||
372 op->GetType() ==
374 op->GetType() == Circuits::OperationType::kReset)
375 samplingCost +=
376 twoQubitOpOrder * affectedQubits.size() * nrQubits / 2.;
377 else if (op->GetType() == Circuits::OperationType::kGate ||
379 if (affectedQubits.size() == 1)
380 samplingCost += oneQubitOpOrder;
381 else if (affectedQubits.size() == 2)
382 // I wish it were simple, but applying a gate involves swapping
383 // the qubits next to each other, then applying the gate
384 samplingCost += twoQubitOpOrder * nrQubits / 3.;
385 else if (affectedQubits.size() == 3)
386 // qiskit aer has three qubit ops, qcsim has not, they are
387 // decomposed into one and two qubit gates
388 samplingCost +=
389 twoQubitOpOrder * nrQubits * 2; // very rough estimation
390 }
391 }
392
393 samplingCost += twoQubitOpOrder * nrQubits * nrQubits;
394
395 return cost + samplingCost * samples;
396 }
397
398 // sampling can be done here (and for other simulator types as well)
399 // either by saving the state, measuring then restoring the state or
400 // simply going along the chain, computing probabilities, throwing biased
401 // coins and doing matrix multiplications (qcsim does this if all qubits
402 // are sampled, otherwise the measurement method is used)
403
404 // some dummy cost, it's not going to fit all anyways
405 return cost + samples * twoQubitOpOrder * nrQubits * nrQubits;
406 } else if (method == Simulators::SimulationType::kStabilizer) {
407 const double measOrder = pow(nrQubits, 2);
408 const double opOrder = static_cast<double>(nrQubits);
409
410 double cost = 0;
411 for (size_t i = 0; i < circuit->size(); ++i) {
412 if (i >= dif && !executedOps[i - dif]) continue;
413
414 const auto& op = (*circuit)[i];
415 const auto affectedQubits = op->AffectedQubits();
416 if (op->GetType() == Circuits::OperationType::kMeasurement ||
418 op->GetType() == Circuits::OperationType::kReset)
419 cost += measOrder * affectedQubits.size();
420 else if (op->GetType() == Circuits::OperationType::kGate ||
422 cost += opOrder * affectedQubits.size();
423 }
424
425 if (hasMeasurementsInTheMiddle) {
426 double samplingCost = 0;
427 for (size_t i = dif; i < circuit->size(); ++i) {
428 if (executedOps[i - dif]) continue;
429
430 const auto& op = (*circuit)[i];
431 const auto affectedQubits = op->AffectedQubits();
432 if (op->GetType() == Circuits::OperationType::kMeasurement ||
433 op->GetType() ==
435 op->GetType() == Circuits::OperationType::kReset)
436 samplingCost += measOrder * affectedQubits.size();
437 else if (op->GetType() == Circuits::OperationType::kGate ||
439 samplingCost += opOrder * affectedQubits.size();
440 }
441
442 samplingCost += measOrder * nrQubitsSampled;
443
444 return cost + samplingCost * samples;
445 }
446
447 // TODO: to be changed to handle the all qubits sampling case, also the
448 // sampling for qcsim which is efficient for sampling less than all
449 // qubits!
450
451 // sampling is done with saving state, measuring and restoring state
452 // the overhead for saving / restoring is not here (it's of the same order
453 // as measuring), but measurements are not all O(n^2) anyways
454 return cost + samples * measOrder * nrQubitsSampled;
455 } else if (method == Simulators::SimulationType::kPathIntegral) {
456 double cost = 0;
457 double doublingCost = 1;
458 for (size_t i = 0; i < circuit->size(); ++i) {
459 if (i >= dif && !executedOps[i - dif]) continue;
460 const auto& op = (*circuit)[i];
461 if (op->IsBranching()) doublingCost *= 2;
462
463 const auto affectedQubits = op->AffectedQubits();
464 if (op->GetType() == Circuits::OperationType::kMeasurement ||
466 op->GetType() == Circuits::OperationType::kReset)
467 cost += doublingCost * affectedQubits.size();
468 else if (op->GetType() == Circuits::OperationType::kGate ||
470 if (affectedQubits.size() == 1)
471 cost += doublingCost;
472 else if (affectedQubits.size() == 2)
473 cost += doublingCost * 2;
474 else if (affectedQubits.size() == 3)
475 cost += doublingCost * 4;
476 }
477 }
478
479 if (hasMeasurementsInTheMiddle) {
480 double samplingCost = 0;
481 for (size_t i = dif; i < circuit->size(); ++i) {
482 if (executedOps[i - dif]) continue;
483
484 const auto& op = (*circuit)[i];
485 if (op->IsBranching()) doublingCost *= 2;
486
487 const auto affectedQubits = op->AffectedQubits();
488
489 if (op->GetType() == Circuits::OperationType::kMeasurement ||
490 op->GetType() ==
492 op->GetType() == Circuits::OperationType::kReset)
493 samplingCost += doublingCost * affectedQubits.size();
494 else if (op->GetType() == Circuits::OperationType::kGate ||
496 if (affectedQubits.size() == 1)
497 samplingCost += doublingCost;
498 else if (affectedQubits.size() == 2)
499 samplingCost += doublingCost * 2;
500 else if (affectedQubits.size() == 3)
501 samplingCost += doublingCost * 4;
502 }
503 }
504
505 samplingCost += doublingCost * nrQubitsSampled;
506
507 return cost + samplingCost * samples;
508 }
509
510 // some dummy cost, it's not going to fit all anyways
511 return cost + 30 * doublingCost + samples * nrQubits;
512 }
513
514 // for tensor network is hard to guess, it depends on contraction path
515
516 return std::numeric_limits<double>::infinity();
517 }
518
520 const std::string& pauliString, Simulators::SimulationType method,
521 size_t nrQubits, const std::shared_ptr<Circuits::Circuit<>>& circuit,
522 size_t maxBondDim) {
523 // a pauli string propagated
526
527 double cost = EstimateExecutionCost(method, nrQubits, circuit, maxBondDim);
528
529 size_t pauliCnt = 0;
530 for (char c : pauliString) {
531 if (c == 'X' || c == 'x' || c == 'Y' || c == 'y' || c == 'Z' || c == 'z')
532 ++pauliCnt;
533 }
534
536 const double opOrder = exp2(nrQubits);
537
538 // each pauli matrix is applied to the statevector, then the product with
539 // the original is computed
540 cost += opOrder * (pauliCnt + 1);
541
542 return cost;
544 const double twoQubitOpOrder = pow(maxBondDim, 3);
545 const double oneQubitOpOrder = pow(maxBondDim, 2);
546
547 // each pauli is applied to the chain then the resulted chain is
548 // contracted with the original one
549 cost += oneQubitOpOrder * pauliCnt + nrQubits * twoQubitOpOrder;
550
551 return cost;
552 } else if (method == Simulators::SimulationType::kStabilizer) {
553 cost += nrQubits * nrQubits;
554 return cost;
555 } else if (method == Simulators::SimulationType::kPathIntegral) {
556 double doublingCost = 1;
557 for (const auto& op : *circuit) {
558 if (op->IsBranching()) doublingCost *= 2;
559 }
560 cost += 4 * doublingCost * pauliCnt;
561
562 return cost;
563 }
564
565 return std::numeric_limits<double>::infinity();
566 }
567
568 static std::shared_ptr<Circuits::Circuit<>> GenerateRandomCircuit(
569 size_t nrQubits, size_t depth, double measureInsideProbability = 0.,
570 size_t nrMeasAtEnd = 0, bool isClifford = false,
571 size_t nrNonCliffordGatesLimit = 0, size_t nrBranchingGatesLimit = 0) {
572 auto circuit = std::make_shared<Circuits::Circuit<>>();
573 std::random_device rdev;
574 std::mt19937 rng(rdev());
575 std::uniform_real_distribution<double> dist(0.0, 1.0);
576 std::uniform_real_distribution<double> paramDist(-2 * M_PI, 2 * M_PI);
577 std::uniform_int_distribution<Types::qubit_t> qubitDist(0, nrQubits - 1);
578 std::uniform_int_distribution<int> gateDist(
579 0, static_cast<int>(Circuits::QuantumGateType::kCCXGateType));
580
581 std::uniform_int_distribution<int> gateDistOneQubit(
582 0, static_cast<int>(Circuits::QuantumGateType::kUGateType));
583
584 std::vector<Types::qubit_t> qubits(nrQubits);
585 std::iota(qubits.begin(), qubits.end(), 0);
586
587 size_t nrNonCliffordGates = 0;
588 if (nrNonCliffordGatesLimit == 0 && !isClifford)
589 nrNonCliffordGatesLimit = depth;
590
591 size_t nrBranchingGates = 0;
592 if (nrBranchingGatesLimit == 0) nrBranchingGatesLimit = depth;
593
594 for (size_t i = 0; i < depth; ++i) {
595 if (dist(rng) < measureInsideProbability) {
596 Types::qubit_t q = qubitDist(rng);
597 std::vector<std::pair<Types::qubit_t, size_t>> qs = {{q, q}};
598 circuit->AddOperation(
599 std::make_shared<Circuits::MeasurementOperation<>>(qs));
600 continue;
601 }
602
603 std::shuffle(qubits.begin(), qubits.end(), rng);
604 const auto q1 = qubits[0];
605 const auto q2 = qubits[1];
606 const auto q3 = qubits[2];
607
608 auto gateType = static_cast<Circuits::QuantumGateType>(
609 nrNonCliffordGatesLimit < depth
610 ? gateDistOneQubit(rng) // avoid three qubit gates, they are
611 // non-clifford and they cost a lot
612 : gateDist(rng));
613 auto param1 = paramDist(rng);
614 auto param2 = paramDist(rng);
615 auto param3 = paramDist(rng);
616 auto param4 = paramDist(rng);
617
619 gateType, q1, q2, q3, param1, param2, param3, param4);
620 if (isClifford) {
621 while (!theGate->IsClifford()) {
622 gateType = static_cast<Circuits::QuantumGateType>(gateDist(rng));
624 gateType, q1, q2, q3, param1, param2, param3, param4);
625 }
626 }
627
628 if (!theGate->IsClifford()) ++nrNonCliffordGates;
629 if (theGate->IsBranching()) ++nrBranchingGates;
630
631 if (nrNonCliffordGates > nrNonCliffordGatesLimit) {
632 // replace the non clifford gate with a clifford one
633 gateType =
634 static_cast<Circuits::QuantumGateType>(gateDistOneQubit(rng));
636 gateType, q1, q2, q3, param1, param2, param3, param4);
637 while (!theGate->IsClifford()) {
638 gateType =
639 static_cast<Circuits::QuantumGateType>(gateDistOneQubit(rng));
641 gateType, q1, q2, q3, param1, param2, param3, param4);
642 }
643 --nrNonCliffordGates;
644 }
645
646 if (nrBranchingGates > nrBranchingGatesLimit) {
647 // replace the branching gate with a non branching one
648 gateType = static_cast<Circuits::QuantumGateType>(gateDist(rng));
649
651 gateType, q1, q2, q3, param1, param2, param3, param4);
652 while (theGate->IsBranching()) {
653 gateType = static_cast<Circuits::QuantumGateType>(gateDist(rng));
655 gateType, q1, q2, q3, param1, param2, param3, param4);
656 }
657 --nrBranchingGates;
658 }
659
660 circuit->AddOperation(theGate);
661 }
662
663 std::shuffle(circuit->begin(), circuit->end(), rng);
664
665 if (nrMeasAtEnd > 0) {
666 if (nrMeasAtEnd > nrQubits) nrMeasAtEnd = nrQubits;
667 std::shuffle(qubits.begin(), qubits.end(), rng);
668
669 for (size_t i = 0; i < nrMeasAtEnd; ++i) {
670 std::vector<std::pair<Types::qubit_t, size_t>> qs = {{qubits[i], i}};
671 circuit->AddOperation(
672 std::make_shared<Circuits::MeasurementOperation<>>(qs));
673 }
674 }
675
676 return circuit;
677 }
678
679 static double MeasureExecutionTime(
681 size_t nrQubits, const std::shared_ptr<Circuits::Circuit<>>& circuit,
682 size_t nrReps, size_t maxBondDim) {
683 auto sim = GetSimulator(simType, method, nrQubits, maxBondDim);
684
685 Circuits::OperationState dummyState(nrQubits);
686 auto start = std::chrono::high_resolution_clock::now();
687 for (size_t i = 0; i < nrReps; ++i) circuit->Execute(sim, dummyState);
688 auto end = std::chrono::high_resolution_clock::now();
689 return std::chrono::duration<double>(end - start).count();
690 }
691
692 static double MeasureSamplingTime(
694 size_t nrQubits, const std::shared_ptr<Circuits::Circuit<>>& circuit,
695 size_t nrQubitsSampled, size_t nrSamples, size_t nrReps,
696 size_t maxBondDim) {
697 auto sim = GetSimulator(simType, method, nrQubits, maxBondDim);
698 Circuits::OperationState dummyState(nrQubits);
699
700 if (nrQubitsSampled > nrQubits) nrQubitsSampled = nrQubits;
701 Types::qubits_vector qubitsSampled(nrQubitsSampled);
702 std::iota(qubitsSampled.begin(), qubitsSampled.end(), 0);
703
704 const bool hasMeasurementsInTheMiddle = circuit->HasOpsAfterMeasurements();
705
706 auto start = std::chrono::high_resolution_clock::now();
707
708 if (hasMeasurementsInTheMiddle) {
709 for (size_t i = 0; i < nrReps; ++i) {
710 const auto executedOps = circuit->ExecuteNonMeasurements(
711 sim, dummyState); // execute the circuit up to measurements
712
713 // now sample
714 for (size_t sample = 0; sample < nrSamples; ++sample) {
715 circuit->ExecuteMeasurements(
716 sim, dummyState, executedOps); // execute the measurements
717 }
718 }
719 } else {
720 for (size_t i = 0; i < nrReps; ++i) {
721 circuit->Execute(sim, dummyState); // execute the circuit first
722 sim->SampleCountsMany(qubitsSampled, nrSamples);
723 }
724 }
725 auto end = std::chrono::high_resolution_clock::now();
726
727 return std::chrono::duration<double>(end - start).count();
728 }
729
732 size_t nrQubits, const std::shared_ptr<Circuits::Circuit<>>& circuit,
733 const std::string& pauliString, size_t nrReps, size_t maxBondDim) {
734 auto sim = GetSimulator(simType, method, nrQubits, maxBondDim);
735 Circuits::OperationState dummyState(nrQubits);
736 auto start = std::chrono::high_resolution_clock::now();
737 for (size_t i = 0; i < nrReps; ++i) {
738 circuit->Execute(sim, dummyState); // execute the circuit first
739 sim->ExpectationValue(pauliString);
740 }
741 auto end = std::chrono::high_resolution_clock::now();
742 return std::chrono::duration<double>(end - start).count();
743 }
744
745 static std::shared_ptr<Simulators::ISimulator> GetSimulator(
747 size_t nrQubits, size_t maxBondDim) {
748 auto sim = Simulators::SimulatorsFactory::CreateSimulator(simType, method);
750 const auto strVal = std::to_string(maxBondDim);
751 sim->Configure("matrix_product_state_max_bond_dimension", strVal.c_str());
752 }
753 sim->AllocateQubits(nrQubits);
754 sim->SetMultithreading(false);
755 sim->Initialize();
756 return sim;
757 }
758
760 const std::shared_ptr<Circuits::Circuit<>>& circuit) {
761 CircuitInfo info;
762
763 info.nrQubits = circuit->GetMaxQubitIndex() + 1;
764 Circuits::OperationState dummyState(info.nrQubits);
765 const bool hasMeasurementsInTheMiddle = circuit->HasOpsAfterMeasurements();
766 const std::vector<bool> executedOps =
767 circuit->ExecuteNonMeasurements(nullptr, dummyState);
768
769 const size_t dif = circuit->size() - executedOps.size();
770
771 size_t i = 0;
772 for (const auto& op : *circuit) {
773 const auto affectedQubits = op->AffectedQubits();
774 if (op->GetType() == Circuits::OperationType::kMeasurement ||
776 if (hasMeasurementsInTheMiddle)
778 else
779 ++info.nrEndMeasurementOps;
780 } else if (op->GetType() == Circuits::OperationType::kGate ||
782 if (affectedQubits.size() == 1) {
783 ++info.nrOneQubitOps;
784 if (i < dif || executedOps[i - dif]) ++info.nrOneQubitOpsExecutedOnce;
785 } else if (affectedQubits.size() == 2) {
786 ++info.nrTwoQubitOps;
787 if (i < dif || executedOps[i - dif]) ++info.nrTwoQubitOpsExecutedOnce;
788 } else if (affectedQubits.size() == 3) {
789 ++info.nrThreeQubitOps;
790 if (i < dif || executedOps[i - dif])
792 }
793 }
794 ++i;
795 }
796
797 return info;
798 }
799
800 static std::vector<ExecutionInfo> ReadLog(const std::string& logFilePath) {
801 std::vector<ExecutionInfo> executionInfos;
802
803 std::ifstream logFile(logFilePath);
804 if (!logFile.is_open()) {
805 std::cerr << "Failed to open log file: " << logFilePath << std::endl;
806 return executionInfos;
807 }
808
809 std::string line;
810
811 while (std::getline(logFile, line)) {
812 std::stringstream ss(line);
813 std::string value;
814 ExecutionInfo info;
815 std::getline(ss, value, ',');
816 info.nrQubits = std::stoul(value);
817 std::getline(ss, value, ',');
818 info.nrOneQubitOps = std::stoul(value);
819 std::getline(ss, value, ',');
820 info.nrTwoQubitOps = std::stoul(value);
821 std::getline(ss, value, ',');
822 info.nrThreeQubitOps = std::stoul(value);
823 std::getline(ss, value, ',');
824 info.nrMiddleMeasurementOps = std::stoul(value);
825 std::getline(ss, value, ',');
826 info.nrEndMeasurementOps = std::stoul(value);
827 std::getline(ss, value, ',');
828 info.nrOneQubitOpsExecutedOnce = std::stoul(value);
829 std::getline(ss, value, ',');
830 info.nrTwoQubitOpsExecutedOnce = std::stoul(value);
831 std::getline(ss, value, ',');
832 info.nrThreeQubitOpsExecutedOnce = std::stoul(value);
833
834 std::getline(ss, value, ',');
835 info.nrSamples = std::stoul(value);
836 std::getline(ss, value, ',');
837 info.nrQubitsSampled = std::stoul(value);
838 std::getline(ss, value, ',');
839 info.maxBondDim = std::stoul(value);
840 std::getline(ss, value, ',');
841 info.nrPauliOps = std::stoul(value);
842
843 std::getline(ss, value, ',');
844 info.executionCost = std::stod(value);
845 std::getline(ss, value, ',');
846 info.runtime = std::stod(value);
847
848 if (info.nrSamples < 1) info.nrSamples = 1;
849
850 executionInfos.push_back(std::move(info));
851 }
852
853 std::random_device rdev;
854 std::mt19937 rng(rdev());
855 std::shuffle(executionInfos.begin(), executionInfos.end(), rng);
856
857 return executionInfos;
858 }
859
862 size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits,
863 size_t depthMin, size_t depthMax, size_t stepDepth,
864 double measureInsideProbability, size_t nrMeasAtEndMin,
865 size_t nrMeasAtEndMax, size_t stepMeasAtEnd,
866 size_t nrRandomCircuitsPerConfig, const std::string& logFilePath,
867 size_t startBondDim = 16, size_t endBondDim = 16) {
868 bool isClifford = (method == Simulators::SimulationType::kStabilizer);
869 int nrNonCliffordGates = static_cast<int>(depthMax); // a limit
870 int nrBranchingGates = static_cast<int>(depthMax);
871
872 // but if it's pauli...
874 nrNonCliffordGates = 1;
875 } else if (method == Simulators::SimulationType::kStabilizer) {
876 nrNonCliffordGates = 0;
877 } else if (method == Simulators::SimulationType::kPathIntegral) {
878 nrBranchingGates = 8;
879 }
880
881 std::cout << "Benchmarking execution for simType: "
882 << static_cast<int>(simType)
883 << ", method: " << static_cast<int>(method) << std::endl;
884
885 Utils::LogFile log(logFilePath);
886
887 for (size_t nrQubits = nrMinQubits; nrQubits <= nrMaxQubits;
888 nrQubits += stepQubits) {
889 std::cout << " Qubits: " << nrQubits << std::endl;
890 for (size_t depth = depthMin; depth <= depthMax; depth += stepDepth) {
891 std::cout << " Depth: " << depth << std::endl;
892 for (size_t nrMeasAtEnd = nrMeasAtEndMin;
893 nrMeasAtEnd <= std::min(nrMeasAtEndMax, nrQubits);
894 nrMeasAtEnd += stepMeasAtEnd) {
895 std::cout << " Measurements at end: " << nrMeasAtEnd
896 << std::endl;
897 for (size_t i = 0; i < nrRandomCircuitsPerConfig; ++i) {
898 std::cout << " Random circuit: " << i + 1 << "/"
899 << nrRandomCircuitsPerConfig << std::endl;
901 isClifford = !isClifford;
902
903 const auto circuit = GenerateRandomCircuit(
904 nrQubits, depth, measureInsideProbability, nrMeasAtEnd,
905 isClifford, nrNonCliffordGates, nrBranchingGates);
906
907 for (size_t maxBondDim = startBondDim; maxBondDim <= endBondDim;
908 maxBondDim *= 2) {
909 BenchmarkAndLogExecution(simType, method, circuit, nrReps,
910 maxBondDim, log);
911 }
912 }
913 }
914 }
915 }
916 }
917
918 static std::string GeneratePauliString(size_t nrQubits) {
919 std::string pauli;
920 pauli.resize(nrQubits);
921 std::random_device rd;
922 std::mt19937 g(rd());
923 std::uniform_int_distribution<int> dist(0, 3);
924
925 for (size_t i = 0; i < nrQubits; ++i) {
926 const int v = dist(g);
927 switch (v) {
928 case 0:
929 pauli[i] = 'I';
930 break;
931 case 1:
932 pauli[i] = 'X';
933 break;
934 case 2:
935 pauli[i] = 'Y';
936 break;
937 case 3:
938 pauli[i] = 'Z';
939 break;
940 }
941 }
942
943 return pauli;
944 }
945
948 size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits,
949 size_t depthMin, size_t depthMax, size_t stepDepth,
950 size_t nrRandomCircuitsPerConfig,
951 const std::string& logFilePath, size_t startBondDim = 16, size_t endBondDim = 16) {
952 bool isClifford = (method == Simulators::SimulationType::kStabilizer);
953 int nrNonCliffordGates = static_cast<int>(depthMax); // a limit
954 int nrBranchingGates = static_cast<int>(depthMax);
955
956 // but if it's pauli...
958 nrNonCliffordGates = 1;
959 } else if (method == Simulators::SimulationType::kStabilizer) {
960 nrNonCliffordGates = 0;
961 } else if (method == Simulators::SimulationType::kPathIntegral) {
962 nrBranchingGates = 8;
963 }
964 Utils::LogFile log(logFilePath);
965
966 std::cout << "Benchmarking Pauli expectation for simType: "
967 << static_cast<int>(simType)
968 << ", method: " << static_cast<int>(method) << std::endl;
969
970 for (size_t nrQubits = nrMinQubits; nrQubits <= nrMaxQubits;
971 nrQubits += stepQubits) {
972 std::cout << " Qubits: " << nrQubits << std::endl;
973 for (size_t depth = depthMin; depth <= depthMax; depth += stepDepth) {
974 std::cout << " Depth: " << depth << std::endl;
975 for (size_t i = 0; i < nrRandomCircuitsPerConfig; ++i) {
977 isClifford = !isClifford;
978
979 const auto circuit =
980 GenerateRandomCircuit(nrQubits, depth, 0., 0, isClifford,
981 nrNonCliffordGates, nrBranchingGates);
982 const std::string pauliString = GeneratePauliString(nrQubits);
983
984 std::cout << " Random circuit: " << i + 1 << "/"
985 << nrRandomCircuitsPerConfig
986 << " Pauli string: " << pauliString << std::endl;
987 for (size_t maxBondDim = startBondDim; maxBondDim <= endBondDim;
988 maxBondDim *= 2) {
989 std::cout << " Max bond dimension: " << maxBondDim
990 << std::endl;
992 simType, method, circuit, pauliString, nrReps, maxBondDim, log);
993 }
994 }
995 }
996 }
997 }
998
1001 size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits,
1002 size_t depthMin, size_t depthMax, size_t stepDepth, size_t nrMeasAtEndMin,
1003 size_t nrMeasAtEndMax, size_t stepMeasAtEnd, size_t nrSamplesMin,
1004 size_t nrSamplesMax, size_t multiplierSamples,
1005 size_t nrRandomCircuitsPerConfig, const std::string& logFilePath,
1006 size_t startBondDim = 16, size_t endBondDim = 16) {
1007 bool isClifford = (method == Simulators::SimulationType::kStabilizer);
1008 int nrNonCliffordGates = static_cast<int>(depthMax); // a limit
1009 int nrBranchingGates = static_cast<int>(depthMax);
1010 // but if it's pauli...
1012 nrNonCliffordGates = 1;
1013 } else if (method == Simulators::SimulationType::kStabilizer) {
1014 nrNonCliffordGates = 0;
1015 } else if (method == Simulators::SimulationType::kPathIntegral) {
1016 nrBranchingGates = 8;
1017 }
1018 Utils::LogFile log(logFilePath);
1019
1020 std::cout << "Benchmarking sampling for simType: "
1021 << static_cast<int>(simType)
1022 << ", method: " << static_cast<int>(method) << std::endl;
1023
1024 for (size_t nrQubits = nrMinQubits; nrQubits <= nrMaxQubits;
1025 nrQubits += stepQubits) {
1026 std::cout << " Qubits: " << nrQubits << std::endl;
1027 for (size_t depth = depthMin; depth <= depthMax; depth += stepDepth) {
1028 std::cout << " Depth: " << depth << std::endl;
1029 for (size_t nrSamples = nrSamplesMin; nrSamples <= nrSamplesMax;
1030 nrSamples *= multiplierSamples) {
1031 std::cout << " Samples: " << nrSamples << std::endl;
1032 for (size_t i = 0; i < nrRandomCircuitsPerConfig; ++i) {
1034 isClifford = !isClifford;
1035 const auto circuit =
1036 GenerateRandomCircuit(nrQubits, depth, 0., 0, isClifford,
1037 nrNonCliffordGates, nrBranchingGates);
1038 for (size_t nrQubitsSampled = nrQubits; nrQubitsSampled >= 1;
1039 nrQubitsSampled /= 2) {
1040 std::cout << " Random circuit: " << i + 1 << "/"
1041 << nrRandomCircuitsPerConfig
1042 << " Qubits sampled: " << nrQubitsSampled << std::endl;
1043 for (size_t maxBondDim = startBondDim; maxBondDim <= endBondDim;
1044 maxBondDim *= 2) {
1045 BenchmarkAndLogSampling(simType, method, circuit,
1046 nrQubitsSampled, nrSamples, nrReps,
1047 maxBondDim, log);
1048 }
1049 }
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1058 const std::shared_ptr<Circuits::Circuit<>>& circuit, size_t nrReps,
1059 size_t maxBondDim, Utils::LogFile& log) {
1060 const auto info = GetCircuitInfo(circuit);
1061 const double estimatedCost =
1062 EstimateExecutionCost(method, info.nrQubits, circuit, maxBondDim);
1063 const double executionTime =
1064 MeasureExecutionTime(simType, method, info.nrQubits, circuit, nrReps,
1065 maxBondDim) /
1066 nrReps;
1067
1068 std::stringstream ss;
1069
1070 ss << info.nrQubits << "," << info.nrOneQubitOps << ","
1071 << info.nrTwoQubitOps << "," << info.nrThreeQubitOps << ","
1072 << info.nrMiddleMeasurementOps << "," << info.nrEndMeasurementOps << ","
1073 << info.nrOneQubitOpsExecutedOnce << ","
1074 << info.nrTwoQubitOpsExecutedOnce << ","
1075 << info.nrThreeQubitOpsExecutedOnce << ","
1076 << "0,0," << maxBondDim << ","
1077 << "0," << estimatedCost << "," << executionTime;
1078
1079 log.Log(ss.str());
1080 }
1081
1084 const std::shared_ptr<Circuits::Circuit<>>& circuit,
1085 size_t nrQubitsSampled, size_t nrSamples, size_t nrReps,
1086 size_t maxBondDim, Utils::LogFile& log) {
1087 const auto info = GetCircuitInfo(circuit);
1088 const double estimatedCost = EstimateSamplingCost(
1089 method, info.nrQubits, nrQubitsSampled, nrSamples, circuit, maxBondDim);
1090 const double samplingTime =
1091 MeasureSamplingTime(simType, method, info.nrQubits, circuit,
1092 nrQubitsSampled, nrSamples, nrReps, maxBondDim) /
1093 nrReps;
1094 std::stringstream ss;
1095 ss << info.nrQubits << "," << info.nrOneQubitOps << ","
1096 << info.nrTwoQubitOps << "," << info.nrThreeQubitOps << ","
1097 << info.nrMiddleMeasurementOps << "," << info.nrEndMeasurementOps << ","
1098 << info.nrOneQubitOpsExecutedOnce << ","
1099 << info.nrTwoQubitOpsExecutedOnce << ","
1100 << info.nrThreeQubitOpsExecutedOnce << "," << nrSamples << ","
1101 << nrQubitsSampled << "," << maxBondDim << ","
1102 << "0," << estimatedCost << "," << samplingTime;
1103 log.Log(ss.str());
1104 }
1105
1108 const std::shared_ptr<Circuits::Circuit<>>& circuit,
1109 const std::string& pauliString, size_t nrReps, size_t maxBondDim,
1110 Utils::LogFile& log) {
1111 auto info = GetCircuitInfo(circuit);
1112 info.nrQubits = std::max(info.nrQubits, pauliString.size());
1113 const double estimatedCost = EstimatePauliExpectationCost(
1114 pauliString, method, info.nrQubits, circuit, maxBondDim);
1115 const double expectationTime =
1116 MeasurePauliExpectationTime(simType, method, info.nrQubits, circuit,
1117 pauliString, nrReps, maxBondDim) /
1118 nrReps;
1119
1120 size_t cntPauli = 0;
1121 for (char c : pauliString) {
1122 if (c == 'X' || c == 'x' || c == 'Y' || c == 'y' || c == 'Z' || c == 'z')
1123 ++cntPauli;
1124 }
1125
1126 std::stringstream ss;
1127 ss << info.nrQubits << "," << info.nrOneQubitOps << ","
1128 << info.nrTwoQubitOps << "," << info.nrThreeQubitOps << ","
1129 << info.nrMiddleMeasurementOps << "," << info.nrEndMeasurementOps << ","
1130 << info.nrOneQubitOpsExecutedOnce << ","
1131 << info.nrTwoQubitOpsExecutedOnce << ","
1132 << info.nrThreeQubitOpsExecutedOnce << ","
1133 << "0,0," << maxBondDim << "," << cntPauli << "," << estimatedCost << ","
1134 << expectationTime;
1135 log.Log(ss.str());
1136 }
1137
1138 static std::shared_ptr<Utils::MultipleLinearRegression> GetRegressor(
1139 const std::string& logFilePath,
1140 const std::vector<size_t>& featureIndices) {
1141 const auto executionInfos = ReadLog(logFilePath);
1142
1143 std::vector<std::vector<double>> features;
1144 features.reserve(executionInfos.size());
1145
1146 std::vector<double> targetValues;
1147 targetValues.reserve(executionInfos.size());
1148
1149 for (const auto& info : executionInfos) {
1150 std::vector<double> featureVector(featureIndices.size());
1151 for (size_t i = 0; i < featureVector.size(); ++i)
1152 featureVector[i] = info.getFieldValue(featureIndices[i]);
1153 features.push_back(std::move(featureVector));
1154
1155 targetValues.push_back(info.runtime);
1156 }
1157
1158 auto regressor = std::make_shared<Utils::MultipleLinearRegression>();
1159 regressor->SetSamples(features, targetValues);
1160
1161 return regressor;
1162 }
1163};
1164
1165} // namespace Estimators
1166
1167#endif
static const std::shared_ptr< IQuantumGate< Time > > CreateGate(QuantumGateType type, size_t q1, size_t q2=0, size_t q3=0, double param1=0, double param2=0, double param3=0, double param4=0)
Construct a quantum gate.
Circuit class for holding the sequence of operations.
Definition Circuit.h:46
Measurement operation class.
The state class that stores the classical state of a quantum circuit execution.
Definition Operations.h:63
static std::shared_ptr< Circuits::Circuit<> > GenerateRandomCircuit(size_t nrQubits, size_t depth, double measureInsideProbability=0., size_t nrMeasAtEnd=0, bool isClifford=false, size_t nrNonCliffordGatesLimit=0, size_t nrBranchingGatesLimit=0)
static double MeasureSamplingTime(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrQubits, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t nrQubitsSampled, size_t nrSamples, size_t nrReps, size_t maxBondDim)
static void BenchmarkAndLogPauliExpectation(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits, size_t depthMin, size_t depthMax, size_t stepDepth, size_t nrRandomCircuitsPerConfig, const std::string &logFilePath, size_t startBondDim=16, size_t endBondDim=16)
static double MeasureExecutionTime(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrQubits, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t nrReps, size_t maxBondDim)
static void BenchmarkAndLogPauliExpectation(Simulators::SimulatorType simType, Simulators::SimulationType method, const std::shared_ptr< Circuits::Circuit<> > &circuit, const std::string &pauliString, size_t nrReps, size_t maxBondDim, Utils::LogFile &log)
static void BenchmarkAndLogSampling(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits, size_t depthMin, size_t depthMax, size_t stepDepth, size_t nrMeasAtEndMin, size_t nrMeasAtEndMax, size_t stepMeasAtEnd, size_t nrSamplesMin, size_t nrSamplesMax, size_t multiplierSamples, size_t nrRandomCircuitsPerConfig, const std::string &logFilePath, size_t startBondDim=16, size_t endBondDim=16)
static double MeasurePauliExpectationTime(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrQubits, const std::shared_ptr< Circuits::Circuit<> > &circuit, const std::string &pauliString, size_t nrReps, size_t maxBondDim)
static std::shared_ptr< Utils::MultipleLinearRegression > GetRegressor(const std::string &logFilePath, const std::vector< size_t > &featureIndices)
static std::shared_ptr< Simulators::ISimulator > GetSimulator(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrQubits, size_t maxBondDim)
static double EstimatePauliExpectationCost(const std::string &pauliString, Simulators::SimulationType method, size_t nrQubits, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t maxBondDim)
static void BenchmarkAndLogSampling(Simulators::SimulatorType simType, Simulators::SimulationType method, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t nrQubitsSampled, size_t nrSamples, size_t nrReps, size_t maxBondDim, Utils::LogFile &log)
static double EstimateSamplingCost(Simulators::SimulationType method, size_t nrQubits, size_t nrQubitsSampled, size_t samples, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t maxBondDim)
static std::string GeneratePauliString(size_t nrQubits)
static double EstimateExecutionCost(Simulators::SimulationType method, size_t nrQubits, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t maxBondDim)
static CircuitInfo GetCircuitInfo(const std::shared_ptr< Circuits::Circuit<> > &circuit)
static void BenchmarkAndLogExecution(Simulators::SimulatorType simType, Simulators::SimulationType method, const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t nrReps, size_t maxBondDim, Utils::LogFile &log)
static std::vector< ExecutionInfo > ReadLog(const std::string &logFilePath)
static void BenchmarkAndLogExecution(Simulators::SimulatorType simType, Simulators::SimulationType method, size_t nrReps, size_t nrMinQubits, size_t nrMaxQubits, size_t stepQubits, size_t depthMin, size_t depthMax, size_t stepDepth, double measureInsideProbability, size_t nrMeasAtEndMin, size_t nrMeasAtEndMax, size_t stepMeasAtEnd, size_t nrRandomCircuitsPerConfig, const std::string &logFilePath, size_t startBondDim=16, size_t endBondDim=16)
static double GetSamplingCost(const std::shared_ptr< Circuits::Circuit<> > &circuit, size_t nrQubitsSampled, size_t samples)
static double GetCost(const std::shared_ptr< Circuits::Circuit<> > &circuit)
static std::shared_ptr< ISimulator > CreateSimulator(SimulatorType t=SimulatorType::kQCSim, SimulationType method=SimulationType::kMatrixProductState)
Create a quantum computing simulator.
Definition Factory.cpp:101
void Log(const std::string &message)
Definition LogFile.h:30
QuantumGateType
The type of quantum gates.
@ kConditionalGate
conditional gate, similar with gate, but conditioned on something from 'OperationState'
Definition Operations.h:31
@ kConditionalMeasurement
conditional measurement, similar with measurement, but conditioned on something from 'OperationState'
Definition Operations.h:33
@ kMeasurement
measurement, result in 'OperationState'
Definition Operations.h:29
@ kGate
the usual quantum gate, result stays in simulator's state
Definition Operations.h:28
@ kReset
reset, no result in 'state', just apply measurement, then apply not on all qubits that were measured ...
Definition Operations.h:39
SimulationType
The type of simulation.
Definition State.h:85
@ kStatevector
statevector simulation type
Definition State.h:86
@ kMatrixProductState
matrix product state simulation type
Definition State.h:87
@ kStabilizer
Clifford gates simulation type.
Definition State.h:88
@ kPauliPropagator
Pauli propagator simulation type.
Definition State.h:90
@ kPathIntegral
Path integral simulation type.
Definition State.h:92
SimulatorType
The type of simulator.
Definition State.h:68
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:22
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:21
CircuitInfo(const CircuitInfo &other)=default
CircuitInfo & operator=(const CircuitInfo &other)=default
double getFieldValue(size_t index) const
ExecutionInfo(const CircuitInfo &circuitInfo)
ExecutionInfo & operator=(const ExecutionInfo &other)=default
double getFieldValue(size_t index) const
ExecutionInfo(const ExecutionInfo &other)=default
ExecutionInfo & operator=(const CircuitInfo &circuitInfo)