Maestro 0.1.0
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
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;
50public:
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
184 simulationType = SimulationType::kOther;
185 } else if (std::string("matrix_product_state_truncation_threshold") ==
186 key) {
187 singularValueThreshold = std::stod(value);
188 if (singularValueThreshold > 0.)
189 limitEntanglement = true;
190 else
191 limitEntanglement = false;
192 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
193 chi = std::stoi(value);
194 if (chi > 0)
195 limitSize = true;
196 else
197 limitSize = false;
198 } else if (std::string("mps_sample_measure_algorithm") == key)
199 useMPSMeasureNoCollapse = std::string("mps_probabilities") == value;
200
201 state->configure(key, value);
202 }
203
211 std::string GetConfiguration(const char *key) const override {
212 if (std::string("method") == key) {
213 switch (simulationType) {
214 case SimulationType::kStatevector:
215 return "statevector";
216 case SimulationType::kMatrixProductState:
217 return "matrix_product_state";
218 case SimulationType::kStabilizer:
219 return "stabilizer";
220 case SimulationType::kTensorNetwork:
221 return "tensor_network";
222 default:
223 return "other";
224 }
225 } else if (std::string("matrix_product_state_truncation_threshold") ==
226 key) {
227 if (limitEntanglement && singularValueThreshold > 0.)
228 return std::to_string(singularValueThreshold);
229 } else if (std::string("matrix_product_state_max_bond_dimension") == key) {
230 if (limitSize && limitSize > 0)
231 return std::to_string(chi);
232 } else if (std::string("mps_sample_measure_algorithm") == key) {
233 return useMPSMeasureNoCollapse ? "mps_probabilities"
234 : "mps_apply_measure";
235 }
236
237 return "";
238 }
239
247 size_t AllocateQubits(size_t num_qubits) override {
248 const auto ids = state->allocate_qubits(num_qubits);
249 return ids[0];
250 }
251
258 size_t GetNumberOfQubits() const override {
259 if (state->is_initialized())
260 return state->num_of_qubits();
261
262 return 0;
263 }
264
272 void Clear() override {
273 state->clear();
274 SetMultithreading(enableMultithreading);
275
276 if (simulationType == SimulationType::kMatrixProductState)
277 Configure("mps_sample_measure_algorithm", useMPSMeasureNoCollapse
278 ? "mps_probabilities"
279 : "mps_apply_measure");
280 }
281
289 size_t Measure(const Types::qubits_vector &qubits) override {
290 const size_t res = state->apply_measure(qubits);
291
292 NotifyObservers(qubits);
293
294 return res;
295 }
296
303 void ApplyReset(const Types::qubits_vector &qubits) override {
304 state->apply_reset(qubits);
305
306 NotifyObservers(qubits);
307 }
308
320 double Probability(Types::qubit_t outcome) override {
321 return state->probability(outcome);
322 }
323
334 std::complex<double> Amplitude(Types::qubit_t outcome) override {
335 return state->amplitude(outcome);
336 }
337
348 std::vector<double> AllProbabilities() override {
349 return state->probabilities();
350 }
351
363 std::vector<double>
364 Probabilities(const Types::qubits_vector &qubits) override {
365 return state->probabilities(qubits);
366 }
367
384 std::unordered_map<Types::qubit_t, Types::qubit_t>
386 size_t shots = 1000) override {
387 if (qubits.empty() || shots == 0)
388 return {};
389
390 const std::unordered_map<Types::qubit_t, Types::qubit_t> res =
391 state->sample_counts(qubits, shots);
392
393 NotifyObservers(qubits);
394
395 return res;
396 }
397
409 double ExpectationValue(const std::string &pauliStringOrig) override {
410 if (pauliStringOrig.empty())
411 return 1.0;
412
413 std::string pauliString = pauliStringOrig;
414 if (pauliString.size() > GetNumberOfQubits()) {
415 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i) {
416 const auto pauliOp = toupper(pauliString[i]);
417 if (pauliOp != 'I' && pauliOp != 'Z')
418 return 0.0;
419 }
420
421 pauliString.resize(GetNumberOfQubits());
422 }
423
424 AER::reg_t qubits;
425 std::string pauli;
426
427 pauli.reserve(pauliString.size());
428 qubits.reserve(pauliString.size());
429
430 for (size_t q = 0; q < pauliString.size(); ++q) {
431 const char p = toupper(pauliString[q]);
432 if (p == 'I')
433 continue;
434
435 pauli.push_back(p);
436 qubits.push_back(q);
437 }
438
439 if (qubits.empty())
440 return 1.0;
441
442 // qiskit aer expects the pauli string in reverse order
443 std::reverse(pauli.begin(), pauli.end());
444
445 return state->expval_pauli(qubits, pauli);
446 }
447
455 SimulatorType GetType() const override { return SimulatorType::kQiskitAer; }
456
465 SimulationType GetSimulationType() const override { return simulationType; }
466
475 void Flush() override {
476 state->flush_ops();
477 // state->set_random_seed(); // avoid reusing the old seed
478 }
479
489 void SaveStateToInternalDestructive() override {
490 savedAmplitudes = state->move_to_vector();
491 }
492
500 const size_t numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
501 state->initialize_statevector(numQubits, savedAmplitudes.move_to_buffer(),
502 false);
503 }
504
513 void SaveState() override {
514 if (!state)
515 return;
516
517 const auto numQubits = GetNumberOfQubits();
518
519 if (simulationType == SimulationType::kStatevector) {
521 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
522 return;
523 }
524
525 bool saved = false;
526
527 if (state->is_initialized()) {
528 AER::Operations::Op op;
529
530 op.type = AER::Operations::OpType::save_state;
531 op.name = "save_state";
532 op.save_type = AER::Operations::DataSubType::single;
533 op.string_params.push_back("s");
534
535 for (size_t q = 0; q < numQubits; ++q)
536 op.qubits.push_back(q);
537
538 state->buffer_op(std::move(op));
539 Flush();
540
541 // get the state from the last result
542 AER::ExperimentResult &last_result = state->last_result();
543 // state should be in last_result.data
544 if (last_result.status == AER::ExperimentResult::Status::completed) {
545 savedState = std::move(last_result.data);
546 saved = true;
547 }
548 } else {
549 // try get the state from the last result
550 AER::ExperimentResult &last_result_prev = state->last_result();
551 // state should be in last_result.data
552 if (last_result_prev.status == AER::ExperimentResult::Status::completed) {
553 savedState = std::move(last_result_prev.data);
554 saved = true;
555 }
556 }
557
558 // this is a hack, for statevector and matrix product state if the last op
559 // is executed, it can destroy the state! see also the workaround for
560 // statevector for the stabilizer at least for now it doesn't seem to do
561 // that
562 // TODO: check everything!!!!
563 if (saved && simulationType == SimulationType::kMatrixProductState)
564 RestoreState();
565 }
566
575 void RestoreState() override {
576 auto numQubits = GetNumberOfQubits();
577
578 AER::Operations::Op op;
579
580 switch (simulationType) {
581 case SimulationType::kStatevector: {
582 // op.type = AER::Operations::OpType::set_statevec;
583 // op.name = "set_statevec";
584
585 // const auto& vec = static_cast<AER::DataMap<AER::SingleData,
586 // AER::Vector<complex_t>>>(savedState).value()["s"].value();
587
588 // this is a hack until I figure it out
589 Clear();
590 numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
591 state->initialize_statevector(numQubits, savedAmplitudes.data(), true);
592
593 return;
594 } break;
595 case SimulationType::kMatrixProductState:
596 op.type = AER::Operations::OpType::set_mps;
597 op.name = "set_mps";
598 op.mps = static_cast<AER::DataMap<AER::SingleData, AER::mps_container_t>>(
599 savedState)
600 .value()["s"]
601 .value();
602
603 /*
604 {
605 auto& value = static_cast<AER::DataMap<AER::SingleData,
606 AER::mps_container_t>>(savedState).value(); if (!value.empty())
607 {
608 if (value.find("s") != value.end())
609 op.mps = value["s"].value();
610 else if (value.find("matrix_product_state") !=
611 value.end()) op.mps = value["matrix_product_state"].value();
612 }
613 }
614 */
615
616 numQubits = op.mps.first.size();
617 break;
618 case SimulationType::kStabilizer:
619 op.type = AER::Operations::OpType::set_stabilizer;
620 op.name = "set_stabilizer";
621 op.clifford =
622 static_cast<AER::DataMap<AER::SingleData, json_t>>(savedState)
623 .value()["s"]
624 .value();
625
626 /*
627 {
628 auto& value = static_cast<AER::DataMap<AER::SingleData,
629 json_t>>(savedState).value(); if (!value.empty())
630 {
631 if (value.find("s") != value.end())
632 op.clifford = value["s"].value();
633 else if (value.find("stabilizer") != value.end())
634 op.clifford = value["stabilizer"].value();
635 }
636 }
637 */
638
639 numQubits = op.clifford.num_qubits();
640 break;
641 case SimulationType::kTensorNetwork:
642 default:
643 throw std::runtime_error("AerState::RestoreState: not implemented yet "
644 "for this type of simulator.");
645 }
646
647 op.save_type = AER::Operations::DataSubType::single;
648 op.string_params.push_back("s");
649
650 for (size_t q = 0; q < numQubits; ++q)
651 op.qubits.push_back(q);
652
653 // WHY?
654 if (!state->is_initialized()) {
655 Clear();
656 AllocateQubits(numQubits);
657 state->initialize();
658 }
659
660 state->buffer_op(std::move(op));
661 Flush();
662 }
663
671 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
672 return savedAmplitudes[outcome];
673 }
674
683 void SetMultithreading(bool multithreading = true) override {
684 enableMultithreading = multithreading;
685 if (state && !state->is_initialized()) {
686 const std::string nrThreads =
687 std::to_string(enableMultithreading ? 0 : 1); // 0 means auto/all available, 1 limits to 1
688 state->configure("max_parallel_threads", nrThreads);
689 state->configure("parallel_state_update", nrThreads);
690 const std::string threadsLimit = std::to_string(12); // set one less, multithreading is started if the value is bigger than this
691 state->configure("statevector_parallel_threshold", threadsLimit);
692 }
693 }
694
702 bool GetMultithreading() const override { return enableMultithreading; }
703
714 bool IsQcsim() const override { return false; }
715
730 if (simulationType == SimulationType::kStatevector) {
731 const double prob =
732 1. - uniformZeroOne(rng); // this excludes 0 as probabiliy
733 double accum = 0;
734 Types::qubit_t state = 0;
735 for (Types::qubit_t i = 0; i < savedAmplitudes.size(); ++i) {
736 accum += std::norm(savedAmplitudes[i]);
737 if (prob <= accum) {
738 state = i;
739 break;
740 }
741 }
742
743 return state;
744 }
745
746 throw std::runtime_error(
747 "AerState::MeasureNoCollapse: Invalid simulation type for measuring "
748 "all the qubits without collapsing the state.");
749
750 return 0;
751 }
752
753protected:
754 SimulationType simulationType =
755 SimulationType::kStatevector;
756 std::unique_ptr<QiskitAerState> state =
757 std::make_unique<QiskitAerState>();
758 AER::Vector<complex_t> savedAmplitudes;
759 bool limitSize = false;
760 bool limitEntanglement = false;
761 Eigen::Index chi = 10; // if limitSize is true
762 double singularValueThreshold = 0.; // if limitEntanglement is true
763 bool enableMultithreading = true;
764 AER::Data savedState;
766 std::mt19937_64 rng;
767 std::uniform_real_distribution<double> uniformZeroOne{0., 1.};
768 bool useMPSMeasureNoCollapse =
769 true;
770};
771
772} // namespace Private
773} // namespace Simulators
774
775#endif
776
777#endif
778
779#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:21
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:20