Maestro 0.2.5
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
AerState.h
Go to the documentation of this file.
1
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
24#include "QubitRegister.h"
25#include "Simulator.h"
26
27#include "QiskitAerState.h"
28
29namespace Simulators {
30// TODO: Maybe use the pimpl idiom
31// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
32// for good but during development this should be good enough
33namespace Private {
34
35class IndividualSimulator;
36
47class AerState : public ISimulator {
48 friend class IndividualSimulator;
50 public:
57 AerState() {
58 std::random_device rd;
59
60 rng.seed(rd());
61 Configure("method", "statevector");
62 }
63
71 void Initialize() override {
72 SetMultithreading(enableMultithreading);
73 if (simulationType == SimulationType::kMatrixProductState)
74 Configure("mps_sample_measure_algorithm", useMPSMeasureNoCollapse
75 ? "mps_probabilities"
76 : "mps_apply_measure");
77 state->initialize();
78 }
79
91 void InitializeState(size_t num_qubits,
92 std::vector<std::complex<double>> &amplitudes) override {
93 Clear();
94 state->initialize_statevector(num_qubits, amplitudes.data(), true);
95 }
96
108 /*
109 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
110 avoid_init_allocator<std::complex<double>>>& amplitudes) override
111 {
112 Clear();
113 state->initialize_statevector(num_qubits, amplitudes.data(), true);
114 }
115 */
116
128 void InitializeState(size_t num_qubits,
129 AER::Vector<std::complex<double>> &amplitudes) override {
130 Clear();
131 state->initialize_statevector(num_qubits, amplitudes.move_to_buffer(),
132 false);
133 }
134
146 void InitializeState(size_t num_qubits,
147 Eigen::VectorXcd &amplitudes) override {
148 Clear();
149 state->initialize_statevector(num_qubits, amplitudes.data(), true);
150 }
151
158 void Reset() override {
159 const auto numQubits = GetNumberOfQubits();
160 Clear();
161 AllocateQubits(numQubits);
162 state->initialize();
163 }
164
173 void Configure(const char *key, const char *value) override {
174 if (std::string("method") == key) {
175 if (std::string("statevector") == value)
176 simulationType = SimulationType::kStatevector;
177 else if (std::string("matrix_product_state") == value)
178 simulationType = SimulationType::kMatrixProductState;
179 else if (std::string("stabilizer") == value)
180 simulationType = SimulationType::kStabilizer;
181 else if (std::string("tensor_network") == value)
182 simulationType = SimulationType::kTensorNetwork;
183 else if (std::string("extended_stabilizer") == value)
184 simulationType = SimulationType::kExtendedStabilizer;
185 else
186 simulationType = SimulationType::kOther;
187 } else if (std::string("matrix_product_state_truncation_threshold") ==
188 key) {
189 singularValueThreshold = std::stod(value);
190 if (singularValueThreshold > 0.)
191 limitEntanglement = true;
192 else
193 limitEntanglement = false;
194 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
195 chi = std::stoi(value);
196 if (chi > 0)
197 limitSize = true;
198 else
199 limitSize = false;
200 } else if (std::string("mps_sample_measure_algorithm") == key)
201 useMPSMeasureNoCollapse = std::string("mps_probabilities") == value;
202
203 state->configure(key, value);
204 }
205
213 std::string GetConfiguration(const char *key) const override {
214 if (std::string("method") == key) {
215 switch (simulationType) {
216 case SimulationType::kStatevector:
217 return "statevector";
218 case SimulationType::kMatrixProductState:
219 return "matrix_product_state";
220 case SimulationType::kStabilizer:
221 return "stabilizer";
222 case SimulationType::kTensorNetwork:
223 return "tensor_network";
224 case SimulationType::kExtendedStabilizer:
225 return "extended_stabilizer";
226 default:
227 return "other";
228 }
229 } else if (std::string("matrix_product_state_truncation_threshold") ==
230 key) {
231 if (limitEntanglement && singularValueThreshold > 0.)
232 return std::to_string(singularValueThreshold);
233 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
234 if (limitSize && limitSize > 0) return std::to_string(chi);
235 } else if (std::string("mps_sample_measure_algorithm") == key) {
236 return useMPSMeasureNoCollapse ? "mps_probabilities"
237 : "mps_apply_measure";
238 }
239
240 return "";
241 }
242
250 size_t AllocateQubits(size_t num_qubits) override {
251 const auto ids = state->allocate_qubits(num_qubits);
252 return ids[0];
253 }
254
261 size_t GetNumberOfQubits() const override {
262 if (state->is_initialized()) return state->num_of_qubits();
263
264 return 0;
265 }
266
274 void Clear() override {
275 state->clear();
276 SetMultithreading(enableMultithreading);
277
278 if (simulationType == SimulationType::kMatrixProductState)
279 Configure("mps_sample_measure_algorithm", useMPSMeasureNoCollapse
280 ? "mps_probabilities"
281 : "mps_apply_measure");
282 }
283
294 size_t Measure(const Types::qubits_vector &qubits) override {
295 if (qubits.size() > sizeof(size_t) * 8)
296 std::cerr
297 << "Warning: The number of qubits to measure is larger than the "
298 "number of bits in the size_t type, the outcome will be undefined"
299 << std::endl;
300
301 const size_t res = state->apply_measure(qubits);
302
303 NotifyObservers(qubits);
304
305 return res;
306 }
307
314 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
315 auto res = state->apply_measure_many(qubits);
316
317 NotifyObservers(qubits);
318
319 return res;
320 }
321
328 void ApplyReset(const Types::qubits_vector &qubits) override {
329 state->apply_reset(qubits);
330
331 NotifyObservers(qubits);
332 }
333
345 double Probability(Types::qubit_t outcome) override {
346 return state->probability(outcome);
347 }
348
359 std::complex<double> Amplitude(Types::qubit_t outcome) override {
360 return state->amplitude(outcome);
361 }
362
376 std::complex<double> ProjectOnZero() override {
377 return Amplitude(0);
378 }
379
380
391 std::vector<double> AllProbabilities() override {
392 return state->probabilities();
393 }
394
406 std::vector<double> Probabilities(
407 const Types::qubits_vector &qubits) override {
408 return state->probabilities(qubits);
409 }
410
427 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
428 const Types::qubits_vector &qubits, size_t shots = 1000) override {
429 if (qubits.empty() || shots == 0) return {};
430
431 if (qubits.size() > sizeof(Types::qubit_t) * 8)
432 std::cerr
433 << "Warning: The number of qubits to measure is larger than the "
434 "number of bits in the Types::qubit_t type, the outcome will be "
435 "undefined"
436 << std::endl;
437
438 std::unordered_map<Types::qubit_t, Types::qubit_t> res =
439 state->sample_counts(qubits, shots);
440
441 NotifyObservers(qubits);
442
443 return res;
444 }
445
459 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
460 const Types::qubits_vector &qubits, size_t shots = 1000) override {
461 if (qubits.empty() || shots == 0) return {};
462 std::unordered_map<std::vector<bool>, Types::qubit_t> res =
463 state->sample_counts_many(qubits, shots);
464 NotifyObservers(qubits);
465 return res;
466 }
467
479 double ExpectationValue(const std::string &pauliStringOrig) override {
480 if (pauliStringOrig.empty()) return 1.0;
481
482 std::string pauliString = pauliStringOrig;
483 if (pauliString.size() > GetNumberOfQubits()) {
484 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i) {
485 const auto pauliOp = toupper(pauliString[i]);
486 if (pauliOp != 'I' && pauliOp != 'Z') return 0.0;
487 }
488
489 pauliString.resize(GetNumberOfQubits());
490 }
491
492 AER::reg_t qubits;
493 std::string pauli;
494
495 pauli.reserve(pauliString.size());
496 qubits.reserve(pauliString.size());
497
498 for (size_t q = 0; q < pauliString.size(); ++q) {
499 const char p = toupper(pauliString[q]);
500 if (p == 'I') continue;
501
502 pauli.push_back(p);
503 qubits.push_back(q);
504 }
505
506 if (qubits.empty()) return 1.0;
507
508 // qiskit aer expects the pauli string in reverse order
509 std::reverse(pauli.begin(), pauli.end());
510
511 return state->expval_pauli(qubits, pauli);
512 }
513
521 SimulatorType GetType() const override { return SimulatorType::kQiskitAer; }
522
531 SimulationType GetSimulationType() const override { return simulationType; }
532
541 void Flush() override {
542 state->flush_ops();
543 // state->set_random_seed(); // avoid reusing the old seed
544 }
545
555 void SaveStateToInternalDestructive() override {
556 savedAmplitudes = state->move_to_vector();
557 }
558
566 const size_t numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
567 state->initialize_statevector(numQubits, savedAmplitudes.move_to_buffer(),
568 false);
569 }
570
579 void SaveState() override {
580 if (!state) return;
581
582 const auto numQubits = GetNumberOfQubits();
583
584 if (simulationType == SimulationType::kStatevector) {
586 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
587 return;
588 }
589
590 bool saved = false;
591
592 if (state->is_initialized()) {
593 AER::Operations::Op op;
594
595 op.type = AER::Operations::OpType::save_state;
596 op.name = "save_state";
597 op.save_type = AER::Operations::DataSubType::single;
598 op.string_params.push_back("s");
599
600 for (size_t q = 0; q < numQubits; ++q) op.qubits.push_back(q);
601
602 state->buffer_op(std::move(op));
603 Flush();
604
605 // get the state from the last result
606 AER::ExperimentResult &last_result = state->last_result();
607 // state should be in last_result.data
608 if (last_result.status == AER::ExperimentResult::Status::completed) {
609 savedState = std::move(last_result.data);
610 saved = true;
611 }
612 } else {
613 // try get the state from the last result
614 AER::ExperimentResult &last_result_prev = state->last_result();
615 // state should be in last_result.data
616 if (last_result_prev.status == AER::ExperimentResult::Status::completed) {
617 savedState = std::move(last_result_prev.data);
618 saved = true;
619 }
620 }
621
622 // this is a hack, for statevector and matrix product state if the last op
623 // is executed, it can destroy the state! see also the workaround for
624 // statevector for the stabilizer at least for now it doesn't seem to do
625 // that
626 // TODO: check everything!!!!
627 if (saved && simulationType == SimulationType::kMatrixProductState)
628 RestoreState();
629 }
630
639 void RestoreState() override {
640 auto numQubits = GetNumberOfQubits();
641
642 AER::Operations::Op op;
643
644 switch (simulationType) {
645 case SimulationType::kStatevector: {
646 // op.type = AER::Operations::OpType::set_statevec;
647 // op.name = "set_statevec";
648
649 // const auto& vec = static_cast<AER::DataMap<AER::SingleData,
650 // AER::Vector<complex_t>>>(savedState).value()["s"].value();
651
652 // this is a hack until I figure it out
653 Clear();
654 numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
655 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
656
657 return;
658 } break;
659 case SimulationType::kMatrixProductState:
660 op.type = AER::Operations::OpType::set_mps;
661 op.name = "set_mps";
662 op.mps =
663 static_cast<AER::DataMap<AER::SingleData, AER::mps_container_t>>(
664 savedState)
665 .value()["s"]
666 .value();
667
668 /*
669 {
670 auto& value = static_cast<AER::DataMap<AER::SingleData,
671 AER::mps_container_t>>(savedState).value(); if (!value.empty())
672 {
673 if (value.find("s") != value.end())
674 op.mps = value["s"].value();
675 else if (value.find("matrix_product_state") !=
676 value.end()) op.mps = value["matrix_product_state"].value();
677 }
678 }
679 */
680
681 numQubits = op.mps.first.size();
682 break;
683 case SimulationType::kStabilizer:
684 op.type = AER::Operations::OpType::set_stabilizer;
685 op.name = "set_stabilizer";
686 op.clifford =
687 static_cast<AER::DataMap<AER::SingleData, json_t>>(savedState)
688 .value()["s"]
689 .value();
690
691 /*
692 {
693 auto& value = static_cast<AER::DataMap<AER::SingleData,
694 json_t>>(savedState).value(); if (!value.empty())
695 {
696 if (value.find("s") != value.end())
697 op.clifford = value["s"].value();
698 else if (value.find("stabilizer") != value.end())
699 op.clifford = value["stabilizer"].value();
700 }
701 }
702 */
703
704 numQubits = op.clifford.num_qubits();
705 break;
706 case SimulationType::kTensorNetwork:
707 default:
708 throw std::runtime_error(
709 "AerState::RestoreState: not implemented yet "
710 "for this type of simulator.");
711 }
712
713 op.save_type = AER::Operations::DataSubType::single;
714 op.string_params.push_back("s");
715
716 for (size_t q = 0; q < numQubits; ++q) op.qubits.push_back(q);
717
718 // WHY?
719 if (!state->is_initialized()) {
720 Clear();
721 AllocateQubits(numQubits);
722 state->initialize();
723 }
724
725 state->buffer_op(std::move(op));
726 Flush();
727 }
728
736 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
737 return savedAmplitudes[outcome];
738 }
739
748 void SetMultithreading(bool multithreading = true) override {
749 enableMultithreading = multithreading;
750 if (state && !state->is_initialized()) {
751 const std::string nrThreads =
752 std::to_string(enableMultithreading
753 ? 0
754 : 1); // 0 means auto/all available, 1 limits to 1
755 state->configure("max_parallel_threads", nrThreads);
756 state->configure("parallel_state_update", nrThreads);
757 const std::string threadsLimit =
758 std::to_string(12); // set one less, multithreading is started if the
759 // value is bigger than this
760 state->configure("statevector_parallel_threshold", threadsLimit);
761 }
762 }
763
771 bool GetMultithreading() const override { return enableMultithreading; }
772
783 bool IsQcsim() const override { return false; }
784
802 if (simulationType == SimulationType::kStatevector) {
803 const double prob =
804 1. - uniformZeroOne(rng); // this excludes 0 as probabiliy
805 double accum = 0;
806 Types::qubit_t state = 0;
807 for (Types::qubit_t i = 0; i < savedAmplitudes.size(); ++i) {
808 accum += std::norm(savedAmplitudes[i]);
809 if (prob <= accum) {
810 state = i;
811 break;
812 }
813 }
814
815 return state;
816 }
817
818 throw std::runtime_error(
819 "AerState::MeasureNoCollapse: Invalid simulation type for measuring "
820 "all the qubits without collapsing the state.");
821
822 return 0;
823 }
824
839 std::vector<bool> MeasureNoCollapseMany() override {
840 if (simulationType == SimulationType::kStatevector) {
841 const size_t numQubits =
842 static_cast<size_t>(log2(savedAmplitudes.size()));
843 std::vector<bool> res(numQubits, false);
844
846
847 for (size_t i = 0; i < numQubits; ++i) {
848 if ((state & 1) == 1) res[i] = true;
849 state >>= 1;
850 }
851
852 return res;
853 }
854 throw std::runtime_error(
855 "AerState::MeasureNoCollapseMany: Invalid simulation type for "
856 "measuring "
857 "all the qubits without collapsing the state.");
858
859 return {};
860 }
861
862 protected:
863 SimulationType simulationType =
864 SimulationType::kStatevector;
865 std::unique_ptr<QiskitAerState> state =
866 std::make_unique<QiskitAerState>();
867 AER::Vector<complex_t> savedAmplitudes;
868 bool limitSize = false;
869 bool limitEntanglement = false;
870 Eigen::Index chi = 10; // if limitSize is true
871 double singularValueThreshold = 0.; // if limitEntanglement is true
872 bool enableMultithreading = true;
873 AER::Data savedState;
875 std::mt19937_64 rng;
876 std::uniform_real_distribution<double> uniformZeroOne{0., 1.};
877 bool useMPSMeasureNoCollapse =
878 true;
879};
880
881} // namespace Private
882} // namespace Simulators
883
884#endif
885
886#endif
887
888#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)
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:21
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:23