Maestro 0.2.5
Unified interface for quantum circuit simulation
Loading...
Searching...
No Matches
Individual.h
Go to the documentation of this file.
1
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 newSimulator->Initialize();
193 if (qubitOutcome) {
194 newSimulator->ApplyX(qubit);
195 // newSimulator->Flush();
196 }
197
198 qubitsMap.erase(qubit); // the qubit is removed from the current simulator
199
201
202 if (GetType() == SimulatorType::kQCSim) {
203 /*
204 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
205 some bit manipulations, I must do some benchmarks to see if it's worth it
206 and find where the limit is SplitOmpQcsim(localQubit, newNrQubits,
207 nrBasisStates, qubitOutcome); else
208 */
209 {
210 // now the adjusted current simulator, without the removed qubit
211 Eigen::VectorXcd newAmplitudes;
212 newAmplitudes.resize(nrBasisStates);
213
214 // compute the new amplitudes
215
216 const size_t localQubitMask = 1ULL << localQubit;
217 const size_t maskLow = localQubitMask - 1ULL;
218 const size_t maskHigh = ~maskLow;
219 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
220
221 for (size_t state = 0; state < nrBasisStates; ++state) {
222 const size_t stateLow = state & maskLow;
223 const size_t stateHigh = (state & maskHigh) << 1ULL;
224
225 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
226 }
227
228 // simulator->SetMultithreading(enableMultithreading);
229 simulator->InitializeState(
230 newNrQubits,
231 newAmplitudes); // this will end up by swapping the data from
232 // newAmplitudes to the simulator, no allocation
233 // and copying is done
234 }
235 }
236#ifndef NO_QISKIT_AER
237 else {
238 /*
239 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
240 some bit manipulations, I must do some benchmarks to see if it's worth it
241 and find where the limit is SplitOmpAer(localQubit, newNrQubits,
242 nrBasisStates, qubitOutcome); else
243 */
244 {
245 // now the adjusted current simulator, without the removed qubit
246 AER::Vector<std::complex<double>> newAmplitudes(
247 nrBasisStates, false); // the false here avoids data
248 // initialization, it will be set anyway
249
250 // compute the new amplitudes
251 const size_t localQubitMask = 1ULL << localQubit;
252 const size_t maskLow = localQubitMask - 1ULL;
253 const size_t maskHigh = ~maskLow;
254 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
255
256 for (size_t state = 0; state < nrBasisStates; ++state) {
257 const size_t stateLow = state & maskLow;
258 const size_t stateHigh = (state & maskHigh) << 1ULL;
259
260 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
261 }
262
263 // simulator->SetMultithreading(enableMultithreading);
264 simulator->InitializeState(
265 newNrQubits,
266 newAmplitudes); // this will move the data from newAmplitudes to
267 // the simulator, no allocation and copying is done
268 }
269 }
270#endif
271
272 // now adjust the local qubits map
273 for (auto &mapped : qubitsMap)
274 if (mapped.second > localQubit) --mapped.second;
275
276 return newSimulator;
277 }
278
288 std::complex<double> AmplitudeRaw(Types::qubit_t outcome) override {
289 return simulator->AmplitudeRaw(outcome);
290 }
291
301 void SaveStateToInternalDestructive() override {
302 simulator->SaveStateToInternalDestructive();
303 }
304
312 simulator->RestoreInternalDestructiveSavedState();
313 }
314
324 inline Types::qubits_vector ConvertQubits(
325 const Types::qubits_vector &qubits) {
326 Types::qubits_vector converted;
327 converted.reserve(qubits.size());
328
329 for (auto qubit : qubits)
330 if (HasQubit(qubit)) converted.emplace_back(qubitsMap[qubit]);
331
332 return converted;
333 }
334
343 inline bool HasQubit(Types::qubit_t qubit) const {
344 return qubitsMap.find(qubit) != qubitsMap.end();
345 }
346
355 inline Types::qubit_t ConvertOutcomeFromLocal(Types::qubit_t outcome) const {
356 Types::qubit_t res = 0;
357
358 for (auto [origQubit, localQubit] : qubitsMap)
359 if (outcome & (1ULL << localQubit)) res |= (1ULL << origQubit);
360
361 return res;
362 }
363
372 inline Types::qubit_t ConvertOutcomeFromGlobal(Types::qubit_t outcome) const {
373 Types::qubit_t res = 0;
374
375 for (auto [origQubit, localQubit] : qubitsMap)
376 if (outcome & (1ULL << origQubit)) res |= (1ULL << localQubit);
377
378 return res;
379 }
380
389 inline std::vector<bool> ConvertOutcomeFromLocal(
390 const std::vector<bool> &outcome) const {
391 std::vector<bool> res;
392
393 size_t maxQubit = 0;
394 for (auto [origQubit, localQubit] : qubitsMap)
395 if (origQubit > maxQubit) maxQubit = origQubit;
396 res.resize(maxQubit + 1, false);
397
398 for (auto [origQubit, localQubit] : qubitsMap)
399 if (outcome[localQubit]) res[origQubit] = true;
400
401 return res;
402 }
403
412 inline std::vector<bool> ConvertOutcomeFromGlobal(
413 const std::vector<bool> &outcome) const {
414 const size_t nrQubits = simulator->GetNumberOfQubits();
415 std::vector<bool> res(nrQubits, false);
416
417 for (auto [origQubit, localQubit] : qubitsMap)
418 if (outcome[origQubit]) res[localQubit] = true;
419
420 return res;
421 }
422
428 void SaveState() override {
429 if (!simulator) return;
430 const size_t nrBasisStates = 1ULL << simulator->GetNumberOfQubits();
431 savedState.reserve(nrBasisStates);
432
433 for (Types::qubit_t state = 0; state < nrBasisStates; ++state)
434 savedState.emplace_back(simulator->Amplitude(state));
435 }
436
442 void ClearSavedState() { savedState.clear(); }
443
449 void RestoreState() override {
450 if (!simulator) return;
451 const size_t nrQubits = simulator->GetNumberOfQubits();
452
453 simulator->Clear();
454 simulator->InitializeState(nrQubits, savedState);
455 ClearSavedState();
456 }
457
466 inline std::unordered_map<Types::qubit_t, Types::qubit_t> &GetQubitsMap() {
467 return qubitsMap;
468 }
469
478 inline const std::unordered_map<Types::qubit_t, Types::qubit_t> &
479 GetQubitsMap() const {
480 return qubitsMap;
481 }
482
489 void Initialize() override { simulator->Initialize(); }
490
503 void InitializeState(size_t num_qubits,
504 std::vector<std::complex<double>> &amplitudes) override {
505 simulator->InitializeState(num_qubits, amplitudes);
506 }
507
520 /*
521 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
522 avoid_init_allocator<std::complex<double>>>& amplitudes) override
523 {
524 simulator->InitializeState(num_qubits, amplitudes);
525 }
526 */
527
540#ifndef NO_QISKIT_AER
541 void InitializeState(size_t num_qubits,
542 AER::Vector<std::complex<double>> &amplitudes) override {
543 simulator->InitializeState(num_qubits, amplitudes);
544 }
545#endif
546
559 void InitializeState(size_t num_qubits,
560 Eigen::VectorXcd &amplitudes) override {
561 simulator->InitializeState(num_qubits, amplitudes);
562 }
563
572 void Configure(const char *key, const char *value) override {
573 simulator->Configure(key, value);
574 }
575
583 std::string GetConfiguration(const char *key) const override {
584 if (!simulator) return "";
585
586 return simulator->GetConfiguration(key);
587 }
588
596 size_t AllocateQubits(size_t num_qubits) override {
597 return simulator->AllocateQubits(num_qubits);
598 }
599
606 size_t GetNumberOfQubits() const override {
607 return simulator->GetNumberOfQubits();
608 }
609
617 void Clear() override { simulator->Clear(); }
618
630 size_t Measure(const Types::qubits_vector &qubits) override {
631 return ConvertOutcomeFromLocal(simulator->Measure(ConvertQubits(qubits)));
632 }
633
642 std::vector<bool> MeasureMany(const Types::qubits_vector &qubits) override {
643 return ConvertOutcomeFromLocal(
644 simulator->MeasureMany(ConvertQubits(qubits)));
645 }
646
653 void ApplyReset(const Types::qubits_vector &qubits) override {
654 simulator->ApplyReset(ConvertQubits(qubits));
655 }
656
668 double Probability(Types::qubit_t outcome) override {
669 return simulator->Probability(ConvertOutcomeFromGlobal(outcome));
670 }
671
682 std::complex<double> Amplitude(Types::qubit_t outcome) override {
683 return simulator->Amplitude(ConvertOutcomeFromGlobal(outcome));
684 }
685
699 std::complex<double> ProjectOnZero() override {
700 return Amplitude(0);
701 }
702
712 std::vector<double> AllProbabilities() override {
713 return simulator->AllProbabilities();
714 }
715
727 std::vector<double> Probabilities(
728 const Types::qubits_vector &qubits) override {
729 return simulator->Probabilities(ConvertQubits(qubits));
730 }
731
749 std::unordered_map<Types::qubit_t, Types::qubit_t> SampleCounts(
750 const Types::qubits_vector &qubits, size_t shots = 1000) override {
751 std::unordered_map<Types::qubit_t, Types::qubit_t> res;
752
753 const auto sc = simulator->SampleCounts(ConvertQubits(qubits), shots);
754
755 for (auto [outcome, count] : sc)
756 res[ConvertOutcomeFromLocal(outcome)] += count;
757
758 return res;
759 }
760
774 std::unordered_map<std::vector<bool>, Types::qubit_t> SampleCountsMany(
775 const Types::qubits_vector &qubits, size_t shots = 1000) override {
776 std::unordered_map<std::vector<bool>, Types::qubit_t> res;
777
778 const auto sc = simulator->SampleCountsMany(ConvertQubits(qubits), shots);
779
780 for (auto [outcome, count] : sc)
781 res[ConvertOutcomeFromLocal(outcome)] = count;
782
783 return res;
784 }
785
797 double ExpectationValue(const std::string &pauliString) override {
798 return simulator->ExpectationValue(pauliString);
799 }
800
808 SimulatorType GetType() const override { return simulator->GetType(); }
809
818 SimulationType GetSimulationType() const override {
819 return SimulationType::kStatevector;
820 }
821
829 void Flush() override { simulator->Flush(); }
830
831 // WARNING: for all the following functions, the call is supposed to be made
832 // after the proper joining (or splitting)!
833
841 void ApplyP(Types::qubit_t qubit, double lambda) override {
842 simulator->ApplyP(qubitsMap[qubit], lambda);
843 }
844
851 void ApplyX(Types::qubit_t qubit) override {
852 simulator->ApplyX(qubitsMap[qubit]);
853 }
854
861 void ApplyY(Types::qubit_t qubit) override {
862 simulator->ApplyY(qubitsMap[qubit]);
863 }
864
871 void ApplyZ(Types::qubit_t qubit) override {
872 simulator->ApplyZ(qubitsMap[qubit]);
873 }
874
881 void ApplyH(Types::qubit_t qubit) override {
882 simulator->ApplyH(qubitsMap[qubit]);
883 }
884
891 void ApplyS(Types::qubit_t qubit) override {
892 simulator->ApplyS(qubitsMap[qubit]);
893 }
894
901 void ApplySDG(Types::qubit_t qubit) override {
902 simulator->ApplySDG(qubitsMap[qubit]);
903 }
904
911 void ApplyT(Types::qubit_t qubit) override {
912 simulator->ApplyT(qubitsMap[qubit]);
913 }
914
921 void ApplyTDG(Types::qubit_t qubit) override {
922 simulator->ApplyTDG(qubitsMap[qubit]);
923 }
924
931 void ApplySx(Types::qubit_t qubit) override {
932 simulator->ApplySx(qubitsMap[qubit]);
933 }
934
941 void ApplySxDAG(Types::qubit_t qubit) override {
942 simulator->ApplySxDAG(qubitsMap[qubit]);
943 }
944
951 void ApplyK(Types::qubit_t qubit) override {
952 simulator->ApplyK(qubitsMap[qubit]);
953 }
954
962 void ApplyRx(Types::qubit_t qubit, double theta) override {
963 simulator->ApplyRx(qubitsMap[qubit], theta);
964 }
965
973 void ApplyRy(Types::qubit_t qubit, double theta) override {
974 simulator->ApplyRy(qubitsMap[qubit], theta);
975 }
976
984 void ApplyRz(Types::qubit_t qubit, double theta) override {
985 simulator->ApplyRz(qubitsMap[qubit], theta);
986 }
987
998 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda,
999 double gamma) override {
1000 simulator->ApplyU(qubitsMap[qubit], theta, phi, lambda, gamma);
1001 }
1002
1010 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1011 simulator->ApplyCX(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1012 }
1013
1021 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1022 simulator->ApplyCY(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1023 }
1024
1032 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1033 simulator->ApplyCZ(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1034 }
1035
1044 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1045 double lambda) override {
1046 simulator->ApplyCP(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], lambda);
1047 }
1048
1057 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1058 double theta) override {
1059 simulator->ApplyCRx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1060 }
1061
1070 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1071 double theta) override {
1072 simulator->ApplyCRy(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1073 }
1074
1083 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1084 double theta) override {
1085 simulator->ApplyCRz(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
1086 }
1087
1095 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1096 simulator->ApplyCH(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1097 }
1098
1106 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1107 simulator->ApplyCSx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1108 }
1109
1117 void ApplyCSxDAG(Types::qubit_t ctrl_qubit,
1118 Types::qubit_t tgt_qubit) override {
1119 simulator->ApplyCSxDAG(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1120 }
1121
1129 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override {
1130 simulator->ApplySwap(qubitsMap[qubit0], qubitsMap[qubit1]);
1131 }
1132
1141 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1,
1142 Types::qubit_t qubit2) override {
1143 simulator->ApplyCCX(qubitsMap[qubit0], qubitsMap[qubit1],
1144 qubitsMap[qubit2]);
1145 }
1146
1155 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0,
1156 Types::qubit_t qubit1) override {
1157 simulator->ApplyCSwap(qubitsMap[ctrl_qubit], qubitsMap[qubit0],
1158 qubitsMap[qubit1]);
1159 }
1160
1172 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1173 double theta, double phi, double lambda, double gamma) override {
1174 simulator->ApplyCU(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta, phi,
1175 lambda, gamma);
1176 }
1177
1178 void ApplyNop() override { simulator->ApplyNop(); }
1179
1188 void SetMultithreading(bool multithreading = true) override {
1189 if (simulator) simulator->SetMultithreading(multithreading);
1190
1191 processor_count =
1192 multithreading ? QC::QubitRegister<>::GetNumberOfThreads() : 1;
1193 }
1194
1202 bool GetMultithreading() const override {
1203 if (simulator) return simulator->GetMultithreading();
1204
1205 return false;
1206 }
1207
1218 bool IsQcsim() const override {
1219 return GetType() == Simulators::SimulatorType::kQCSim;
1220 }
1221
1239 return ConvertOutcomeFromLocal(simulator->MeasureNoCollapse());
1240 }
1241
1256 std::vector<bool> MeasureNoCollapseMany() override {
1257 auto res = simulator->MeasureNoCollapseMany();
1258 return ConvertOutcomeFromLocal(res);
1259 }
1260
1271 std::unique_ptr<ISimulator> Clone() override {
1272 auto cloned = std::make_unique<IndividualSimulator>();
1273
1274 cloned->qubitsMap =
1275 qubitsMap;
1277 cloned->savedState =
1278 savedState;
1280 cloned->simulator = simulator->Clone();
1282 return cloned;
1283 }
1284
1285 Types::qubit_t SampleFromAlias() {
1286 if (!alias || !simulator) return 0;
1287
1288 double prob = 0.0;
1289 if (GetType() == SimulatorType::kQCSim) {
1290 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1291 // there the statevector is accessible)
1292 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1293 prob = 1. - qcsim->uniformZeroOne(qcsim->rng);
1294 }
1295#ifndef NO_QISKIT_AER
1296 else {
1297 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1298 // 'savedAmplitudes' (assumes destructive saving of the state)
1299#ifndef NO_QISKIT_AER
1300 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1301 prob = 1 - aer->uniformZeroOne(aer->rng);
1302#else
1303 return 0;
1304#endif
1305 }
1306#endif
1307
1308 const size_t measRaw = alias->Sample(prob);
1309
1310 return ConvertOutcomeFromLocal(measRaw);
1311 }
1312
1313 private:
1314 void InitializeAlias() {
1315 // TODO: implement it!
1316 if (GetType() == SimulatorType::kQCSim) {
1317 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1318 // there the statevector is accessible)
1319 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1320
1321 alias = std::unique_ptr<Utils::Alias>(
1322 new Utils::Alias(qcsim->state->getRegisterStorage()));
1323 }
1324#ifndef NO_QISKIT_AER
1325 else {
1326 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1327 // 'savedAmplitudes' (assumes destructive saving of the state)
1328#ifndef NO_QISKIT_AER
1329 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1330 if (aer) {
1331 alias = std::unique_ptr<Utils::Alias>(
1332 new Utils::Alias(aer->savedAmplitudes));
1333 }
1334#else
1335 // If we are here, it means it's not QCSim, but Qiskit is disabled.
1336 throw std::runtime_error("Qiskit Aer is disabled in this build.");
1337#endif
1338 }
1339#endif
1340 }
1341
1342 void ClearAlias() { alias = nullptr; }
1343
1356#ifndef NO_QISKIT_AER
1357 inline void JoinOmpAer(size_t nrQubits1, size_t nrBasisStates1,
1358 size_t nrBasisStates2, size_t newNrQubits,
1359 size_t nrBasisStates,
1360 const std::unique_ptr<IndividualSimulator> &other,
1361 bool enableMultithreading) {
1362 AER::Vector<std::complex<double>> newAmplitudes(
1363 nrBasisStates, false); // the false here avoids data initialization, it
1364 // will be set anyway
1365
1366 /*
1367 const size_t state1Mask = nrBasisStates1 - 1ULL;
1368
1369#pragma omp parallel for num_threads(processor_count) schedule(static,
1370OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1371static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1372AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1373 */
1374
1375 // TODO: check if this is better
1376#pragma omp parallel for num_threads(processor_count)
1377 for (long long int state2 = 0;
1378 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1379 const auto ampl2 = other->AmplitudeRaw(state2);
1380 const size_t state2Mask = state2 << nrQubits1;
1381 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1382 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1383 }
1384
1385 // 3. set the state of the current simulator to the joined state
1386 // the original qubits of this simulator get mapped as they are
1387 // the other ones get shifted to the left by the number of qubits of this
1388 // simulator so transfer mapping keeping this in mind
1389 // simulator->SetMultithreading(enableMultithreading);
1390 simulator->InitializeState(
1391 newNrQubits,
1392 newAmplitudes); // this will move the data from newAmplitudes to the
1393 // simulator, no allocation and copying is done
1394 }
1395#endif
1396
1407 /*
1408 inline void SplitOmpAer(size_t localQubit, size_t newNrQubits, size_t
1409nrBasisStates, bool qubitOutcome = false)
1410 {
1411 // now the adjusted current simulator, without the removed qubit
1412 AER::Vector<std::complex<double>> newAmplitudes(nrBasisStates, false);
1413// the false here avoids data initialization, it will be set anyway
1414
1415 // compute the new amplitudes
1416
1417 const size_t localQubitMask = 1ULL << localQubit;
1418 const size_t maskLow = localQubitMask - 1ULL;
1419 const size_t maskHigh = ~maskLow;
1420 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1421
1422#pragma omp parallel for num_threads(processor_count) schedule(static,
1423OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1424static_cast<long long int>(nrBasisStates); ++state)
1425 {
1426 const size_t stateLow = state & maskLow;
1427 const size_t stateHigh = (state & maskHigh) << 1ULL;
1428
1429 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1430qubitMask);
1431 }
1432
1433 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1434move the data from newAmplitudes to the simulator, no allocation and copying is
1435done
1436 }
1437 */
1438
1451 inline void JoinOmpQcsim(size_t nrQubits1, size_t nrBasisStates1,
1452 size_t nrBasisStates2, size_t newNrQubits,
1453 size_t nrBasisStates,
1454 const std::unique_ptr<IndividualSimulator> &other,
1455 bool enableMultithreading) {
1456 Eigen::VectorXcd newAmplitudes;
1457 newAmplitudes.resize(nrBasisStates);
1458
1459 /*
1460 const size_t state1Mask = nrBasisStates1 - 1ULL;
1461
1462#pragma omp parallel for num_threads(processor_count) schedule(static,
1463OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1464static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1465AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1466 */
1467
1468 // TODO: check if this is better
1469#pragma omp parallel for num_threads(processor_count)
1470 for (long long int state2 = 0;
1471 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1472 const auto ampl2 = other->AmplitudeRaw(state2);
1473 const size_t state2Mask = state2 << nrQubits1;
1474 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1475 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1476 }
1477
1478 // 3. set the state of the current simulator to the joined state
1479 // the original qubits of this simulator get mapped as they are
1480 // the other ones get shifted to the left by the number of qubits of this
1481 // simulator so transfer mapping keeping this in mind
1482 // simulator->SetMultithreading(enableMultithreading);
1483 simulator->InitializeState(
1484 newNrQubits, newAmplitudes); // this will end up by swapping the data
1485 // from newAmplitudes to the simulator, no
1486 // allocation and copying is done
1487 }
1488
1499 /*
1500 inline void SplitOmpQcsim(size_t localQubit, size_t newNrQubits, size_t
1501nrBasisStates, bool qubitOutcome = false)
1502 {
1503 // now the adjusted current simulator, without the removed qubit
1504 Eigen::VectorXcd newAmplitudes;
1505 newAmplitudes.resize(nrBasisStates);
1506
1507 // compute the new amplitudes
1508 const size_t localQubitMask = 1ULL << localQubit;
1509 const size_t maskLow = localQubitMask - 1ULL;
1510 const size_t maskHigh = ~maskLow;
1511 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1512
1513#pragma omp parallel for num_threads(processor_count) schedule(static,
1514OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1515static_cast<long long int>(nrBasisStates); ++state)
1516 {
1517 const size_t stateLow = state & maskLow;
1518 const size_t stateHigh = (state & maskHigh) << 1ULL;
1519
1520 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1521qubitMask);
1522 }
1523
1524 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1525end up by swapping the data from newAmplitudes to the simulator, no allocation
1526and copying is done
1527 }
1528 */
1529
1530 std::unordered_map<Types::qubit_t, Types::qubit_t>
1531 qubitsMap;
1533 std::unique_ptr<ISimulator> simulator;
1534 std::vector<std::complex<double>>
1535 savedState;
1538 std::unique_ptr<Utils::Alias>
1539 alias;
1541 int processor_count =
1542 QC::QubitRegister<>::GetNumberOfThreads();
1546 // constexpr static int divSchedule = 4;
1547 constexpr static size_t OmpLimitJoin = 4096 * 2;
1548 // constexpr static size_t OmpLimitSplit = OmpLimitJoin * 16;
1549};
1550
1551} // namespace Private
1552} // namespace Simulators
1553
1554#endif
1555#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:85
@ kStatevector
statevector simulation type
SimulatorType
The type of simulator.
Definition State.h:68
@ kQCSim
qcsim simulator type
@ kQiskitAer
qiskit aer simulator type
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