Maestro 0.3.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 <numeric>
26#include <sstream>
27#include <stdexcept>
28
30#include "QubitRegister.h"
31
33#include "QiskitAerState.h"
34
35namespace Simulators
36{
37// TODO: Maybe use the pimpl idiom
38// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
39// for good but during development this should be good enough
40namespace Private
41{
42
43class IndividualSimulator;
44
55class AerState : public ISimulator
56{
57 friend class IndividualSimulator;
59 public:
66 AerState()
67 {
68 std::random_device rd;
69
70 rng.seed(rd());
71 Configure("method", "statevector");
72 }
73
81 void Initialize() override
82 {
83 SetMultithreading(enableMultithreading);
84 if (simulationType == SimulationType::kMatrixProductState && !configuration.IsSet("mps_sample_measure_algorithm"))
85 Configure("mps_sample_measure_algorithm", "mps_probabilities");
86
87 // Gate fusion must be off for MPS. Aer intends this itself -- AerState::transpile_ops()
88 // has `case Method::matrix_product_state: fusion_pass_.active = false;` -- but the very
89 // next statement is `fusion_pass_.set_config(configs_)`, and Fusion::set_config does an
90 // unconditional `active = config.fusion_enable`, whose default is true. The MPS disable is
91 // therefore dead code and fusion runs anyway (see qiskit-aer/src/transpile/fusion.hpp and
92 // src/controllers/state_controller.hpp). Aer's other execution path, transpile_fusion() in
93 // simulators/circuit_executor.hpp, guards against exactly this with an early return.
94 // Fixed in our fork as well, but kept here so a build against an unpatched Aer behaves.
95 // It is not a small effect: fusing gates into wider unitaries forces the MPS to bring more
96 // qubits together and do bigger SVDs, and on a 16 qubit brickwork circuit at bond dimension
97 // 128 it costs about 7x (1.5s -> 10.7s). Only set it when the caller has not, so an explicit
98 // "fusion_enable" from a configuration still wins.
99 if (simulationType == SimulationType::kMatrixProductState && !configuration.IsSet("fusion_enable"))
100 Configure("fusion_enable", "false");
101
102 state->initialize();
103 }
104
116 void InitializeState(size_t num_qubits, std::vector<std::complex<double>> &amplitudes) override
117 {
118 Clear();
119 if (simulationType == SimulationType::kDensityMatrix)
120 InitializeDensityMatrixFromStatevector(num_qubits, amplitudes.data());
121 else
122 InitializeStatevectorBuffer(num_qubits, amplitudes.data());
123 }
124
136 /*
137 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
138 avoid_init_allocator<std::complex<double>>>& amplitudes) override
139 {
140 Clear();
141 InitializeStatevectorBuffer(num_qubits, amplitudes.data());
142 }
143 */
144
156 void InitializeState(size_t num_qubits, AER::Vector<std::complex<double>> &amplitudes) override
157 {
158 Clear();
159 if (simulationType == SimulationType::kDensityMatrix)
160 InitializeDensityMatrixFromStatevector(num_qubits, amplitudes.data());
161 else
162 InitializeStatevectorBuffer(num_qubits, amplitudes.data());
163 }
164
176 void InitializeState(size_t num_qubits, Eigen::VectorXcd &amplitudes) override
177 {
178 Clear();
179 if (simulationType == SimulationType::kDensityMatrix)
180 InitializeDensityMatrixFromStatevector(num_qubits, amplitudes.data());
181 else
182 InitializeStatevectorBuffer(num_qubits, amplitudes.data());
183 }
184
191 void Reset() override
192 {
193 const auto numQubits = GetNumberOfQubits();
194 Clear();
195 AllocateQubits(numQubits);
196 state->initialize();
197 }
198
207 void Configure(const char *key, const char *value) override
208 {
209 if (!key || !value)
210 throw std::invalid_argument("Null Aer configuration");
211 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
212 {
213 const bool useDouble = Configuration::ParsePrecision(key, value);
214 const std::string precision = useDouble ? "double" : "single";
215 // Fixed once initialized; a reapplied precision is ignored until Clear.
216 if (state->is_initialized())
217 return;
218 state->configure("precision", precision);
219 configuration.SetConfiguration("precision", precision);
220 return;
221 }
222 if (std::string("seed") != key && configuration.WasApplied(key, value) && state->is_initialized())
223 return;
224
225 // Validate BEFORE storing below: Qiskit Aer's own MPS/MPO truncation always
226 // implements the discarded-weight (Aer/iTensor) convention natively -- see
227 // reduce_zeros() in qiskit-aer/src/simulators/matrix_product_state/svd.cpp.
228 // Unlike the QCSim and GPU backends (see Simulators/QCSimState.h,
229 // Simulators/GpuState.h), there is no relative-to-max mode to switch to
230 // here, so requesting anything else is rejected outright. Checked here,
231 // ahead of the store below, so a rejected value never lingers in
232 // `configuration` -- otherwise it would still show up via
233 // GetConfiguration(), or get silently replayed later by Clone()'s
234 // generic configuration-replay loop, throwing again from an unrelated
235 // call site.
236 if ((std::string("matrix_product_state_truncation_mode") == key || std::string("matrix_product_operator_truncation_mode") == key) &&
237 std::string("discarded_weight") != value)
238 throw std::invalid_argument("Aer backend only supports the discarded_weight truncation mode");
239
240 // Shared configurations can contain settings Aer does not support. Only
241 // record settings after Aer accepts them, so rejected values are not
242 // replayed by Clone() and cannot change the reported simulation type.
243 if (std::string("seed") != key && std::string("use_double_precision") != key && std::string("matrix_product_state_truncation_mode") != key &&
244 std::string("matrix_product_operator_truncation_mode") != key)
245 {
246 try
247 {
248 state->configure(key, value);
249 }
250 catch (const std::exception &)
251 {
252 return;
253 }
254 }
255
256 if (!configuration.WasApplied(key, value))
257 configuration.SetConfiguration(key, value);
258
259 if (std::string("seed") == key)
260 {
261 const uint64_t seed = std::stoull(value);
262 SeedAuxiliaryRng(seed);
263 nextSeedStream = 0;
264 rng.seed(seed);
265 state->set_seed(seed);
266 return;
267 }
268
269 if (std::string("method") == key)
270 {
271 if (std::string("statevector") == value)
272 simulationType = SimulationType::kStatevector;
273 else if (std::string("matrix_product_state") == value)
274 simulationType = SimulationType::kMatrixProductState;
275 else if (std::string("stabilizer") == value)
276 simulationType = SimulationType::kStabilizer;
277 else if (std::string("tensor_network") == value)
278 simulationType = SimulationType::kTensorNetwork;
279 else if (std::string("extended_stabilizer") == value)
280 simulationType = SimulationType::kExtendedStabilizer;
281 else if (std::string("density_matrix") == value)
282 simulationType = SimulationType::kDensityMatrix;
283 else
284 simulationType = SimulationType::kOther;
285 }
286 }
287
295 std::string GetConfiguration(const char *key) const override
296 {
297 if (!key)
298 return {};
299 if (std::string(key) == "precision" || std::string(key) == "use_double_precision")
300 {
301 const bool fixedDouble = simulationType == SimulationType::kMatrixProductState;
302 const bool useDouble = fixedDouble || !configuration.IsSet("precision") || configuration.GetConfiguration("precision") == "double";
303 return std::string(key) == "precision" ? (useDouble ? "double" : "single") : (useDouble ? "1" : "0");
304 }
305 if (std::string("method") == key)
306 {
307 switch (simulationType)
308 {
309 case SimulationType::kStatevector:
310 return "statevector";
311 case SimulationType::kMatrixProductState:
312 return "matrix_product_state";
313 case SimulationType::kStabilizer:
314 return "stabilizer";
315 case SimulationType::kTensorNetwork:
316 return "tensor_network";
317 case SimulationType::kExtendedStabilizer:
318 return "extended_stabilizer";
319 case SimulationType::kDensityMatrix:
320 return "density_matrix";
321 default:
322 return "other";
323 }
324 }
325
326 return configuration.GetConfiguration(key);
327 }
328
336 size_t AllocateQubits(size_t num_qubits) override
337 {
338 const auto ids = state->allocate_qubits(num_qubits);
339 return ids[0];
340 }
341
348 size_t GetNumberOfQubits() const override
349 {
350 if (state->is_initialized())
351 return state->num_of_qubits();
352
353 return 0;
354 }
355
363 void Clear() override
364 {
365 state->clear();
366 SetMultithreading(enableMultithreading);
367
368 if (simulationType == SimulationType::kMatrixProductState && !configuration.IsSet("mps_sample_measure_algorithm"))
369 Configure("mps_sample_measure_algorithm", "mps_probabilities");
370 }
371
382 size_t Measure(const Types::qubits_vector &qubits) override
383 {
384 if (qubits.size() > sizeof(size_t) * 8)
385 std::cerr << "Warning: The number of qubits to measure is larger than the "
386 "number of bits in the size_t type, the outcome will be undefined"
387 << std::endl;
388
389 const size_t res = state->apply_measure(qubits);
390
391 NotifyObservers(qubits);
392
393 return res;
394 }
395
402 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override
403 {
404 auto res = state->apply_measure_many(qubits);
405
406 NotifyObservers(qubits);
407
408 return res;
409 }
410
417 void ApplyReset(const Types::qubits_vector &qubits) override
418 {
419 state->apply_reset(qubits);
420
421 NotifyObservers(qubits);
422 }
423
424 bool SupportsQuantumChannels() const override
425 {
426 return simulationType == SimulationType::kDensityMatrix;
427 }
428
439 void ApplyQuantumChannel(const Types::qubits_vector &targets, const QuantumChannel &channel) override
440 {
441 if (!SupportsQuantumChannels())
442 throw std::runtime_error("Aer exact quantum channels require density_matrix simulation");
443 if (targets.size() != channel.GetNumberOfQubits())
444 throw std::invalid_argument("The number of channel targets does not match its Kraus operators");
445 if (targets.empty() || targets.size() > 2)
446 throw std::invalid_argument("Maestro supports only one- and two-qubit local channels");
447
448 std::unordered_set<Types::qubit_t> uniqueTargets;
449 for (const Types::qubit_t target : targets)
450 {
451 if (target >= GetNumberOfQubits())
452 throw std::invalid_argument("Quantum-channel qubit is out of range");
453 if (!uniqueTargets.insert(target).second)
454 throw std::invalid_argument("Quantum-channel target qubits must be distinct");
455 }
456
457 std::vector<AER::cmatrix_t> aerKrausOperators;
458 aerKrausOperators.reserve(channel.GetKrausOperators().size());
459 for (const Eigen::MatrixXcd &krausOperator : channel.GetKrausOperators())
460 {
461 AER::cmatrix_t aerOperator(static_cast<size_t>(krausOperator.rows()), static_cast<size_t>(krausOperator.cols()));
462 for (Eigen::Index row = 0; row < krausOperator.rows(); ++row)
463 for (Eigen::Index column = 0; column < krausOperator.cols(); ++column)
464 aerOperator(static_cast<size_t>(row), static_cast<size_t>(column)) = krausOperator(row, column);
465 aerKrausOperators.emplace_back(std::move(aerOperator));
466 }
467
468 // Both QCSim and Aer treat targets[0] as local matrix bit zero (LSB), so
469 // channel target order is preserved and matrices need no permutation.
470 const AER::reg_t aerTargets(targets.begin(), targets.end());
471 state->apply_kraus(aerTargets, aerKrausOperators);
472 NotifyObservers(targets);
473 }
474
486 double Probability(Types::qubit_t outcome) override
487 {
488 return state->probability(outcome);
489 }
490
501 std::complex<double> Amplitude(Types::qubit_t outcome) override
502 {
503 if (simulationType == SimulationType::kDensityMatrix)
504 throw std::runtime_error("AerState::Amplitude is not defined for density matrix simulation");
505
506 if (simulationType == SimulationType::kExtendedStabilizer)
507 {
508 const auto amplitudes = state->statevector();
509 return outcome < amplitudes.size() ? amplitudes[outcome] : complex_t{};
510 }
511
512 return state->amplitude(outcome);
513 }
514
528 std::complex<double> ProjectOnZero() override
529 {
530 return Amplitude(0);
531 }
532
543 std::vector<double> AllProbabilities() override
544 {
545 if (simulationType == SimulationType::kDensityMatrix)
546 {
548 std::iota(qubits.begin(), qubits.end(), 0);
549 return state->probabilities(qubits);
550 }
551
552 if (simulationType == SimulationType::kExtendedStabilizer)
553 {
554 const auto amplitudes = state->statevector();
555 std::vector<double> probabilities(amplitudes.size());
556 for (size_t outcome = 0; outcome < amplitudes.size(); ++outcome)
557 probabilities[outcome] = std::norm(amplitudes[outcome]);
558 return probabilities;
559 }
560
561 return state->probabilities();
562 }
563
575 std::vector<double> Probabilities(const Types::qubits_vector &qubits) override
576 {
577 if (simulationType == SimulationType::kDensityMatrix)
578 {
579 std::vector<double> probabilities;
580 probabilities.reserve(qubits.size());
581 for (const auto outcome : qubits)
582 probabilities.push_back(state->probability(outcome));
583 return probabilities;
584 }
585
586 if (simulationType == SimulationType::kExtendedStabilizer)
587 {
588 const auto amplitudes = state->statevector();
589 std::vector<double> probabilities;
590 probabilities.reserve(qubits.size());
591 for (const auto outcome : qubits)
592 probabilities.push_back(outcome < amplitudes.size() ? std::norm(amplitudes[outcome]) : 0.0);
593 return probabilities;
594 }
595
596 return state->probabilities(qubits);
597 }
598
615 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(const Types::qubits_vector &qubits, size_t shots = 1000) override
616 {
617 if (qubits.empty() || shots == 0)
618 return {};
619
620 if (qubits.size() > sizeof(Types::qubit_t) * 8)
621 std::cerr << "Warning: The number of qubits to measure is larger than the "
622 "number of bits in the Types::qubit_t type, the outcome will be "
623 "undefined"
624 << std::endl;
625
626 // Aer MPS can return native samples sorted by qubit index. Use the
627 // ordered sampling adapter for both public result formats.
628 std::unordered_map<Types::qubit_t, Types::qubit_t> res;
629 for (const auto &[bits, count] : state->sample_counts_many(qubits, shots))
630 {
631 Types::qubit_t packed = 0;
632 const size_t width = std::min(bits.size(), sizeof(Types::qubit_t) * 8);
633 for (size_t i = 0; i < width; ++i)
634 if (bits[i])
635 packed |= Types::qubit_t(1) << i;
636 res[packed] += count;
637 }
638
639 NotifyObservers(qubits);
640
641 return res;
642 }
643
657 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(const Types::qubits_vector &qubits, size_t shots = 1000) override
658 {
659 if (qubits.empty() || shots == 0)
660 return {};
661 std::unordered_map<std::vector<bool>, Types::qubit_t> res = state->sample_counts_many(qubits, shots);
662 NotifyObservers(qubits);
663 return res;
664 }
665
677 double ExpectationValue(const std::string &pauliStringOrig) override
678 {
679 if (pauliStringOrig.empty())
680 return 1.0;
681
682 std::string pauliString = pauliStringOrig;
683 if (pauliString.size() > GetNumberOfQubits())
684 {
685 for (size_t i = GetNumberOfQubits(); i < pauliString.size(); ++i)
686 {
687 const auto pauliOp = toupper(pauliString[i]);
688 if (pauliOp != 'I' && pauliOp != 'Z')
689 return 0.0;
690 }
691
692 pauliString.resize(GetNumberOfQubits());
693 }
694
695 AER::reg_t qubits;
696 std::string pauli;
697
698 pauli.reserve(pauliString.size());
699 qubits.reserve(pauliString.size());
700
701 for (size_t q = 0; q < pauliString.size(); ++q)
702 {
703 const char p = toupper(pauliString[q]);
704 if (p == 'I')
705 continue;
706
707 pauli.push_back(p);
708 qubits.push_back(q);
709 }
710
711 if (qubits.empty())
712 return 1.0;
713
714 // qiskit aer expects the pauli string in reverse order
715 std::reverse(pauli.begin(), pauli.end());
716
717 return state->expval_pauli(qubits, pauli);
718 }
719
727 SimulatorType GetType() const override
728 {
729 return SimulatorType::kQiskitAer;
730 }
731
740 SimulationType GetSimulationType() const override
741 {
742 return simulationType;
743 }
744
753 void Flush() override
754 {
755 state->flush_ops();
756 // state->set_random_seed(); // avoid reusing the old seed
757 }
758
768 void SaveStateToInternalDestructive() override
769 {
770 if (simulationType == SimulationType::kDensityMatrix)
771 savedDensityMatrix = state->move_to_matrix();
772 else
773 savedAmplitudes = state->move_to_vector();
774 }
775
783 {
784 if (simulationType == SimulationType::kDensityMatrix)
785 {
786 const size_t numQubits = static_cast<size_t>(log2(savedDensityMatrix.GetRows()));
787 InitializeDensityMatrixBuffer(numQubits, savedDensityMatrix.data());
788 return;
789 }
790
791 const size_t numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
792 InitializeStatevectorBuffer(numQubits, savedAmplitudes.data());
793 }
794
803 void SaveState() override
804 {
805 if (!state)
806 return;
807
808 const auto numQubits = GetNumberOfQubits();
809
810 if (simulationType == SimulationType::kExtendedStabilizer)
811 {
812 savedExtendedStabilizerState = state->clone_extended_stabilizer_state();
813 return;
814 }
815
816 if (simulationType == SimulationType::kStatevector || simulationType == SimulationType::kDensityMatrix)
817 {
819 if (simulationType == SimulationType::kDensityMatrix)
820 InitializeDensityMatrixBuffer(numQubits, savedDensityMatrix.data());
821 else
822 InitializeStatevectorBuffer(numQubits, savedAmplitudes.data());
823 return;
824 }
825
826 bool saved = false;
827
828 if (state->is_initialized())
829 {
830 AER::Operations::Op op;
831
832 op.type = AER::Operations::OpType::save_state;
833 op.name = "save_state";
834 op.save_type = AER::Operations::DataSubType::single;
835 op.string_params.push_back("s");
836
837 for (size_t q = 0; q < numQubits; ++q)
838 op.qubits.push_back(q);
839
840 state->buffer_op(std::move(op));
841 Flush();
842
843 // get the state from the last result
844 AER::ExperimentResult &last_result = state->last_result();
845 // state should be in last_result.data
846 if (last_result.status == AER::ExperimentResult::Status::completed)
847 {
848 savedState = std::move(last_result.data);
849 saved = true;
850 }
851 }
852 else
853 {
854 // try get the state from the last result
855 AER::ExperimentResult &last_result_prev = state->last_result();
856 // state should be in last_result.data
857 if (last_result_prev.status == AER::ExperimentResult::Status::completed)
858 {
859 savedState = std::move(last_result_prev.data);
860 saved = true;
861 }
862 }
863
864 // this is a hack, for statevector and matrix product state if the last op
865 // is executed, it can destroy the state! see also the workaround for
866 // statevector for the stabilizer at least for now it doesn't seem to do
867 // that
868 // TODO: check everything!!!!
869 if (saved && simulationType == SimulationType::kMatrixProductState)
870 RestoreState();
871 }
872
881 void RestoreState() override
882 {
883 auto numQubits = GetNumberOfQubits();
884
885 AER::Operations::Op op;
886
887 switch (simulationType)
888 {
889 case SimulationType::kStatevector: {
890 // op.type = AER::Operations::OpType::set_statevec;
891 // op.name = "set_statevec";
892
893 // const auto& vec = static_cast<AER::DataMap<AER::SingleData,
894 // AER::Vector<complex_t>>>(savedState).value()["s"].value();
895
896 // this is a hack until I figure it out
897 Clear();
898 numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
899 InitializeStatevectorBuffer(numQubits, savedAmplitudes.data());
900
901 return;
902 }
903 break;
904 case SimulationType::kDensityMatrix: {
905 Clear();
906 numQubits = static_cast<size_t>(log2(savedDensityMatrix.GetRows()));
907 InitializeDensityMatrixBuffer(numQubits, savedDensityMatrix.data());
908
909 return;
910 }
911 break;
912 case SimulationType::kExtendedStabilizer:
913 if (!savedExtendedStabilizerState)
914 throw std::runtime_error("AerState::RestoreState: no extended stabilizer state was saved");
915 state->restore_extended_stabilizer_state(savedExtendedStabilizerState);
916 return;
917 case SimulationType::kMatrixProductState:
918 op.type = AER::Operations::OpType::set_mps;
919 op.name = "set_mps";
920 op.mps = static_cast<AER::DataMap<AER::SingleData, AER::mps_container_t>>(savedState).value()["s"].value();
921
922 /*
923 {
924 auto& value = static_cast<AER::DataMap<AER::SingleData,
925 AER::mps_container_t>>(savedState).value(); if (!value.empty())
926 {
927 if (value.find("s") != value.end())
928 op.mps = value["s"].value();
929 else if (value.find("matrix_product_state") !=
930 value.end()) op.mps = value["matrix_product_state"].value();
931 }
932 }
933 */
934
935 numQubits = op.mps.first.size();
936 break;
937 case SimulationType::kStabilizer:
938 op.type = AER::Operations::OpType::set_stabilizer;
939 op.name = "set_stabilizer";
940 op.clifford = static_cast<AER::DataMap<AER::SingleData, json_t>>(savedState).value()["s"].value();
941
942 /*
943 {
944 auto& value = static_cast<AER::DataMap<AER::SingleData,
945 json_t>>(savedState).value(); if (!value.empty())
946 {
947 if (value.find("s") != value.end())
948 op.clifford = value["s"].value();
949 else if (value.find("stabilizer") != value.end())
950 op.clifford = value["stabilizer"].value();
951 }
952 }
953 */
954
955 numQubits = op.clifford.num_qubits();
956 break;
957 case SimulationType::kTensorNetwork:
958 default:
959 throw std::runtime_error("AerState::RestoreState: not implemented yet "
960 "for this type of simulator.");
961 }
962
963 op.save_type = AER::Operations::DataSubType::single;
964 op.string_params.push_back("s");
965
966 for (size_t q = 0; q < numQubits; ++q)
967 op.qubits.push_back(q);
968
969 // WHY?
970 if (!state->is_initialized())
971 {
972 Clear();
973 AllocateQubits(numQubits);
974 state->initialize();
975 }
976
977 state->buffer_op(std::move(op));
978 Flush();
979 }
980
988 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override
989 {
990 if (simulationType == SimulationType::kDensityMatrix)
991 throw std::runtime_error("AerState::AmplitudeRaw is not defined for density matrix "
992 "simulation");
993
994 return savedAmplitudes[outcome];
995 }
996
1005 void SetMultithreading(bool multithreading = true) override
1006 {
1007 enableMultithreading = multithreading;
1008 if (state && !state->is_initialized())
1009 {
1010 const std::string nrThreads = std::to_string(enableMultithreading ? 0 : 1); // 0 means auto/all available, 1 limits to 1
1011 state->configure("max_parallel_threads", nrThreads);
1012 state->configure("parallel_state_update", nrThreads);
1013 const std::string threadsLimit = std::to_string(12); // set one less, multithreading is started if the
1014 // value is bigger than this
1015 state->configure("statevector_parallel_threshold", threadsLimit);
1016
1017 configuration.SetConfiguration(std::string("max_parallel_threads"), nrThreads);
1018 configuration.SetConfiguration(std::string("parallel_state_update"), nrThreads);
1019 configuration.SetConfiguration(std::string("statevector_parallel_threshold"), threadsLimit);
1020 }
1021 }
1022
1030 bool GetMultithreading() const override
1031 {
1032 return enableMultithreading;
1033 }
1034
1045 bool IsQcsim() const override
1046 {
1047 return false;
1048 }
1049
1067 {
1068 if (simulationType == SimulationType::kStatevector)
1069 {
1070 const double prob = 1. - uniformZeroOne(rng); // this excludes 0 as probabiliy
1071 double accum = 0;
1072 Types::qubit_t state = 0;
1073 for (Types::qubit_t i = 0; i < savedAmplitudes.size(); ++i)
1074 {
1075 accum += std::norm(savedAmplitudes[i]);
1076 if (prob <= accum)
1077 {
1078 state = i;
1079 break;
1080 }
1081 }
1082
1083 return state;
1084 }
1085
1086 throw std::runtime_error("AerState::MeasureNoCollapse: Invalid simulation type for measuring "
1087 "all the qubits without collapsing the state.");
1088
1089 return 0;
1090 }
1091
1106 std::vector<bool> MeasureNoCollapseMany() override
1107 {
1108 if (simulationType == SimulationType::kStatevector)
1109 {
1110 const size_t numQubits = static_cast<size_t>(log2(savedAmplitudes.size()));
1111 std::vector<bool> res(numQubits, false);
1112
1114
1115 for (size_t i = 0; i < numQubits; ++i)
1116 {
1117 if ((state & 1) == 1)
1118 res[i] = true;
1119 state >>= 1;
1120 }
1121
1122 return res;
1123 }
1124 throw std::runtime_error("AerState::MeasureNoCollapseMany: Invalid simulation type for "
1125 "measuring "
1126 "all the qubits without collapsing the state.");
1127
1128 return {};
1129 }
1130
1131 const Configuration &GetConfiguration() const
1132 {
1133 return configuration;
1134 }
1135
1136 const std::unordered_map<std::string, std::string> &GetConfigMap() const override
1137 {
1138 return configuration.GetConfigMap();
1139 }
1140
1141 protected:
1142 void InitializeStatevectorBuffer(size_t numQubits, std::complex<double> *amplitudes)
1143 {
1144 if (GetConfiguration("precision") == "double")
1145 {
1146 state->initialize_statevector(numQubits, amplitudes, true);
1147 return;
1148 }
1149 AllocateQubits(numQubits);
1150 Initialize();
1151 AER::Operations::Op op;
1152 op.type = AER::Operations::OpType::set_statevec;
1153 op.name = "set_statevector";
1154 op.params.assign(amplitudes, amplitudes + (size_t{1} << numQubits));
1155 for (size_t q = 0; q < numQubits; ++q)
1156 op.qubits.push_back(q);
1157 state->buffer_op(std::move(op));
1158 Flush();
1159 }
1160
1161 void InitializeDensityMatrixBuffer(size_t numQubits, std::complex<double> *values)
1162 {
1163 if (GetConfiguration("precision") == "double")
1164 {
1165 state->initialize_density_matrix(numQubits, values, true, true);
1166 return;
1167 }
1168 AllocateQubits(numQubits);
1169 Initialize();
1170 const size_t dimension = size_t{1} << numQubits;
1171 AER::Operations::Op op;
1172 op.type = AER::Operations::OpType::set_densmat;
1173 op.name = "set_density_matrix";
1174 op.mats.emplace_back(dimension, dimension);
1175 std::copy(values, values + dimension * dimension, op.mats.back().data());
1176 for (size_t q = 0; q < numQubits; ++q)
1177 op.qubits.push_back(q);
1178 state->buffer_op(std::move(op));
1179 Flush();
1180 }
1181
1182 void InitializeDensityMatrixFromStatevector(size_t numQubits, const std::complex<double> *amplitudes)
1183 {
1184 const size_t dimension = 1ULL << numQubits;
1185 AER::cmatrix_t densityMatrix(dimension, dimension);
1186 for (size_t row = 0; row < dimension; ++row)
1187 for (size_t column = 0; column < dimension; ++column)
1188 densityMatrix(row, column) = amplitudes[row] * std::conj(amplitudes[column]);
1189
1190 InitializeDensityMatrixBuffer(numQubits, densityMatrix.data());
1191 }
1192
1193 SimulationType simulationType = SimulationType::kStatevector;
1194 std::unique_ptr<QiskitAerState> state = std::make_unique<QiskitAerState>();
1195 AER::Vector<complex_t> savedAmplitudes;
1196 AER::cmatrix_t savedDensityMatrix;
1197 std::shared_ptr<AER::QuantumState::Base> savedExtendedStabilizerState;
1198
1199 bool enableMultithreading = true;
1200 AER::Data savedState;
1202 std::mt19937_64 rng;
1203 uint64_t nextSeedStream = 0;
1204 std::uniform_real_distribution<double> uniformZeroOne{0., 1.};
1205
1206 Configuration configuration;
1207};
1208
1209} // namespace Private
1210} // namespace Simulators
1211
1212#endif
1213
1214#endif
1215
1216#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)
Bounded online routing based on current physical bond dimensions.
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:24
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:23