Maestro 0.2.11
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
AerState.h
Go to the documentation of this file.
1
12
13#pragma once
14
15#ifndef _AER_STATE_H_
16#define _AER_STATE_H_
17
18#ifndef NO_QISKIT_AER
19
20#ifdef INCLUDED_BY_FACTORY
21
22#include <algorithm>
23#include <iomanip>
24#include <limits>
25#include <sstream>
26
27#include "QubitRegister.h"
28#include "Simulator.h"
29
30#include "QiskitAerState.h"
31
32namespace Simulators {
33// TODO: Maybe use the pimpl idiom
34// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
35// for good but during development this should be good enough
36namespace Private {
37
38class IndividualSimulator;
39
50class AerState : public ISimulator {
51 friend class IndividualSimulator;
53 public:
60 AerState() {
61 std::random_device rd;
62
63 rng.seed(rd());
64 Configure("method", "statevector");
65 }
66
74 void Initialize() override {
75 SetMultithreading(enableMultithreading);
76 if (simulationType == SimulationType::kMatrixProductState)
77 Configure("mps_sample_measure_algorithm", useMPSMeasureNoCollapse
78 ? "mps_probabilities"
79 : "mps_apply_measure");
80 state->initialize();
81 }
82
94 void InitializeState(size_t num_qubits,
95 std::vector<std::complex<double>> &amplitudes) override {
96 Clear();
97 state->initialize_statevector(num_qubits, amplitudes.data(), true);
98 }
99
111 /*
112 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
113 avoid_init_allocator<std::complex<double>>>& amplitudes) override
114 {
115 Clear();
116 state->initialize_statevector(num_qubits, amplitudes.data(), true);
117 }
118 */
119
131 void InitializeState(size_t num_qubits,
132 AER::Vector<std::complex<double>> &amplitudes) override {
133 Clear();
134 state->initialize_statevector(num_qubits, amplitudes.move_to_buffer(),
135 false);
136 }
137
149 void InitializeState(size_t num_qubits,
150 Eigen::VectorXcd &amplitudes) override {
151 Clear();
152 state->initialize_statevector(num_qubits, amplitudes.data(), true);
153 }
154
161 void Reset() override {
162 const auto numQubits = GetNumberOfQubits();
163 Clear();
164 AllocateQubits(numQubits);
165 state->initialize();
166 }
167
176 void Configure(const char *key, const char *value) override {
177 if (std::string("method") == key) {
178 if (std::string("statevector") == value)
179 simulationType = SimulationType::kStatevector;
180 else if (std::string("matrix_product_state") == value)
181 simulationType = SimulationType::kMatrixProductState;
182 else if (std::string("stabilizer") == value)
183 simulationType = SimulationType::kStabilizer;
184 else if (std::string("tensor_network") == value)
185 simulationType = SimulationType::kTensorNetwork;
186 else if (std::string("extended_stabilizer") == value)
187 simulationType = SimulationType::kExtendedStabilizer;
188 else
189 simulationType = SimulationType::kOther;
190 } else if (std::string("matrix_product_state_truncation_threshold") ==
191 key) {
192 singularValueThreshold = std::stod(value);
193 if (singularValueThreshold > 0.)
194 limitEntanglement = true;
195 else
196 limitEntanglement = false;
197 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
198 chi = std::stoi(value);
199 if (chi > 0)
200 limitSize = true;
201 else
202 limitSize = false;
203 } else if (std::string("mps_sample_measure_algorithm") == key)
204 useMPSMeasureNoCollapse = std::string("mps_probabilities") == value;
205
206 state->configure(key, value);
207 }
208
216 std::string GetConfiguration(const char *key) const override {
217 if (std::string("method") == key) {
218 switch (simulationType) {
219 case SimulationType::kStatevector:
220 return "statevector";
221 case SimulationType::kMatrixProductState:
222 return "matrix_product_state";
223 case SimulationType::kStabilizer:
224 return "stabilizer";
225 case SimulationType::kTensorNetwork:
226 return "tensor_network";
227 case SimulationType::kExtendedStabilizer:
228 return "extended_stabilizer";
229 default:
230 return "other";
231 }
232 } else if (std::string("matrix_product_state_truncation_threshold") ==
233 key) {
234 if (limitEntanglement && singularValueThreshold > 0.) {
235 std::ostringstream oss;
236 oss << std::setprecision(std::numeric_limits<double>::max_digits10)
237 << singularValueThreshold;
238 return oss.str();
239 }
240 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
241 if (limitSize && limitSize > 0) return std::to_string(chi);
242 } else if (std::string("mps_sample_measure_algorithm") == key) {
243 return useMPSMeasureNoCollapse ? "mps_probabilities"
244 : "mps_apply_measure";
245 }
246
247 return "";
248 }
249
257 size_t AllocateQubits(size_t num_qubits) override {
258 const auto ids = state->allocate_qubits(num_qubits);
259 return ids[0];
260 }
261
268 size_t GetNumberOfQubits() const override {
269 if (state->is_initialized()) return state->num_of_qubits();
270
271 return 0;
272 }
273
281 void Clear() override {
282 state->clear();
283 SetMultithreading(enableMultithreading);
284
285 if (simulationType == SimulationType::kMatrixProductState)
286 Configure("mps_sample_measure_algorithm", useMPSMeasureNoCollapse
287 ? "mps_probabilities"
288 : "mps_apply_measure");
289 }
290
301 size_t Measure(const Types::qubits_vector &qubits) override {
302 if (qubits.size() > sizeof(size_t) * 8)
303 std::cerr
304 << "Warning: The number of qubits to measure is larger than the "
305 "number of bits in the size_t type, the outcome will be undefined"
306 << std::endl;
307
308 const size_t res = state->apply_measure(qubits);
309
310 NotifyObservers(qubits);
311
312 return res;
313 }
314
321 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
322 auto res = state->apply_measure_many(qubits);
323
324 NotifyObservers(qubits);
325
326 return res;
327 }
328
335 void ApplyReset(const Types::qubits_vector &qubits) override {
336 state->apply_reset(qubits);
337
338 NotifyObservers(qubits);
339 }
340
352 double Probability(Types::qubit_t outcome) override {
353 return state->probability(outcome);
354 }
355
366 std::complex<double> Amplitude(Types::qubit_t outcome) override {
367 return state->amplitude(outcome);
368 }
369
383 std::complex<double> ProjectOnZero() override {
384 return Amplitude(0);
385 }
386
387
398 std::vector<double> AllProbabilities() override {
399 return state->probabilities();
400 }
401
413 std::vector<double> Probabilities(
414 const Types::qubits_vector &qubits) override {
415 return state->probabilities(qubits);
416 }
417
434 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
435 const Types::qubits_vector &qubits, size_t shots = 1000) override {
436 if (qubits.empty() || shots == 0) return {};
437
438 if (qubits.size() > sizeof(Types::qubit_t) * 8)
439 std::cerr
440 << "Warning: The number of qubits to measure is larger than the "
441 "number of bits in the Types::qubit_t type, the outcome will be "
442 "undefined"
443 << std::endl;
444
445 std::unordered_map<Types::qubit_t, Types::qubit_t> res =
446 state->sample_counts(qubits, shots);
447
448 NotifyObservers(qubits);
449
450 return res;
451 }
452
466 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
467 const Types::qubits_vector &qubits, size_t shots = 1000) override {
468 if (qubits.empty() || shots == 0) return {};
469 std::unordered_map<std::vector<bool>, Types::qubit_t> res =
470 state->sample_counts_many(qubits, shots);
471 NotifyObservers(qubits);
472 return res;
473 }
474
486 double ExpectationValue(const std::string &pauliStringOrig) override {
487 if (pauliStringOrig.empty()) return 1.0;
488
489 std::string pauliString = pauliStringOrig;
490 if (pauliString.size() > GetNumberOfQubits()) {
491 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i) {
492 const auto pauliOp = toupper(pauliString[i]);
493 if (pauliOp != 'I' && pauliOp != 'Z') return 0.0;
494 }
495
496 pauliString.resize(GetNumberOfQubits());
497 }
498
499 AER::reg_t qubits;
500 std::string pauli;
501
502 pauli.reserve(pauliString.size());
503 qubits.reserve(pauliString.size());
504
505 for (size_t q = 0; q < pauliString.size(); ++q) {
506 const char p = toupper(pauliString[q]);
507 if (p == 'I') continue;
508
509 pauli.push_back(p);
510 qubits.push_back(q);
511 }
512
513 if (qubits.empty()) return 1.0;
514
515 // qiskit aer expects the pauli string in reverse order
516 std::reverse(pauli.begin(), pauli.end());
517
518 return state->expval_pauli(qubits, pauli);
519 }
520
528 SimulatorType GetType() const override { return SimulatorType::kQiskitAer; }
529
538 SimulationType GetSimulationType() const override { return simulationType; }
539
548 void Flush() override {
549 state->flush_ops();
550 // state->set_random_seed(); // avoid reusing the old seed
551 }
552
562 void SaveStateToInternalDestructive() override {
563 savedAmplitudes = state->move_to_vector();
564 }
565
573 const size_t numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
574 state->initialize_statevector(numQubits, savedAmplitudes.move_to_buffer(),
575 false);
576 }
577
586 void SaveState() override {
587 if (!state) return;
588
589 const auto numQubits = GetNumberOfQubits();
590
591 if (simulationType == SimulationType::kStatevector) {
593 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
594 return;
595 }
596
597 bool saved = false;
598
599 if (state->is_initialized()) {
600 AER::Operations::Op op;
601
602 op.type = AER::Operations::OpType::save_state;
603 op.name = "save_state";
604 op.save_type = AER::Operations::DataSubType::single;
605 op.string_params.push_back("s");
606
607 for (size_t q = 0; q < numQubits; ++q) op.qubits.push_back(q);
608
609 state->buffer_op(std::move(op));
610 Flush();
611
612 // get the state from the last result
613 AER::ExperimentResult &last_result = state->last_result();
614 // state should be in last_result.data
615 if (last_result.status == AER::ExperimentResult::Status::completed) {
616 savedState = std::move(last_result.data);
617 saved = true;
618 }
619 } else {
620 // try get the state from the last result
621 AER::ExperimentResult &last_result_prev = state->last_result();
622 // state should be in last_result.data
623 if (last_result_prev.status == AER::ExperimentResult::Status::completed) {
624 savedState = std::move(last_result_prev.data);
625 saved = true;
626 }
627 }
628
629 // this is a hack, for statevector and matrix product state if the last op
630 // is executed, it can destroy the state! see also the workaround for
631 // statevector for the stabilizer at least for now it doesn't seem to do
632 // that
633 // TODO: check everything!!!!
634 if (saved && simulationType == SimulationType::kMatrixProductState)
635 RestoreState();
636 }
637
646 void RestoreState() override {
647 auto numQubits = GetNumberOfQubits();
648
649 AER::Operations::Op op;
650
651 switch (simulationType) {
652 case SimulationType::kStatevector: {
653 // op.type = AER::Operations::OpType::set_statevec;
654 // op.name = "set_statevec";
655
656 // const auto& vec = static_cast<AER::DataMap<AER::SingleData,
657 // AER::Vector<complex_t>>>(savedState).value()["s"].value();
658
659 // this is a hack until I figure it out
660 Clear();
661 numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
662 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
663
664 return;
665 } break;
666 case SimulationType::kMatrixProductState:
667 op.type = AER::Operations::OpType::set_mps;
668 op.name = "set_mps";
669 op.mps =
670 static_cast<AER::DataMap<AER::SingleData, AER::mps_container_t>>(
671 savedState)
672 .value()["s"]
673 .value();
674
675 /*
676 {
677 auto& value = static_cast<AER::DataMap<AER::SingleData,
678 AER::mps_container_t>>(savedState).value(); if (!value.empty())
679 {
680 if (value.find("s") != value.end())
681 op.mps = value["s"].value();
682 else if (value.find("matrix_product_state") !=
683 value.end()) op.mps = value["matrix_product_state"].value();
684 }
685 }
686 */
687
688 numQubits = op.mps.first.size();
689 break;
690 case SimulationType::kStabilizer:
691 op.type = AER::Operations::OpType::set_stabilizer;
692 op.name = "set_stabilizer";
693 op.clifford =
694 static_cast<AER::DataMap<AER::SingleData, json_t>>(savedState)
695 .value()["s"]
696 .value();
697
698 /*
699 {
700 auto& value = static_cast<AER::DataMap<AER::SingleData,
701 json_t>>(savedState).value(); if (!value.empty())
702 {
703 if (value.find("s") != value.end())
704 op.clifford = value["s"].value();
705 else if (value.find("stabilizer") != value.end())
706 op.clifford = value["stabilizer"].value();
707 }
708 }
709 */
710
711 numQubits = op.clifford.num_qubits();
712 break;
713 case SimulationType::kTensorNetwork:
714 default:
715 throw std::runtime_error(
716 "AerState::RestoreState: not implemented yet "
717 "for this type of simulator.");
718 }
719
720 op.save_type = AER::Operations::DataSubType::single;
721 op.string_params.push_back("s");
722
723 for (size_t q = 0; q < numQubits; ++q) op.qubits.push_back(q);
724
725 // WHY?
726 if (!state->is_initialized()) {
727 Clear();
728 AllocateQubits(numQubits);
729 state->initialize();
730 }
731
732 state->buffer_op(std::move(op));
733 Flush();
734 }
735
743 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
744 return savedAmplitudes[outcome];
745 }
746
755 void SetMultithreading(bool multithreading = true) override {
756 enableMultithreading = multithreading;
757 if (state && !state->is_initialized()) {
758 const std::string nrThreads =
759 std::to_string(enableMultithreading
760 ? 0
761 : 1); // 0 means auto/all available, 1 limits to 1
762 state->configure("max_parallel_threads", nrThreads);
763 state->configure("parallel_state_update", nrThreads);
764 const std::string threadsLimit =
765 std::to_string(12); // set one less, multithreading is started if the
766 // value is bigger than this
767 state->configure("statevector_parallel_threshold", threadsLimit);
768 }
769 }
770
778 bool GetMultithreading() const override { return enableMultithreading; }
779
790 bool IsQcsim() const override { return false; }
791
809 if (simulationType == SimulationType::kStatevector) {
810 const double prob =
811 1. - uniformZeroOne(rng); // this excludes 0 as probabiliy
812 double accum = 0;
813 Types::qubit_t state = 0;
814 for (Types::qubit_t i = 0; i < savedAmplitudes.size(); ++i) {
815 accum += std::norm(savedAmplitudes[i]);
816 if (prob <= accum) {
817 state = i;
818 break;
819 }
820 }
821
822 return state;
823 }
824
825 throw std::runtime_error(
826 "AerState::MeasureNoCollapse: Invalid simulation type for measuring "
827 "all the qubits without collapsing the state.");
828
829 return 0;
830 }
831
846 std::vector<bool> MeasureNoCollapseMany() override {
847 if (simulationType == SimulationType::kStatevector) {
848 const size_t numQubits =
849 static_cast<size_t>(log2(savedAmplitudes.size()));
850 std::vector<bool> res(numQubits, false);
851
853
854 for (size_t i = 0; i < numQubits; ++i) {
855 if ((state & 1) == 1) res[i] = true;
856 state >>= 1;
857 }
858
859 return res;
860 }
861 throw std::runtime_error(
862 "AerState::MeasureNoCollapseMany: Invalid simulation type for "
863 "measuring "
864 "all the qubits without collapsing the state.");
865
866 return {};
867 }
868
869 protected:
870 SimulationType simulationType =
871 SimulationType::kStatevector;
872 std::unique_ptr<QiskitAerState> state =
873 std::make_unique<QiskitAerState>();
874 AER::Vector<complex_t> savedAmplitudes;
875 bool limitSize = false;
876 bool limitEntanglement = false;
877 Eigen::Index chi = 10; // if limitSize is true
878 double singularValueThreshold = 0.; // if limitEntanglement is true
879 bool enableMultithreading = true;
880 AER::Data savedState;
882 std::mt19937_64 rng;
883 std::uniform_real_distribution<double> uniformZeroOne{0., 1.};
884 bool useMPSMeasureNoCollapse =
885 true;
886};
887
888} // namespace Private
889} // namespace Simulators
890
891#endif
892
893#endif
894
895#endif // !_AER_STATE_H_
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
unsigned long int GetNumberOfQubits(void *sim)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int GetMultithreading(void *sim)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
double * Amplitude(void *sim, unsigned long long int outcome)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int SaveStateToInternalDestructive(void *sim)
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 RestoreInternalDestructiveSavedState(void *sim)
int IsQcsim(void *sim)
int SaveState(void *sim)
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