Maestro 0.3.1
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Individual.h
Go to the documentation of this file.
1
11
12#ifndef _INDIVIDUAL_H
13#define _INDIVIDUAL_H
14
15#ifdef INCLUDED_BY_FACTORY
16
17#include "../Utils/Alias.h"
18#include "Factory.h"
19#include <unordered_map>
20
21namespace Simulators {
22
23// TODO: Maybe use the pimpl idiom
24// https://en.cppreference.com/w/cpp/language/pimpl to hide the implementation
25// for good but during development this should be good enough
26namespace Private {
27
28class CompositeSimulator;
29
38class IndividualSimulator : public ISimulator {
39 friend class CompositeSimulator;
40
41 public:
48 IndividualSimulator(SimulatorType type =
49#ifdef NO_QISKIT_AER
51#else
53#endif
54 ) noexcept
55 : simulator(SimulatorsFactory::CreateSimulatorUnique(
57 }
58
66 void Reset() override { simulator->Reset(); }
67
79 inline void Join(size_t simId,
80 const std::unique_ptr<IndividualSimulator> &other,
81 std::vector<size_t> &qubitsMapToSim,
82 bool enableMultithreading) {
83 // 1. grab the state of both simulators (in the first phase, using
84 // Amplitude, but maybe something faster can be done)
85 // 2. join the states by a tensor product
86 const size_t nrQubits1 = GetNumberOfQubits();
87 const size_t nrBasisStates1 = 1ULL << nrQubits1;
88 const size_t nrQubits2 = other->GetNumberOfQubits();
89 const size_t nrBasisStates2 = 1ULL << nrQubits2;
90
91 const size_t newNrQubits = nrQubits1 + nrQubits2;
92 const size_t nrBasisStates = 1ULL << newNrQubits;
93
95 other->SaveStateToInternalDestructive();
96
97 if (GetType() == SimulatorType::kQCSim) {
98 if (enableMultithreading && nrBasisStates > OmpLimitJoin)
99 JoinOmpQcsim(nrQubits1, nrBasisStates1, nrBasisStates2, newNrQubits,
100 nrBasisStates, other, enableMultithreading);
101 else {
102 Eigen::VectorXcd newAmplitudes;
103 newAmplitudes.resize(nrBasisStates);
104
105 for (size_t state2 = 0; state2 < nrBasisStates2; ++state2) {
106 const auto ampl2 = other->AmplitudeRaw(state2);
107 const size_t state2Mask = state2 << nrQubits1;
108 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
109 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
110 }
111
112 // 3. set the state of the current simulator to the joined state
113 // the original qubits of this simulator get mapped as they are
114 // the other ones get shifted to the left by the number of qubits of
115 // this simulator so transfer mapping keeping this in mind
116
117 // simulator->SetMultithreading(enableMultithreading);
118 simulator->InitializeState(
119 newNrQubits,
120 newAmplitudes); // this will end up by swapping the data from
121 // newAmplitudes to the simulator, no allocation
122 // and copying is done
123 }
124 }
125#ifndef NO_QISKIT_AER
126 else {
127 if (enableMultithreading && nrBasisStates > OmpLimitJoin)
128 JoinOmpAer(nrQubits1, nrBasisStates1, nrBasisStates2, newNrQubits,
129 nrBasisStates, other, enableMultithreading);
130 else {
131 AER::Vector<std::complex<double>> newAmplitudes(
132 nrBasisStates, false); // the false here avoids data
133 // initialization, it will be set anyway
134
135 for (size_t state2 = 0; state2 < nrBasisStates2; ++state2) {
136 const auto ampl2 = other->AmplitudeRaw(state2);
137 const size_t state2Mask = state2 << nrQubits1;
138 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
139 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
140 }
141
142 // 3. set the state of the current simulator to the joined state
143 // the original qubits of this simulator get mapped as they are
144 // the other ones get shifted to the left by the number of qubits of
145 // this simulator so transfer mapping keeping this in mind
146 // simulator->SetMultithreading(enableMultithreading);
147 simulator->InitializeState(
148 newNrQubits,
149 newAmplitudes); // this will move the data from newAmplitudes to
150 // the simulator, no allocation and copying is done
151 }
152 }
153#endif
154
155 for (auto [origq, mapq] : other->GetQubitsMap()) {
156 qubitsMap[origq] = mapq + nrQubits1;
157 qubitsMapToSim[origq] = simId;
158 }
159 }
160
174 inline std::unique_ptr<IndividualSimulator> Split(size_t qubit,
175 bool qubitOutcome,
176 bool enableMultithreading) {
177 const size_t oldNrQubits = GetNumberOfQubits();
178 const size_t newNrQubits = oldNrQubits - 1;
179 const size_t nrBasisStates = 1ULL << newNrQubits;
180 const size_t localQubit = qubitsMap[qubit];
181
182 // the new simulator split from this one
183 // a one qubit one, containing the qubit that was measured or reset
184 // TODO: this can be optimized a little bit by directly initializing with
185 // the proper amplitudes, but I'm not sure if it's worth it
186 auto newSimulator = std::make_unique<IndividualSimulator>(GetType());
187 newSimulator->AllocateQubits(1);
188 newSimulator->GetQubitsMap()[qubit] =
189 0; // the qubit is mapped to the only local qubit in the new simulator,
190 // which is 0
191 newSimulator->SetMultithreading(enableMultithreading);
192
193 for (const auto& [key, value] : GetConfigMap())
194 newSimulator->Configure(key.c_str(), value.c_str());
195
196 newSimulator->Initialize();
197 if (qubitOutcome) {
198 newSimulator->ApplyX(qubit);
199 // newSimulator->Flush();
200 }
201
202 qubitsMap.erase(qubit); // the qubit is removed from the current simulator
203
205
206 if (GetType() == SimulatorType::kQCSim) {
207 /*
208 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
209 some bit manipulations, I must do some benchmarks to see if it's worth it
210 and find where the limit is SplitOmpQcsim(localQubit, newNrQubits,
211 nrBasisStates, qubitOutcome); else
212 */
213 {
214 // now the adjusted current simulator, without the removed qubit
215 Eigen::VectorXcd newAmplitudes;
216 newAmplitudes.resize(nrBasisStates);
217
218 // compute the new amplitudes
219
220 const size_t localQubitMask = 1ULL << localQubit;
221 const size_t maskLow = localQubitMask - 1ULL;
222 const size_t maskHigh = ~maskLow;
223 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
224
225 for (size_t state = 0; state < nrBasisStates; ++state) {
226 const size_t stateLow = state & maskLow;
227 const size_t stateHigh = (state & maskHigh) << 1ULL;
228
229 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
230 }
231
232 // simulator->SetMultithreading(enableMultithreading);
233 simulator->InitializeState(
234 newNrQubits,
235 newAmplitudes); // this will end up by swapping the data from
236 // newAmplitudes to the simulator, no allocation
237 // and copying is done
238 }
239 }
240#ifndef NO_QISKIT_AER
241 else {
242 /*
243 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
244 some bit manipulations, I must do some benchmarks to see if it's worth it
245 and find where the limit is SplitOmpAer(localQubit, newNrQubits,
246 nrBasisStates, qubitOutcome); else
247 */
248 {
249 // now the adjusted current simulator, without the removed qubit
250 AER::Vector<std::complex<double>> newAmplitudes(
251 nrBasisStates, false); // the false here avoids data
252 // initialization, it will be set anyway
253
254 // compute the new amplitudes
255 const size_t localQubitMask = 1ULL << localQubit;
256 const size_t maskLow = localQubitMask - 1ULL;
257 const size_t maskHigh = ~maskLow;
258 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
259
260 for (size_t state = 0; state < nrBasisStates; ++state) {
261 const size_t stateLow = state & maskLow;
262 const size_t stateHigh = (state & maskHigh) << 1ULL;
263
264 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
265 }
266
267 // simulator->SetMultithreading(enableMultithreading);
268 simulator->InitializeState(
269 newNrQubits,
270 newAmplitudes); // this will move the data from newAmplitudes to
271 // the simulator, no allocation and copying is done
272 }
273 }
274#endif
275
276 // now adjust the local qubits map
277 for (auto &mapped : qubitsMap)
278 if (mapped.second > localQubit) --mapped.second;
279
280 return newSimulator;
281 }
282
292 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
293 return simulator->AmplitudeRaw(outcome);
294 }
295
305 void SaveStateToInternalDestructive() override {
306 simulator->SaveStateToInternalDestructive();
307 }
308
316 simulator->RestoreInternalDestructiveSavedState();
317 }
318
328 inline Types::qubits_vector ConvertQubits(
329 const Types::qubits_vector &qubits) {
330 Types::qubits_vector converted;
331 converted.reserve(qubits.size());
332
333 for (auto qubit : qubits)
334 if (HasQubit(qubit)) converted.emplace_back(qubitsMap[qubit]);
335
336 return converted;
337 }
338
347 inline bool HasQubit(Types::qubit_t qubit) const {
348 return qubitsMap.find(qubit) != qubitsMap.end();
349 }
350
359 inline Types::qubit_t ConvertOutcomeFromLocal(Types::qubit_t outcome) const {
360 Types::qubit_t res = 0;
361
362 for (auto [origQubit, localQubit] : qubitsMap)
363 if (outcome & (1ULL << localQubit)) res |= (1ULL << origQubit);
364
365 return res;
366 }
367
376 inline Types::qubit_t ConvertOutcomeFromGlobal(Types::qubit_t outcome) const {
377 Types::qubit_t res = 0;
378
379 for (auto [origQubit, localQubit] : qubitsMap)
380 if (outcome & (1ULL << origQubit)) res |= (1ULL << localQubit);
381
382 return res;
383 }
384
393 inline std::vector<bool> ConvertOutcomeFromLocal(
394 const std::vector<bool> &outcome) const {
395 std::vector<bool> res;
396
397 size_t maxQubit = 0;
398 for (auto [origQubit, localQubit] : qubitsMap)
399 if (origQubit > maxQubit) maxQubit = origQubit;
400 res.resize(maxQubit + 1, false);
401
402 for (auto [origQubit, localQubit] : qubitsMap)
403 if (outcome[localQubit]) res[origQubit] = true;
404
405 return res;
406 }
407
416 inline std::vector<bool> ConvertOutcomeFromGlobal(
417 const std::vector<bool> &outcome) const {
418 const size_t nrQubits = simulator->GetNumberOfQubits();
419 std::vector<bool> res(nrQubits, false);
420
421 for (auto [origQubit, localQubit] : qubitsMap)
422 if (outcome[origQubit]) res[localQubit] = true;
423
424 return res;
425 }
426
432 void SaveState() override {
433 if (!simulator) return;
434 const size_t nrBasisStates = 1ULL << simulator->GetNumberOfQubits();
435 savedState.reserve(nrBasisStates);
436
437 for (Types::qubit_t state = 0; state < nrBasisStates; ++state)
438 savedState.emplace_back(simulator->Amplitude(state));
439 }
440
446 void ClearSavedState() { savedState.clear(); }
447
453 void RestoreState() override {
454 if (!simulator) return;
455 const size_t nrQubits = simulator->GetNumberOfQubits();
456
457 simulator->Clear();
458 simulator->InitializeState(nrQubits, savedState);
459 ClearSavedState();
460 }
461
470 inline std::unordered_map<Types::qubit_t, Types::qubit_t> &GetQubitsMap() {
471 return qubitsMap;
472 }
473
482 inline const std::unordered_map<Types::qubit_t, Types::qubit_t> &
483 GetQubitsMap() const {
484 return qubitsMap;
485 }
486
493 void Initialize() override { simulator->Initialize(); }
494
507 void InitializeState(size_t num_qubits,
508 std::vector<std::complex<double>> &amplitudes) override {
509 simulator->InitializeState(num_qubits, amplitudes);
510 }
511
524 /*
525 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
526 avoid_init_allocator<std::complex<double>>>& amplitudes) override
527 {
528 simulator->InitializeState(num_qubits, amplitudes);
529 }
530 */
531
544#ifndef NO_QISKIT_AER
545 void InitializeState(size_t num_qubits,
546 AER::Vector<std::complex<double>> &amplitudes) override {
547 simulator->InitializeState(num_qubits, amplitudes);
548 }
549#endif
550
563 void InitializeState(size_t num_qubits,
564 Eigen::VectorXcd &amplitudes) override {
565 simulator->InitializeState(num_qubits, amplitudes);
566 }
567
576 void Configure(const char *key, const char *value) override {
577 simulator->Configure(key, value);
578 }
579
587 std::string GetConfiguration(const char *key) const override {
588 if (!simulator) return "";
589
590 return simulator->GetConfiguration(key);
591 }
592
600 size_t AllocateQubits(size_t num_qubits) override {
601 return simulator->AllocateQubits(num_qubits);
602 }
603
610 size_t GetNumberOfQubits() const override {
611 return simulator->GetNumberOfQubits();
612 }
613
621 void Clear() override { simulator->Clear(); }
622
634 size_t Measure(const Types::qubits_vector &qubits) override {
635 return ConvertOutcomeFromLocal(simulator->Measure(ConvertQubits(qubits)));
636 }
637
646 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
647 return ConvertOutcomeFromLocal(
648 simulator->MeasureMany(ConvertQubits(qubits)));
649 }
650
657 void ApplyReset(const Types::qubits_vector &qubits) override {
658 simulator->ApplyReset(ConvertQubits(qubits));
659 }
660
672 double Probability(Types::qubit_t outcome) override {
673 return simulator->Probability(ConvertOutcomeFromGlobal(outcome));
674 }
675
686 std::complex<double> Amplitude(Types::qubit_t outcome) override {
687 return simulator->Amplitude(ConvertOutcomeFromGlobal(outcome));
688 }
689
703 std::complex<double> ProjectOnZero() override {
704 return Amplitude(0);
705 }
706
716 std::vector<double> AllProbabilities() override {
717 return simulator->AllProbabilities();
718 }
719
731 std::vector<double> Probabilities(
732 const Types::qubits_vector &qubits) override {
733 return simulator->Probabilities(ConvertQubits(qubits));
734 }
735
753 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
754 const Types::qubits_vector &qubits, size_t shots = 1000) override {
755 // Results are indexed by positions in the requested list, not by local
756 // register IDs. Convert the input IDs only; preserve the returned order.
757 return simulator->SampleCounts(ConvertQubits(qubits), shots);
758 }
759
773 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
774 const Types::qubits_vector &qubits, size_t shots = 1000) override {
775 return simulator->SampleCountsMany(ConvertQubits(qubits), shots);
776 }
777
789 double ExpectationValue(const std::string &pauliString) override {
790 return simulator->ExpectationValue(pauliString);
791 }
792
800 SimulatorType GetType() const override { return simulator->GetType(); }
801
810 SimulationType GetSimulationType() const override {
811 return SimulationType::kStatevector;
812 }
813
821 void Flush() override { simulator->Flush(); }
822
823 // WARNING: for all the following functions, the call is supposed to be made
824 // after the proper joining (or splitting)!
825
831 void ApplyGenericOneQubitGate(Types::qubit_t qubit,
832 const Eigen::Matrix2cd& gate) override {
833 simulator->ApplyGenericOneQubitGate(qubitsMap[qubit], gate);
834 }
835
842 void ApplyGenericTwoQubitGate(Types::qubit_t qubit0, Types::qubit_t qubit1,
843 const Eigen::Matrix4cd& gate) override {
844 simulator->ApplyGenericTwoQubitGate(qubitsMap[qubit0], qubitsMap[qubit1],
845 gate);
846 }
847
848
856 void ApplyP(Types::qubit_t qubit, double lambda) override {
857 simulator->ApplyP(qubitsMap[qubit], lambda);
858 }
859
866 void ApplyX(Types::qubit_t qubit) override {
867 simulator->ApplyX(qubitsMap[qubit]);
868 }
869
876 void ApplyY(Types::qubit_t qubit) override {
877 simulator->ApplyY(qubitsMap[qubit]);
878 }
879
886 void ApplyZ(Types::qubit_t qubit) override {
887 simulator->ApplyZ(qubitsMap[qubit]);
888 }
889
896 void ApplyH(Types::qubit_t qubit) override {
897 simulator->ApplyH(qubitsMap[qubit]);
898 }
899
906 void ApplyS(Types::qubit_t qubit) override {
907 simulator->ApplyS(qubitsMap[qubit]);
908 }
909
916 void ApplySDG(Types::qubit_t qubit) override {
917 simulator->ApplySDG(qubitsMap[qubit]);
918 }
919
926 void ApplyT(Types::qubit_t qubit) override {
927 simulator->ApplyT(qubitsMap[qubit]);
928 }
929
936 void ApplyTDG(Types::qubit_t qubit) override {
937 simulator->ApplyTDG(qubitsMap[qubit]);
938 }
939
946 void ApplySx(Types::qubit_t qubit) override {
947 simulator->ApplySx(qubitsMap[qubit]);
948 }
949
956 void ApplySxDAG(Types::qubit_t qubit) override {
957 simulator->ApplySxDAG(qubitsMap[qubit]);
958 }
959
966 void ApplyK(Types::qubit_t qubit) override {
967 simulator->ApplyK(qubitsMap[qubit]);
968 }
969
977 void ApplyRx(Types::qubit_t qubit, double theta) override {
978 simulator->ApplyRx(qubitsMap[qubit], theta);
979 }
980
988 void ApplyRy(Types::qubit_t qubit, double theta) override {
989 simulator->ApplyRy(qubitsMap[qubit], theta);
990 }
991
999 void ApplyRz(Types::qubit_t qubit, double theta) override {
1000 simulator->ApplyRz(qubitsMap[qubit], theta);
1001 }
1002
1013 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda,
1014 double gamma) override {
1015 simulator->ApplyU(qubitsMap[qubit], theta, phi, lambda, gamma);
1016 }
1017
1025 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1026 simulator->ApplyCX(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1027 }
1028
1036 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1037 simulator->ApplyCY(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1038 }
1039
1047 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1048 simulator->ApplyCZ(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1049 }
1050
1059 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1060 double lambda) override {
1061 simulator->ApplyCP(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], lambda);
1062 }
1063
1072 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1073 double theta) override {
1074 simulator->ApplyCRx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1075 }
1076
1085 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1086 double theta) override {
1087 simulator->ApplyCRy(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1088 }
1089
1098 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1099 double theta) override {
1100 simulator->ApplyCRz(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1101 }
1102
1110 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1111 simulator->ApplyCH(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1112 }
1113
1121 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1122 simulator->ApplyCSx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1123 }
1124
1132 void ApplyCSxDAG(Types::qubit_t ctrl_qubit,
1133 Types::qubit_t tgt_qubit) override {
1134 simulator->ApplyCSxDAG(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1135 }
1136
1144 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override {
1145 simulator->ApplySwap(qubitsMap[qubit0], qubitsMap[qubit1]);
1146 }
1147
1156 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1,
1157 Types::qubit_t qubit2) override {
1158 simulator->ApplyCCX(qubitsMap[qubit0], qubitsMap[qubit1],
1159 qubitsMap[qubit2]);
1160 }
1161
1170 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0,
1171 Types::qubit_t qubit1) override {
1172 simulator->ApplyCSwap(qubitsMap[ctrl_qubit], qubitsMap[qubit0],
1173 qubitsMap[qubit1]);
1174 }
1175
1187 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1188 double theta, double phi, double lambda, double gamma) override {
1189 simulator->ApplyCU(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta, phi,
1190 lambda, gamma);
1191 }
1192
1193 void ApplyNop() override { simulator->ApplyNop(); }
1194
1203 void SetMultithreading(bool multithreading = true) override {
1204 if (simulator) simulator->SetMultithreading(multithreading);
1205
1206 processor_count =
1207 multithreading ? QC::QubitRegister<>::GetNumberOfThreads() : 1;
1208 }
1209
1217 bool GetMultithreading() const override {
1218 if (simulator) return simulator->GetMultithreading();
1219
1220 return false;
1221 }
1222
1233 bool IsQcsim() const override {
1234 return GetType() == Simulators::SimulatorType::kQCSim;
1235 }
1236
1254 return ConvertOutcomeFromLocal(simulator->MeasureNoCollapse());
1255 }
1256
1271 std::vector<bool> MeasureNoCollapseMany() override {
1272 auto res = simulator->MeasureNoCollapseMany();
1273 return ConvertOutcomeFromLocal(res);
1274 }
1275
1286 std::unique_ptr<ISimulator> Clone() override {
1287 auto cloned = std::make_unique<IndividualSimulator>();
1288
1289 cloned->qubitsMap =
1290 qubitsMap;
1292 cloned->savedState =
1293 savedState;
1295 cloned->simulator = simulator->Clone();
1296
1297 return cloned;
1298 }
1299
1300 Types::qubit_t SampleFromAlias() {
1301 if (!alias || !simulator) return 0;
1302
1303 double prob = 0.0;
1304 if (GetType() == SimulatorType::kQCSim) {
1305 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1306 // there the statevector is accessible)
1307 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1308 prob = 1. - qcsim->uniformZeroOne(qcsim->rng);
1309 }
1310#ifndef NO_QISKIT_AER
1311 else {
1312 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1313 // 'savedAmplitudes' (assumes destructive saving of the state)
1314#ifndef NO_QISKIT_AER
1315 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1316 prob = 1 - aer->uniformZeroOne(aer->rng);
1317#else
1318 return 0;
1319#endif
1320 }
1321#endif
1322
1323 const size_t measRaw = alias->Sample(prob);
1324
1325 return ConvertOutcomeFromLocal(measRaw);
1326 }
1327
1328 const std::unordered_map<std::string, std::string>& GetConfigMap()
1329 const override {
1330 if (!simulator) {
1331 static const std::unordered_map<std::string, std::string> emptyMap;
1332 return emptyMap;
1333 }
1334
1335 return simulator->GetConfigMap();
1336 }
1337
1338 private:
1339 void InitializeAlias() {
1340 // TODO: implement it!
1341 if (GetType() == SimulatorType::kQCSim) {
1342 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1343 // there the statevector is accessible)
1344 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1345
1346 alias = std::unique_ptr<Utils::Alias>(
1347 new Utils::Alias(qcsim->state->getRegisterStorage()));
1348 }
1349#ifndef NO_QISKIT_AER
1350 else {
1351 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1352 // 'savedAmplitudes' (assumes destructive saving of the state)
1353#ifndef NO_QISKIT_AER
1354 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1355 if (aer) {
1356 alias = std::unique_ptr<Utils::Alias>(
1357 new Utils::Alias(aer->savedAmplitudes));
1358 }
1359#else
1360 // If we are here, it means it's not QCSim, but Qiskit is disabled.
1361 throw std::runtime_error("Qiskit Aer is disabled in this build.");
1362#endif
1363 }
1364#endif
1365 }
1366
1367 void ClearAlias() { alias = nullptr; }
1368
1381#ifndef NO_QISKIT_AER
1382 inline void JoinOmpAer(size_t nrQubits1, size_t nrBasisStates1,
1383 size_t nrBasisStates2, size_t newNrQubits,
1384 size_t nrBasisStates,
1385 const std::unique_ptr<IndividualSimulator> &other,
1386 bool enableMultithreading) {
1387 AER::Vector<std::complex<double>> newAmplitudes(
1388 nrBasisStates, false); // the false here avoids data initialization, it
1389 // will be set anyway
1390
1391 /*
1392 const size_t state1Mask = nrBasisStates1 - 1ULL;
1393
1394#pragma omp parallel for num_threads(processor_count) schedule(static,
1395OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1396static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1397AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1398 */
1399
1400 // TODO: check if this is better
1401#pragma omp parallel for num_threads(processor_count)
1402 for (long long int state2 = 0;
1403 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1404 const auto ampl2 = other->AmplitudeRaw(state2);
1405 const size_t state2Mask = state2 << nrQubits1;
1406 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1407 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1408 }
1409
1410 // 3. set the state of the current simulator to the joined state
1411 // the original qubits of this simulator get mapped as they are
1412 // the other ones get shifted to the left by the number of qubits of this
1413 // simulator so transfer mapping keeping this in mind
1414 // simulator->SetMultithreading(enableMultithreading);
1415 simulator->InitializeState(
1416 newNrQubits,
1417 newAmplitudes); // this will move the data from newAmplitudes to the
1418 // simulator, no allocation and copying is done
1419 }
1420#endif
1421
1432 /*
1433 inline void SplitOmpAer(size_t localQubit, size_t newNrQubits, size_t
1434nrBasisStates, bool qubitOutcome = false)
1435 {
1436 // now the adjusted current simulator, without the removed qubit
1437 AER::Vector<std::complex<double>> newAmplitudes(nrBasisStates, false);
1438// the false here avoids data initialization, it will be set anyway
1439
1440 // compute the new amplitudes
1441
1442 const size_t localQubitMask = 1ULL << localQubit;
1443 const size_t maskLow = localQubitMask - 1ULL;
1444 const size_t maskHigh = ~maskLow;
1445 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1446
1447#pragma omp parallel for num_threads(processor_count) schedule(static,
1448OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1449static_cast<long long int>(nrBasisStates); ++state)
1450 {
1451 const size_t stateLow = state & maskLow;
1452 const size_t stateHigh = (state & maskHigh) << 1ULL;
1453
1454 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1455qubitMask);
1456 }
1457
1458 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1459move the data from newAmplitudes to the simulator, no allocation and copying is
1460done
1461 }
1462 */
1463
1476 inline void JoinOmpQcsim(size_t nrQubits1, size_t nrBasisStates1,
1477 size_t nrBasisStates2, size_t newNrQubits,
1478 size_t nrBasisStates,
1479 const std::unique_ptr<IndividualSimulator> &other,
1480 bool enableMultithreading) {
1481 Eigen::VectorXcd newAmplitudes;
1482 newAmplitudes.resize(nrBasisStates);
1483
1484 /*
1485 const size_t state1Mask = nrBasisStates1 - 1ULL;
1486
1487#pragma omp parallel for num_threads(processor_count) schedule(static,
1488OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1489static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1490AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1491 */
1492
1493 // TODO: check if this is better
1494#pragma omp parallel for num_threads(processor_count)
1495 for (long long int state2 = 0;
1496 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1497 const auto ampl2 = other->AmplitudeRaw(state2);
1498 const size_t state2Mask = state2 << nrQubits1;
1499 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1500 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1501 }
1502
1503 // 3. set the state of the current simulator to the joined state
1504 // the original qubits of this simulator get mapped as they are
1505 // the other ones get shifted to the left by the number of qubits of this
1506 // simulator so transfer mapping keeping this in mind
1507 // simulator->SetMultithreading(enableMultithreading);
1508 simulator->InitializeState(
1509 newNrQubits, newAmplitudes); // this will end up by swapping the data
1510 // from newAmplitudes to the simulator, no
1511 // allocation and copying is done
1512 }
1513
1524 /*
1525 inline void SplitOmpQcsim(size_t localQubit, size_t newNrQubits, size_t
1526nrBasisStates, bool qubitOutcome = false)
1527 {
1528 // now the adjusted current simulator, without the removed qubit
1529 Eigen::VectorXcd newAmplitudes;
1530 newAmplitudes.resize(nrBasisStates);
1531
1532 // compute the new amplitudes
1533 const size_t localQubitMask = 1ULL << localQubit;
1534 const size_t maskLow = localQubitMask - 1ULL;
1535 const size_t maskHigh = ~maskLow;
1536 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1537
1538#pragma omp parallel for num_threads(processor_count) schedule(static,
1539OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1540static_cast<long long int>(nrBasisStates); ++state)
1541 {
1542 const size_t stateLow = state & maskLow;
1543 const size_t stateHigh = (state & maskHigh) << 1ULL;
1544
1545 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1546qubitMask);
1547 }
1548
1549 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1550end up by swapping the data from newAmplitudes to the simulator, no allocation
1551and copying is done
1552 }
1553 */
1554
1555 std::unordered_map<Types::qubit_t, Types::qubit_t>
1556 qubitsMap;
1558 std::unique_ptr<ISimulator> simulator;
1559 std::vector<std::complex<double>>
1560 savedState;
1562
1563 std::unique_ptr<Utils::Alias>
1564 alias;
1565
1566 int processor_count =
1567 QC::QubitRegister<>::GetNumberOfThreads();
1570
1571 // constexpr static int divSchedule = 4;
1572 constexpr static size_t OmpLimitJoin = 4096 * 2;
1573 // constexpr static size_t OmpLimitSplit = OmpLimitJoin * 16;
1574};
1575
1576} // namespace Private
1577} // namespace Simulators
1578
1579#endif
1580#endif
int ApplyK(void *sim, int qubit)
double Probability(void *sim, unsigned long long int outcome)
char * GetConfiguration(void *sim, const char *key)
int RestoreState(void *sim)
int ApplyRx(void *sim, int qubit, double theta)
int ApplyReset(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyX(void *sim, int qubit)
int ApplyU(void *sim, int qubit, double theta, double phi, double lambda, double gamma)
int ApplyCRy(void *sim, int controlQubit, int targetQubit, double theta)
int ApplyTDG(void *sim, int qubit)
int ApplyS(void *sim, int qubit)
int ApplyCX(void *sim, int controlQubit, int targetQubit)
unsigned long int AllocateQubits(void *sim, unsigned long int nrQubits)
int ApplyCRz(void *sim, int controlQubit, int targetQubit, double theta)
unsigned long int GetNumberOfQubits(void *sim)
double * AllProbabilities(void *sim)
unsigned long long int MeasureNoCollapse(void *sim)
int ApplyCP(void *sim, int controlQubit, int targetQubit, double theta)
int GetMultithreading(void *sim)
int ApplySDG(void *sim, int qubit)
unsigned long long int Measure(void *sim, const unsigned long int *qubits, unsigned long int nrQubits)
int ApplyCSwap(void *sim, int controlQubit, int qubit1, int qubit2)
int ApplyCCX(void *sim, int controlQubit1, int controlQubit2, int targetQubit)
int ApplyY(void *sim, int qubit)
double * Amplitude(void *sim, unsigned long long int outcome)
int ApplyZ(void *sim, int qubit)
int ApplyH(void *sim, int qubit)
int ApplyCY(void *sim, int controlQubit, int targetQubit)
double * Probabilities(void *sim, const unsigned long long int *qubits, unsigned long int nrQubits)
int SetMultithreading(void *sim, int multithreading)
int ApplyCU(void *sim, int controlQubit, int targetQubit, double theta, double phi, double lambda, double gamma)
int ApplySwap(void *sim, int qubit1, int qubit2)
int ApplyRy(void *sim, int qubit, double theta)
int ApplyP(void *sim, int qubit, double theta)
int SaveStateToInternalDestructive(void *sim)
int ApplyCH(void *sim, int controlQubit, int targetQubit)
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 ApplyCZ(void *sim, int controlQubit, int targetQubit)
int ApplyRz(void *sim, int qubit, double theta)
int RestoreInternalDestructiveSavedState(void *sim)
int ApplyT(void *sim, int qubit)
int ApplyCRx(void *sim, int controlQubit, int targetQubit, double theta)
int IsQcsim(void *sim)
int SaveState(void *sim)
SimulationType
The type of simulation.
Definition State.h:98
@ kStatevector
statevector simulation type
Definition State.h:99
SimulatorType
The type of simulator.
Definition State.h:72
@ kQCSim
qcsim simulator type
Definition State.h:76
@ kQiskitAer
qiskit aer simulator type
Definition State.h:74
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