Maestro 0.1.0
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
41public:
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 and
122 // 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 initialization,
133 // 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 the
150 // 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>
175 Split(size_t qubit, bool qubitOutcome, bool enableMultithreading) {
176 const size_t oldNrQubits = GetNumberOfQubits();
177 const size_t newNrQubits = oldNrQubits - 1;
178 const size_t nrBasisStates = 1ULL << newNrQubits;
179 const size_t localQubit = qubitsMap[qubit];
180
181 // the new simulator split from this one
182 // a one qubit one, containing the qubit that was measured or reset
183 // TODO: this can be optimized a little bit by directly initializing with
184 // the proper amplitudes, but I'm not sure if it's worth it
185 auto newSimulator = std::make_unique<IndividualSimulator>(GetType());
186 newSimulator->AllocateQubits(1);
187 newSimulator->GetQubitsMap()[qubit] =
188 0; // the qubit is mapped to the only local qubit in the new simulator,
189 // which is 0
190 newSimulator->SetMultithreading(enableMultithreading);
191 newSimulator->Initialize();
192 if (qubitOutcome) {
193 newSimulator->ApplyX(qubit);
194 // newSimulator->Flush();
195 }
196
197 qubitsMap.erase(qubit); // the qubit is removed from the current simulator
198
200
201 if (GetType() == SimulatorType::kQCSim) {
202 /*
203 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
204 some bit manipulations, I must do some benchmarks to see if it's worth it
205 and find where the limit is SplitOmpQcsim(localQubit, newNrQubits,
206 nrBasisStates, qubitOutcome); else
207 */
208 {
209 // now the adjusted current simulator, without the removed qubit
210 Eigen::VectorXcd newAmplitudes;
211 newAmplitudes.resize(nrBasisStates);
212
213 // compute the new amplitudes
214
215 const size_t localQubitMask = 1ULL << localQubit;
216 const size_t maskLow = localQubitMask - 1ULL;
217 const size_t maskHigh = ~maskLow;
218 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
219
220 for (size_t state = 0; state < nrBasisStates; ++state) {
221 const size_t stateLow = state & maskLow;
222 const size_t stateHigh = (state & maskHigh) << 1ULL;
223
224 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
225 }
226
227 // simulator->SetMultithreading(enableMultithreading);
228 simulator->InitializeState(
229 newNrQubits,
230 newAmplitudes); // this will end up by swapping the data from
231 // newAmplitudes to the simulator, no allocation and
232 // copying is done
233 }
234 }
235#ifndef NO_QISKIT_AER
236 else {
237 /*
238 if (nrBasisStates > OmpLimitSplit) // parallelization for assignment and
239 some bit manipulations, I must do some benchmarks to see if it's worth it
240 and find where the limit is SplitOmpAer(localQubit, newNrQubits,
241 nrBasisStates, qubitOutcome); else
242 */
243 {
244 // now the adjusted current simulator, without the removed qubit
245 AER::Vector<std::complex<double>> newAmplitudes(
246 nrBasisStates, false); // the false here avoids data initialization,
247 // it will be set anyway
248
249 // compute the new amplitudes
250 const size_t localQubitMask = 1ULL << localQubit;
251 const size_t maskLow = localQubitMask - 1ULL;
252 const size_t maskHigh = ~maskLow;
253 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
254
255 for (size_t state = 0; state < nrBasisStates; ++state) {
256 const size_t stateLow = state & maskLow;
257 const size_t stateHigh = (state & maskHigh) << 1ULL;
258
259 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh | qubitMask);
260 }
261
262 // simulator->SetMultithreading(enableMultithreading);
263 simulator->InitializeState(
264 newNrQubits,
265 newAmplitudes); // this will move the data from newAmplitudes to the
266 // simulator, no allocation and copying is done
267 }
268 }
269#endif
270
271 // now adjust the local qubits map
272 for (auto &mapped : qubitsMap)
273 if (mapped.second > localQubit)
274 --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
325 ConvertQubits(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))
331 converted.emplace_back(qubitsMap[qubit]);
332
333 return converted;
334 }
335
344 inline bool HasQubit(Types::qubit_t qubit) const {
345 return qubitsMap.find(qubit) != qubitsMap.end();
346 }
347
356 inline Types::qubit_t ConvertOutcomeFromLocal(Types::qubit_t outcome) const {
357 Types::qubit_t res = 0;
358
359 for (auto [origQubit, localQubit] : qubitsMap)
360 if (outcome & (1ULL << localQubit))
361 res |= (1ULL << origQubit);
362
363 return res;
364 }
365
374 inline Types::qubit_t ConvertOutcomeFromGlobal(Types::qubit_t outcome) const {
375 Types::qubit_t res = 0;
376
377 for (auto [origQubit, localQubit] : qubitsMap)
378 if (outcome & (1ULL << origQubit))
379 res |= (1ULL << localQubit);
380
381 return res;
382 }
383
389 void SaveState() {
390 if (!simulator)
391 return;
392 const size_t nrBasisStates = 1ULL << simulator->GetNumberOfQubits();
393 savedState.reserve(nrBasisStates);
394
395 for (Types::qubit_t state = 0; state < nrBasisStates; ++state)
396 savedState.emplace_back(simulator->Amplitude(state));
397 }
398
404 void ClearSavedState() { savedState.clear(); }
405
411 void RestoreState() {
412 if (!simulator)
413 return;
414 const size_t nrQubits = simulator->GetNumberOfQubits();
415
416 simulator->Clear();
417 simulator->InitializeState(nrQubits, savedState);
418 ClearSavedState();
419 }
420
429 inline std::unordered_map<Types::qubit_t, Types::qubit_t> &GetQubitsMap() {
430 return qubitsMap;
431 }
432
441 inline const std::unordered_map<Types::qubit_t, Types::qubit_t> &
442 GetQubitsMap() const {
443 return qubitsMap;
444 }
445
452 void Initialize() override { simulator->Initialize(); }
453
466 void InitializeState(size_t num_qubits,
467 std::vector<std::complex<double>> &amplitudes) override {
468 simulator->InitializeState(num_qubits, amplitudes);
469 }
470
483 /*
484 void InitializeState(size_t num_qubits, std::vector<std::complex<double>,
485 avoid_init_allocator<std::complex<double>>>& amplitudes) override
486 {
487 simulator->InitializeState(num_qubits, amplitudes);
488 }
489 */
490
503#ifndef NO_QISKIT_AER
504 void InitializeState(size_t num_qubits,
505 AER::Vector<std::complex<double>> &amplitudes) override {
506 simulator->InitializeState(num_qubits, amplitudes);
507 }
508#endif
509
522 void InitializeState(size_t num_qubits,
523 Eigen::VectorXcd &amplitudes) override {
524 simulator->InitializeState(num_qubits, amplitudes);
525 }
526
535 void Configure(const char *key, const char *value) override {
536 simulator->Configure(key, value);
537 }
538
546 std::string GetConfiguration(const char *key) const override {
547 if (!simulator)
548 return "";
549
550 return simulator->GetConfiguration(key);
551 }
552
560 size_t AllocateQubits(size_t num_qubits) override {
561 return simulator->AllocateQubits(num_qubits);
562 }
563
570 size_t GetNumberOfQubits() const override {
571 return simulator->GetNumberOfQubits();
572 }
573
581 void Clear() override { simulator->Clear(); }
582
592 size_t Measure(const Types::qubits_vector &qubits) override {
593 return ConvertOutcomeFromLocal(simulator->Measure(ConvertQubits(qubits)));
594 }
595
602 void ApplyReset(const Types::qubits_vector &qubits) override {
603 simulator->ApplyReset(ConvertQubits(qubits));
604 }
605
617 double Probability(Types::qubit_t outcome) override {
618 return simulator->Probability(ConvertOutcomeFromGlobal(outcome));
619 }
620
631 std::complex<double> Amplitude(Types::qubit_t outcome) override {
632 return simulator->Amplitude(ConvertOutcomeFromGlobal(outcome));
633 }
634
644 std::vector<double> AllProbabilities() override {
645 return simulator->AllProbabilities();
646 }
647
659 std::vector<double>
660 Probabilities(const Types::qubits_vector &qubits) override {
661 return simulator->Probabilities(ConvertQubits(qubits));
662 }
663
679 std::unordered_map<Types::qubit_t, Types::qubit_t>
681 size_t shots = 1000) override {
682 std::unordered_map<Types::qubit_t, Types::qubit_t> res;
683
684 const auto sc = simulator->SampleCounts(ConvertQubits(qubits), shots);
685
686 for (auto [outcome, count] : sc)
687 res[ConvertOutcomeFromLocal(outcome)] = count;
688
689 return res;
690 }
691
703 double ExpectationValue(const std::string &pauliString) override {
704 return simulator->ExpectationValue(pauliString);
705 }
706
714 SimulatorType GetType() const override { return simulator->GetType(); }
715
724 SimulationType GetSimulationType() const override {
725 return SimulationType::kStatevector;
726 }
727
735 void Flush() override { simulator->Flush(); }
736
737 // WARNING: for all the following functions, the call is supposed to be made
738 // after the proper joining (or splitting)!
739
747 void ApplyP(Types::qubit_t qubit, double lambda) override {
748 simulator->ApplyP(qubitsMap[qubit], lambda);
749 }
750
757 void ApplyX(Types::qubit_t qubit) override {
758 simulator->ApplyX(qubitsMap[qubit]);
759 }
760
767 void ApplyY(Types::qubit_t qubit) override {
768 simulator->ApplyY(qubitsMap[qubit]);
769 }
770
777 void ApplyZ(Types::qubit_t qubit) override {
778 simulator->ApplyZ(qubitsMap[qubit]);
779 }
780
787 void ApplyH(Types::qubit_t qubit) override {
788 simulator->ApplyH(qubitsMap[qubit]);
789 }
790
797 void ApplyS(Types::qubit_t qubit) override {
798 simulator->ApplyS(qubitsMap[qubit]);
799 }
800
807 void ApplySDG(Types::qubit_t qubit) override {
808 simulator->ApplySDG(qubitsMap[qubit]);
809 }
810
817 void ApplyT(Types::qubit_t qubit) override {
818 simulator->ApplyT(qubitsMap[qubit]);
819 }
820
827 void ApplyTDG(Types::qubit_t qubit) override {
828 simulator->ApplyTDG(qubitsMap[qubit]);
829 }
830
837 void ApplySx(Types::qubit_t qubit) override {
838 simulator->ApplySx(qubitsMap[qubit]);
839 }
840
847 void ApplySxDAG(Types::qubit_t qubit) override {
848 simulator->ApplySxDAG(qubitsMap[qubit]);
849 }
850
857 void ApplyK(Types::qubit_t qubit) override {
858 simulator->ApplyK(qubitsMap[qubit]);
859 }
860
868 void ApplyRx(Types::qubit_t qubit, double theta) override {
869 simulator->ApplyRx(qubitsMap[qubit], theta);
870 }
871
879 void ApplyRy(Types::qubit_t qubit, double theta) override {
880 simulator->ApplyRy(qubitsMap[qubit], theta);
881 }
882
890 void ApplyRz(Types::qubit_t qubit, double theta) override {
891 simulator->ApplyRz(qubitsMap[qubit], theta);
892 }
893
904 void ApplyU(Types::qubit_t qubit, double theta, double phi, double lambda,
905 double gamma) override {
906 simulator->ApplyU(qubitsMap[qubit], theta, phi, lambda, gamma);
907 }
908
916 void ApplyCX(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
917 simulator->ApplyCX(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
918 }
919
927 void ApplyCY(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
928 simulator->ApplyCY(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
929 }
930
938 void ApplyCZ(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
939 simulator->ApplyCZ(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
940 }
941
950 void ApplyCP(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
951 double lambda) override {
952 simulator->ApplyCP(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], lambda);
953 }
954
963 void ApplyCRx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
964 double theta) override {
965 simulator->ApplyCRx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
966 }
967
976 void ApplyCRy(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
977 double theta) override {
978 simulator->ApplyCRy(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
979 }
980
989 void ApplyCRz(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
990 double theta) override {
991 simulator->ApplyCRz(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta);
992 }
993
1001 void ApplyCH(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1002 simulator->ApplyCH(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1003 }
1004
1012 void ApplyCSx(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit) override {
1013 simulator->ApplyCSx(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1014 }
1015
1023 void ApplyCSxDAG(Types::qubit_t ctrl_qubit,
1024 Types::qubit_t tgt_qubit) override {
1025 simulator->ApplyCSxDAG(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit]);
1026 }
1027
1035 void ApplySwap(Types::qubit_t qubit0, Types::qubit_t qubit1) override {
1036 simulator->ApplySwap(qubitsMap[qubit0], qubitsMap[qubit1]);
1037 }
1038
1047 void ApplyCCX(Types::qubit_t qubit0, Types::qubit_t qubit1,
1048 Types::qubit_t qubit2) override {
1049 simulator->ApplyCCX(qubitsMap[qubit0], qubitsMap[qubit1],
1050 qubitsMap[qubit2]);
1051 }
1052
1061 void ApplyCSwap(Types::qubit_t ctrl_qubit, Types::qubit_t qubit0,
1062 Types::qubit_t qubit1) override {
1063 simulator->ApplyCSwap(qubitsMap[ctrl_qubit], qubitsMap[qubit0],
1064 qubitsMap[qubit1]);
1065 }
1066
1078 void ApplyCU(Types::qubit_t ctrl_qubit, Types::qubit_t tgt_qubit,
1079 double theta, double phi, double lambda, double gamma) override {
1080 simulator->ApplyCU(qubitsMap[ctrl_qubit], qubitsMap[tgt_qubit], theta, phi,
1081 lambda, gamma);
1082 }
1083
1084 void ApplyNop() override { simulator->ApplyNop(); }
1085
1094 void SetMultithreading(bool multithreading = true) override {
1095 if (simulator)
1096 simulator->SetMultithreading(multithreading);
1097
1098 processor_count =
1099 multithreading ? QC::QubitRegister<>::GetNumberOfThreads() : 1;
1100 }
1101
1109 bool GetMultithreading() const override {
1110 if (simulator)
1111 return simulator->GetMultithreading();
1112
1113 return false;
1114 }
1115
1126 bool IsQcsim() const override {
1127 return GetType() == Simulators::SimulatorType::kQiskitAer;
1128 }
1129
1144 return ConvertOutcomeFromLocal(simulator->MeasureNoCollapse());
1145 }
1146
1157 std::unique_ptr<ISimulator> Clone() override {
1158 auto cloned = std::make_unique<IndividualSimulator>();
1159
1160 cloned->qubitsMap =
1161 qubitsMap;
1163 cloned->savedState =
1164 savedState;
1166 cloned->simulator = simulator->Clone();
1167
1168 return cloned;
1169 }
1170
1171 Types::qubit_t SampleFromAlias() {
1172 if (!alias || !simulator)
1173 return 0;
1174
1175 double prob = 0.0;
1176 if (GetType() == SimulatorType::kQCSim) {
1177 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1178 // there the statevector is accessible)
1179 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1180 prob = 1. - qcsim->uniformZeroOne(qcsim->rng);
1181 } else {
1182 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1183 // 'savedAmplitudes' (assumes destructive saving of the state)
1184 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1185 prob = 1 - aer->uniformZeroOne(aer->rng);
1186 }
1187
1188 const size_t measRaw = alias->Sample(prob);
1189
1190 return ConvertOutcomeFromLocal(measRaw);
1191 }
1192
1193private:
1194 void InitializeAlias() {
1195 // TODO: implement it!
1196 if (GetType() == SimulatorType::kQCSim) {
1197 // qcsim - convert 'simulator' to qcsim simulator and access 'state' (from
1198 // there the statevector is accessible)
1199 QCSimSimulator *qcsim = dynamic_cast<QCSimSimulator *>(simulator.get());
1200
1201 alias = std::unique_ptr<Utils::Alias>(
1202 new Utils::Alias(qcsim->state->getRegisterStorage()));
1203 } else {
1204 // qiskit aer - convert 'simulator' to qiskit aer simulator and access
1205 // 'savedAmplitudes' (assumes destructive saving of the state)
1206 AerSimulator *aer = dynamic_cast<AerSimulator *>(simulator.get());
1207
1208 alias =
1209 std::unique_ptr<Utils::Alias>(new Utils::Alias(aer->savedAmplitudes));
1210 }
1211 }
1212
1213 void ClearAlias() { alias = nullptr; }
1214
1227#ifndef NO_QISKIT_AER
1228 inline void JoinOmpAer(size_t nrQubits1, size_t nrBasisStates1,
1229 size_t nrBasisStates2, size_t newNrQubits,
1230 size_t nrBasisStates,
1231 const std::unique_ptr<IndividualSimulator> &other,
1232 bool enableMultithreading) {
1233 AER::Vector<std::complex<double>> newAmplitudes(
1234 nrBasisStates, false); // the false here avoids data initialization, it
1235 // will be set anyway
1236
1237 /*
1238 const size_t state1Mask = nrBasisStates1 - 1ULL;
1239
1240#pragma omp parallel for num_threads(processor_count) schedule(static,
1241OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1242static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1243AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1244 */
1245
1246 // TODO: check if this is better
1247#pragma omp parallel for num_threads(processor_count)
1248 for (long long int state2 = 0;
1249 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1250 const auto ampl2 = other->AmplitudeRaw(state2);
1251 const size_t state2Mask = state2 << nrQubits1;
1252 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1253 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1254 }
1255
1256 // 3. set the state of the current simulator to the joined state
1257 // the original qubits of this simulator get mapped as they are
1258 // the other ones get shifted to the left by the number of qubits of this
1259 // simulator so transfer mapping keeping this in mind
1260 // simulator->SetMultithreading(enableMultithreading);
1261 simulator->InitializeState(
1262 newNrQubits,
1263 newAmplitudes); // this will move the data from newAmplitudes to the
1264 // simulator, no allocation and copying is done
1265 }
1266#endif
1267
1278 /*
1279 inline void SplitOmpAer(size_t localQubit, size_t newNrQubits, size_t
1280nrBasisStates, bool qubitOutcome = false)
1281 {
1282 // now the adjusted current simulator, without the removed qubit
1283 AER::Vector<std::complex<double>> newAmplitudes(nrBasisStates, false);
1284// the false here avoids data initialization, it will be set anyway
1285
1286 // compute the new amplitudes
1287
1288 const size_t localQubitMask = 1ULL << localQubit;
1289 const size_t maskLow = localQubitMask - 1ULL;
1290 const size_t maskHigh = ~maskLow;
1291 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1292
1293#pragma omp parallel for num_threads(processor_count) schedule(static,
1294OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1295static_cast<long long int>(nrBasisStates); ++state)
1296 {
1297 const size_t stateLow = state & maskLow;
1298 const size_t stateHigh = (state & maskHigh) << 1ULL;
1299
1300 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1301qubitMask);
1302 }
1303
1304 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1305move the data from newAmplitudes to the simulator, no allocation and copying is
1306done
1307 }
1308 */
1309
1322 inline void JoinOmpQcsim(size_t nrQubits1, size_t nrBasisStates1,
1323 size_t nrBasisStates2, size_t newNrQubits,
1324 size_t nrBasisStates,
1325 const std::unique_ptr<IndividualSimulator> &other,
1326 bool enableMultithreading) {
1327 Eigen::VectorXcd newAmplitudes;
1328 newAmplitudes.resize(nrBasisStates);
1329
1330 /*
1331 const size_t state1Mask = nrBasisStates1 - 1ULL;
1332
1333#pragma omp parallel for num_threads(processor_count) schedule(static,
1334OmpLimitJoin / divSchedule) for (long long int state = 0; state <
1335static_cast<long long int>(nrBasisStates); ++state) newAmplitudes[state] =
1336AmplitudeRaw(state & state1Mask) * other->AmplitudeRaw(state >> nrQubits1);
1337 */
1338
1339 // TODO: check if this is better
1340#pragma omp parallel for num_threads(processor_count)
1341 for (long long int state2 = 0;
1342 state2 < static_cast<long long int>(nrBasisStates2); ++state2) {
1343 const auto ampl2 = other->AmplitudeRaw(state2);
1344 const size_t state2Mask = state2 << nrQubits1;
1345 for (size_t state1 = 0; state1 < nrBasisStates1; ++state1)
1346 newAmplitudes[state2Mask | state1] = AmplitudeRaw(state1) * ampl2;
1347 }
1348
1349 // 3. set the state of the current simulator to the joined state
1350 // the original qubits of this simulator get mapped as they are
1351 // the other ones get shifted to the left by the number of qubits of this
1352 // simulator so transfer mapping keeping this in mind
1353 // simulator->SetMultithreading(enableMultithreading);
1354 simulator->InitializeState(
1355 newNrQubits, newAmplitudes); // this will end up by swapping the data
1356 // from newAmplitudes to the simulator, no
1357 // allocation and copying is done
1358 }
1359
1370 /*
1371 inline void SplitOmpQcsim(size_t localQubit, size_t newNrQubits, size_t
1372nrBasisStates, bool qubitOutcome = false)
1373 {
1374 // now the adjusted current simulator, without the removed qubit
1375 Eigen::VectorXcd newAmplitudes;
1376 newAmplitudes.resize(nrBasisStates);
1377
1378 // compute the new amplitudes
1379 const size_t localQubitMask = 1ULL << localQubit;
1380 const size_t maskLow = localQubitMask - 1ULL;
1381 const size_t maskHigh = ~maskLow;
1382 const size_t qubitMask = qubitOutcome ? localQubitMask : 0ULL;
1383
1384#pragma omp parallel for num_threads(processor_count) schedule(static,
1385OmpLimitSplit / divSchedule) for (long long int state = 0; state <
1386static_cast<long long int>(nrBasisStates); ++state)
1387 {
1388 const size_t stateLow = state & maskLow;
1389 const size_t stateHigh = (state & maskHigh) << 1ULL;
1390
1391 newAmplitudes[state] = AmplitudeRaw(stateLow | stateHigh |
1392qubitMask);
1393 }
1394
1395 simulator->InitializeState(newNrQubits, newAmplitudes); // this will
1396end up by swapping the data from newAmplitudes to the simulator, no allocation
1397and copying is done
1398 }
1399 */
1400
1401 std::unordered_map<Types::qubit_t, Types::qubit_t>
1402 qubitsMap;
1404 std::unique_ptr<ISimulator> simulator;
1405 std::vector<std::complex<double>>
1406 savedState;
1408
1409 std::unique_ptr<Utils::Alias>
1410 alias;
1411
1412 int processor_count =
1413 QC::QubitRegister<>::GetNumberOfThreads();
1416
1417 // constexpr static int divSchedule = 4;
1418 constexpr static size_t OmpLimitJoin = 4096 * 2;
1419 // constexpr static size_t OmpLimitSplit = OmpLimitJoin * 16;
1420};
1421
1422} // namespace Private
1423} // namespace Simulators
1424
1425#endif
1426#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:82
@ kStatevector
statevector simulation type
Definition State.h:83
SimulatorType
The type of simulator.
Definition State.h:63
@ kQCSim
qcsim simulator type
Definition State.h:67
@ kQiskitAer
qiskit aer simulator type
Definition State.h:65
std::vector< qubit_t > qubits_vector
The type of a vector of qubits.
Definition Types.h:21
uint_fast64_t qubit_t
The type of a qubit.
Definition Types.h:20