Qrack  10.0
General classical-emulating-quantum development framework
statevector.hpp
Go to the documentation of this file.
1 //
3 // (C) Daniel Strano and the Qrack contributors 2017-2023. All rights reserved.
4 //
5 // This header defines buffers for Qrack::QFusion.
6 // QFusion adds an optional "gate fusion" layer on top of a QEngine or QUnit.
7 // Single bit gates are buffered in per-bit 2x2 complex matrices, to reduce the cost
8 // of successive application of single bit gates to the same bit.
9 //
10 // Licensed under the GNU Lesser General Public License V3.
11 // See LICENSE.md in the project root or https://www.gnu.org/licenses/lgpl-3.0.en.html
12 // for details.
13 
14 #pragma once
15 
16 #include "common/parallel_for.hpp"
17 
18 #include <algorithm>
19 #include <mutex>
20 #include <numeric>
21 #include <random>
22 #include <set>
23 
24 #ifdef ENABLE_PTHREAD
25 #include <future>
26 #endif
27 
28 #if ENABLE_CPP_INT && (QBCAPPOW > 6) && BOOST_AVAILABLE
29 #include <map>
30 #define SparseStateVecMap std::map<bitCapInt, complex>
31 #else
32 #include <unordered_map>
33 #define SparseStateVecMap std::unordered_map<bitCapInt, complex>
34 #endif
35 
36 #if ENABLE_COMPLEX_X2
37 #if FPPOW == 5
39 #elif FPPOW == 6
41 #endif
42 #endif
43 
44 namespace Qrack {
45 
46 class StateVectorArray;
47 class StateVectorSparse;
48 
49 // This is a buffer struct that's capable of representing controlled single bit gates and arithmetic, when subclassed.
50 class StateVector : public ParallelFor {
51 protected:
53 
54 public:
56 
58  : capacity(cap)
59  , isReadLocked(true)
60  {
61  }
62  virtual ~StateVector()
63  {
64  // Intentionally left blank.
65  }
66 
67  virtual complex read(const bitCapIntOcl& i) = 0;
68  virtual complex read(const bitCapInt& i) = 0;
69 #if ENABLE_COMPLEX_X2
70  virtual complex2 read2(const bitCapIntOcl& i1, const bitCapIntOcl& i2) = 0;
71  virtual complex2 read2(const bitCapInt& i1, const bitCapInt& i2) = 0;
72 #endif
73  virtual void write(const bitCapIntOcl& i, const complex& c) = 0;
74  virtual void write(const bitCapInt& i, const complex& c) = 0;
77  virtual void write2(const bitCapIntOcl& i1, const complex& c1, const bitCapIntOcl& i2, const complex& c2) = 0;
78  virtual void write2(const bitCapInt& i1, const complex& c1, const bitCapInt& i2, const complex& c2) = 0;
79  virtual void clear() = 0;
80  virtual void copy_in(const complex* inArray) = 0;
81  virtual void copy_in(const complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length) = 0;
82  virtual void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset,
83  const bitCapIntOcl length) = 0;
84  virtual void copy_out(complex* outArray) = 0;
85  virtual void copy_out(complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length) = 0;
86  virtual void copy(StateVectorPtr toCopy) = 0;
87  virtual void shuffle(StateVectorPtr svp) = 0;
88  virtual void get_probs(real1* outArray) = 0;
89  virtual bool is_sparse() = 0;
90 };
91 
92 class StateVectorArray : public StateVector {
93 public:
94  std::unique_ptr<complex[], void (*)(complex*)> amplitudes;
95 
96 protected:
97 #if defined(__APPLE__)
98  static complex* _aligned_state_vec_alloc(bitCapIntOcl allocSize)
99  {
100  void* toRet;
101  posix_memalign(&toRet, QRACK_ALIGN_SIZE, allocSize);
102  return (complex*)toRet;
103  }
104 #endif
105 
106  static std::unique_ptr<complex[], void (*)(complex*)> Alloc(bitCapIntOcl elemCount)
107  {
108 #if defined(__ANDROID__)
109  return std::unique_ptr<complex[], void (*)(complex*)>(new complex[elemCount], [](complex* c) { delete c; });
110 #else
111  // elemCount is always a power of two, but might be smaller than QRACK_ALIGN_SIZE
112  size_t allocSize = sizeof(complex) * elemCount;
113  if (allocSize < QRACK_ALIGN_SIZE) {
114  allocSize = QRACK_ALIGN_SIZE;
115  }
116 #if defined(__APPLE__)
117  return std::unique_ptr<complex[], void (*)(complex*)>(
118  _aligned_state_vec_alloc(allocSize), [](complex* c) { free(c); });
119 #elif defined(_WIN32) && !defined(__CYGWIN__)
120  return std::unique_ptr<complex[], void (*)(complex*)>(
121  (complex*)_aligned_malloc(allocSize, QRACK_ALIGN_SIZE), [](complex* c) { _aligned_free(c); });
122 #else
123  return std::unique_ptr<complex[], void (*)(complex*)>(
124  (complex*)aligned_alloc(QRACK_ALIGN_SIZE, allocSize), [](complex* c) { free(c); });
125 #endif
126 #endif
127  }
128 
129  virtual void Free() { amplitudes = nullptr; }
130 
131 public:
133  : StateVector(cap)
135  {
136  // Intentionally left blank.
137  }
138 
139  virtual ~StateVectorArray() { Free(); }
140 
141  complex* get_raw() { return amplitudes.get(); }
142 
143  complex read(const bitCapInt& i) { return read((bitCapIntOcl)i); }
144 #if ENABLE_COMPLEX_X2
145  complex2 read2(const bitCapInt& i1, const bitCapInt& i2) { return read2((bitCapIntOcl)i1, (bitCapIntOcl)i2); }
146 #endif
147  void write(const bitCapInt& i, const complex& c) { write((bitCapIntOcl)i, c); }
148  void write2(const bitCapInt& i1, const complex& c1, const bitCapInt& i2, const complex& c2)
149  {
150  write2((bitCapIntOcl)i1, c1, (bitCapIntOcl)i2, c2);
151  }
152 
153  complex read(const bitCapIntOcl& i) { return amplitudes.get()[i]; };
154 
155 #if ENABLE_COMPLEX_X2
156  complex2 read2(const bitCapIntOcl& i1, const bitCapIntOcl& i2)
157  {
158  return complex2(amplitudes.get()[i1], amplitudes.get()[i2]);
159  }
160 #endif
161 
162  void write(const bitCapIntOcl& i, const complex& c) { amplitudes.get()[i] = c; };
163 
164  void write2(const bitCapIntOcl& i1, const complex& c1, const bitCapIntOcl& i2, const complex& c2)
165  {
166  amplitudes.get()[i1] = c1;
167  amplitudes.get()[i2] = c2;
168  };
169 
170  void clear()
171  {
172  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = ZERO_CMPLX; });
173  }
174 
175  void copy_in(const complex* copyIn)
176  {
177  if (copyIn) {
178  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = copyIn[lcv]; });
179  } else {
180  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = ZERO_CMPLX; });
181  }
182  }
183 
184  void copy_in(const complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
185  {
186  if (copyIn) {
187  par_for(0, length,
188  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + offset] = copyIn[lcv]; });
189  } else {
190  par_for(0, length,
191  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + offset] = ZERO_CMPLX; });
192  }
193  }
194 
195  void copy_in(
196  StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
197  {
198  if (copyInSv) {
199  const complex* copyIn = std::dynamic_pointer_cast<StateVectorArray>(copyInSv)->amplitudes.get() + srcOffset;
200  par_for(0, length,
201  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + dstOffset] = copyIn[lcv]; });
202  } else {
203  par_for(0, length,
204  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + dstOffset] = ZERO_CMPLX; });
205  }
206  }
207 
208  void copy_out(complex* copyOut)
209  {
210  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { copyOut[lcv] = amplitudes[lcv]; });
211  }
212 
213  void copy_out(complex* copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
214  {
215  par_for(
216  0, length, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { copyOut[lcv] = amplitudes[lcv + offset]; });
217  }
218 
219  void copy(StateVectorPtr toCopy) { copy(std::dynamic_pointer_cast<StateVectorArray>(toCopy)); }
220 
222  {
223  par_for(0, capacity,
224  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = toCopy->amplitudes[lcv]; });
225  }
226 
227  void shuffle(StateVectorPtr svp) { shuffle(std::dynamic_pointer_cast<StateVectorArray>(svp)); }
228 
230  {
231  const bitCapIntOcl offset = capacity >> 1U;
232  par_for(0, offset, [&](const bitCapIntOcl& lcv, const unsigned& cpu) {
233  const complex tmp = amplitudes[lcv + offset];
234  amplitudes[lcv + offset] = svp->amplitudes[lcv];
235  svp->amplitudes[lcv] = tmp;
236  });
237  }
238 
239  void get_probs(real1* outArray)
240  {
241  par_for(
242  0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { outArray[lcv] = norm(amplitudes[lcv]); });
243  }
244 
245  bool is_sparse() { return false; }
246 };
247 
249 protected:
251  std::mutex mtx;
252 
254  {
255  auto it = amplitudes.find(i);
256  return (it == amplitudes.end()) ? ZERO_CMPLX : it->second;
257  }
258 
260  {
261  std::lock_guard<std::mutex> lock(mtx);
262  return readUnlocked(i);
263  }
264 
265 public:
267  : StateVector(cap)
268  , amplitudes()
269  {
270  }
271 
272  complex read(const bitCapIntOcl& i) { return read((bitCapInt)i); }
273 #if ENABLE_COMPLEX_X2
274  complex2 read2(const bitCapIntOcl& i1, const bitCapIntOcl& i2) { return read2((bitCapInt)i1, (bitCapInt)i2); }
275 #endif
276  void write(const bitCapIntOcl& i, const complex& c) { write((bitCapInt)i, c); }
277  void write2(const bitCapIntOcl& i1, const complex& c1, const bitCapIntOcl& i2, const complex& c2)
278  {
279  write2((bitCapInt)i1, c1, (bitCapInt)i2, c2);
280  }
281 
282  size_t size() { return amplitudes.size(); }
283 
284  void mult(real1_f nrm)
285  {
286  const real1 _nrm = (real1)nrm;
287  for (auto& pair : amplitudes) {
288  pair.second *= _nrm;
289  }
290  }
291 
292  // Rewrite of truncate_to_size() by (Anthropic) Claude, with thanks!
293  real1_f truncate_to_size(size_t maxAmps)
294  {
295  if (amplitudes.size() <= maxAmps) {
296  return ONE_R1_F;
297  }
298 
299  // Single pass: extract (norm, key) pairs into a flat vector
300  std::vector<std::pair<real1, bitCapInt>> nrmKeys;
301  nrmKeys.reserve(amplitudes.size());
302  for (const auto& pair : amplitudes) {
303  nrmKeys.emplace_back(norm(pair.second), pair.first);
304  }
305 
306  // Partial sort: O(n) average via nth_element
307  // After this, nrmKeys[maxAmps-1] is the pivot,
308  // elements [0, maxAmps) are the largest (unordered)
309  std::nth_element(nrmKeys.begin(), nrmKeys.begin() + (nrmKeys.size() - maxAmps), nrmKeys.end(),
310  [](const auto& a, const auto& b) { return a.first < b.first; });
311 
312  // Pivot norm — everything at or above this index survives
313  const real1 limit = nrmKeys[nrmKeys.size() - maxAmps].first;
314 
315  // Accumulate fidelity from survivors and erase losers in one pass
316  real1 fidelity = ZERO_R1;
317  for (size_t i = nrmKeys.size() - maxAmps; i < nrmKeys.size(); ++i) {
318  fidelity += nrmKeys[i].first;
319  }
320 
321  // Erase from map directly — no copy of nAmplitudes needed
322  for (size_t i = 0; i < nrmKeys.size() - maxAmps; ++i) {
323  amplitudes.erase(nrmKeys[i].second);
324  }
325 
326  // Handle tie-breaking: if we kept too many at exactly `limit`
327  if (amplitudes.size() > maxAmps) {
328  std::vector<bitCapInt> ties;
329  for (const auto& pair : amplitudes) {
330  if (norm(pair.second) == limit) {
331  ties.push_back(pair.first);
332  }
333  }
334  std::random_device rd;
335  std::mt19937 g(rd());
336  std::shuffle(ties.begin(), ties.end(), g);
337  ties.resize(amplitudes.size() - maxAmps);
338  for (const bitCapInt& idx : ties) {
339  amplitudes.erase(idx);
340  fidelity -= limit;
341  }
342  }
343 
344  const real1 nrm = ONE_R1 / (real1)std::sqrt((real1_s)fidelity);
345  mult(nrm);
346 
347  return fidelity;
348  }
349 
350  complex read(const bitCapInt& i) { return isReadLocked ? readLocked(i) : readUnlocked(i); }
351 
352 #if ENABLE_COMPLEX_X2
353  complex2 read2(const bitCapInt& i1, const bitCapInt& i2)
354  {
355  if (isReadLocked) {
356  return complex2(readLocked(i1), readLocked(i2));
357  }
358  return complex2(readUnlocked(i1), readUnlocked(i2));
359  }
360 #endif
361 
362  void write(const bitCapInt& i, const complex& c)
363  {
364  const bool isCSet = abs(c) > REAL1_EPSILON;
365  if (isCSet) {
366  std::lock_guard<std::mutex> lock(mtx);
367  amplitudes[i] = c;
368  } else {
369  std::lock_guard<std::mutex> lock(mtx);
370  amplitudes.erase(i);
371  }
372  }
373 
374  void write2(const bitCapInt& i1, const complex& c1, const bitCapInt& i2, const complex& c2)
375  {
376  const bool isC1Set = abs(c1) > REAL1_EPSILON;
377  const bool isC2Set = abs(c2) > REAL1_EPSILON;
378  if (!isC1Set && !isC2Set) {
379  std::lock_guard<std::mutex> lock(mtx);
380  amplitudes.erase(i1);
381  amplitudes.erase(i2);
382  } else if (isC1Set && isC2Set) {
383  std::lock_guard<std::mutex> lock(mtx);
384  amplitudes[i1] = c1;
385  amplitudes[i2] = c2;
386  } else if (isC1Set) {
387  std::lock_guard<std::mutex> lock(mtx);
388  amplitudes.erase(i2);
389  amplitudes[i1] = c1;
390  } else {
391  std::lock_guard<std::mutex> lock(mtx);
392  amplitudes.erase(i1);
393  amplitudes[i2] = c2;
394  }
395  }
396 
397  void clear()
398  {
399  std::lock_guard<std::mutex> lock(mtx);
400  amplitudes.clear();
401  }
402 
403  void copy_in(const complex* copyIn)
404  {
405  if (!copyIn) {
406  clear();
407  return;
408  }
409 
410  std::lock_guard<std::mutex> lock(mtx);
411  for (bitCapIntOcl i = 0U; i < capacity; ++i) {
412  if (abs(copyIn[i]) <= REAL1_EPSILON) {
413  amplitudes.erase(i);
414  } else {
415  amplitudes[i] = copyIn[i];
416  }
417  }
418  }
419 
420  void copy_in(const complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
421  {
422  if (!copyIn) {
423  std::lock_guard<std::mutex> lock(mtx);
424  for (bitCapIntOcl i = 0U; i < length; ++i) {
425  amplitudes.erase(i);
426  }
427 
428  return;
429  }
430 
431  std::lock_guard<std::mutex> lock(mtx);
432  for (bitCapIntOcl i = 0U; i < length; ++i) {
433  if (abs(copyIn[i]) <= REAL1_EPSILON) {
434  amplitudes.erase(i);
435  } else {
436  amplitudes[i + offset] = copyIn[i];
437  }
438  }
439  }
440 
441  void copy_in(
442  StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
443  {
444  StateVectorSparsePtr copyIn = std::dynamic_pointer_cast<StateVectorSparse>(copyInSv);
445 
446  if (!copyIn) {
447  std::lock_guard<std::mutex> lock(mtx);
448  for (bitCapIntOcl i = 0U; i < length; ++i) {
449  amplitudes.erase(i + srcOffset);
450  }
451 
452  return;
453  }
454 
455  std::lock_guard<std::mutex> lock(mtx);
456  for (bitCapIntOcl i = 0U; i < length; ++i) {
457  complex amp = copyIn->read(i + srcOffset);
458  if (abs(amp) <= REAL1_EPSILON) {
459  amplitudes.erase(i + srcOffset);
460  } else {
461  amplitudes[i + dstOffset] = amp;
462  }
463  }
464  }
465 
466  void copy_out(complex* copyOut)
467  {
468  for (bitCapIntOcl i = 0U; i < capacity; ++i) {
469  copyOut[i] = read(i);
470  }
471  }
472 
473  void copy_out(complex* copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
474  {
475  for (bitCapIntOcl i = 0U; i < length; ++i) {
476  copyOut[i] = read(i + offset);
477  }
478  }
479 
480  void copy(const StateVectorPtr toCopy) { copy(std::dynamic_pointer_cast<StateVectorSparse>(toCopy)); }
481 
483  {
484  std::lock_guard<std::mutex> lock(mtx);
485  amplitudes = toCopy->amplitudes;
486  }
487 
488  void shuffle(StateVectorPtr svp) { shuffle(std::dynamic_pointer_cast<StateVectorSparse>(svp)); }
489 
491  {
492  const size_t halfCap = (size_t)(capacity >> 1U);
493  std::lock_guard<std::mutex> lock(mtx);
494  for (bitCapIntOcl i = 0U; i < halfCap; ++i) {
495  complex amp = svp->read(i);
496  svp->write(i, read(i + halfCap));
497  write(i + halfCap, amp);
498  }
499  }
500 
501  void get_probs(real1* outArray)
502  {
503  for (bitCapIntOcl i = 0U; i < capacity; ++i) {
504  outArray[i] = norm(read(i));
505  }
506  }
507 
508  bool is_sparse() { return (amplitudes.size() < (size_t)(capacity >> 1U)); }
509 
510  std::vector<bitCapInt> iterable()
511  {
512  std::vector<std::vector<bitCapInt>> toRet(GetConcurrencyLevel());
513  std::vector<std::vector<bitCapInt>>::iterator toRetIt;
514 
515  // For lock_guard scope
516  if (true) {
517  std::lock_guard<std::mutex> lock(mtx);
518 
519  par_for(0U, amplitudes.size(), [&](const bitCapIntOcl& lcv, const unsigned& cpu) {
520  auto it = amplitudes.begin();
521  std::advance(it, lcv);
522  toRet[cpu].push_back(it->first);
523  });
524  }
525 
526  for (int64_t i = (int64_t)(toRet.size() - 1U); i >= 0; i--) {
527  if (toRet[i].empty()) {
528  toRetIt = toRet.begin();
529  std::advance(toRetIt, i);
530  toRet.erase(toRetIt);
531  }
532  }
533 
534  if (toRet.empty()) {
535  return {};
536  }
537 
538  while (toRet.size() > 1U) {
539  // Work odd unit into collapse sequence:
540  if (toRet.size() & 1U) {
541  toRet[toRet.size() - 2U].insert(
542  toRet[toRet.size() - 2U].end(), toRet[toRet.size() - 1U].begin(), toRet[toRet.size() - 1U].end());
543  toRet.pop_back();
544  }
545 
546  const int64_t combineCount = (int64_t)(toRet.size() >> 1U);
547 #if ENABLE_PTHREAD
548  std::vector<std::future<void>> futures(combineCount);
549  for (int64_t i = (combineCount - 1); i >= 0; i--) {
550  futures[i] = std::async(std::launch::async, [i, combineCount, &toRet]() {
551  toRet[i].insert(toRet[i].end(), toRet[i + combineCount].begin(), toRet[i + combineCount].end());
552  toRet[i + combineCount].clear();
553  });
554  }
555  for (int64_t i = (combineCount - 1); i >= 0; i--) {
556  futures[i].get();
557  toRet.pop_back();
558  }
559 #else
560  for (int64_t i = (combineCount - 1); i >= 0; i--) {
561  toRet[i].insert(toRet[i].end(), toRet[i + combineCount].begin(), toRet[i + combineCount].end());
562  toRet.pop_back();
563  }
564 #endif
565  }
566 
567  return toRet[0U];
568  }
569 
571  std::set<bitCapInt> iterable(
572  const bitCapInt& setMask, const bitCapInt& filterMask = 0, const bitCapInt& filterValues = 0)
573  {
574  if ((filterMask == ZERO_BCI) && (filterValues != ZERO_BCI)) {
575  return {};
576  }
577 
578  const bitCapInt unsetMask = ~setMask;
579 
580  std::vector<std::set<bitCapInt>> toRet(GetConcurrencyLevel());
581  std::vector<std::set<bitCapInt>>::iterator toRetIt;
582 
583  // For lock_guard scope
584  if (true) {
585  std::lock_guard<std::mutex> lock(mtx);
586 
587  if ((filterMask == ZERO_BCI) && (filterValues == ZERO_BCI)) {
588  par_for(0U, amplitudes.size(), [&](const bitCapIntOcl& lcv, const unsigned& cpu) {
589  auto it = amplitudes.begin();
590  std::advance(it, lcv);
591  toRet[cpu].insert(it->first & unsetMask);
592  });
593  } else {
594  const bitCapInt unfilterMask = ~filterMask;
595  par_for(0U, amplitudes.size(), [&](const bitCapIntOcl lcv, const unsigned& cpu) {
596  auto it = amplitudes.begin();
597  std::advance(it, lcv);
598  if ((it->first & filterMask) == filterValues) {
599  toRet[cpu].insert(it->first & unsetMask & unfilterMask);
600  }
601  });
602  }
603  }
604 
605  for (int64_t i = (int64_t)(toRet.size() - 1U); i >= 0; i--) {
606  if (toRet[i].empty()) {
607  toRetIt = toRet.begin();
608  std::advance(toRetIt, i);
609  toRet.erase(toRetIt);
610  }
611  }
612 
613  if (toRet.empty()) {
614  return {};
615  }
616 
617  while (toRet.size() > 1U) {
618  // Work odd unit into collapse sequence:
619  if (toRet.size() & 1U) {
620  toRet[toRet.size() - 2U].insert(toRet[toRet.size() - 1U].begin(), toRet[toRet.size() - 1U].end());
621  toRet.pop_back();
622  }
623 
624  const int64_t combineCount = (int64_t)(toRet.size() >> 1U);
625 #if ENABLE_PTHREAD
626  std::vector<std::future<void>> futures(combineCount);
627  for (int64_t i = (combineCount - 1); i >= 0; i--) {
628  futures[i] = std::async(std::launch::async, [i, combineCount, &toRet]() {
629  toRet[i].insert(toRet[i + combineCount].begin(), toRet[i + combineCount].end());
630  toRet[i + combineCount].clear();
631  });
632  }
633 
634  for (int64_t i = (combineCount - 1); i >= 0; i--) {
635  futures[i].get();
636  toRet.pop_back();
637  }
638 #else
639  for (int64_t i = (combineCount - 1); i >= 0; i--) {
640  toRet[i].insert(toRet[i + combineCount].begin(), toRet[i + combineCount].end());
641  toRet.pop_back();
642  }
643 #endif
644  }
645 
646  return toRet[0U];
647  }
648 };
649 
650 } // namespace Qrack
Definition: parallel_for.hpp:27
unsigned GetConcurrencyLevel()
Definition: parallel_for.hpp:49
void par_for(const bitCapIntOcl begin, const bitCapIntOcl end, ParallelFunc fn)
Call fn once for every numerical value between begin and end.
Definition: parallel_for.cpp:50
Definition: statevector.hpp:92
void copy(StateVectorArrayPtr toCopy)
Definition: statevector.hpp:221
virtual ~StateVectorArray()
Definition: statevector.hpp:139
std::unique_ptr< complex[], void(*)(complex *)> amplitudes
Definition: statevector.hpp:94
complex read(const bitCapIntOcl &i)
Definition: statevector.hpp:153
void write(const bitCapIntOcl &i, const complex &c)
Definition: statevector.hpp:162
void copy_out(complex *copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:213
void clear()
Definition: statevector.hpp:170
virtual void Free()
Definition: statevector.hpp:129
complex * get_raw()
Definition: statevector.hpp:141
void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
Definition: statevector.hpp:195
void get_probs(real1 *outArray)
Definition: statevector.hpp:239
void write2(const bitCapIntOcl &i1, const complex &c1, const bitCapIntOcl &i2, const complex &c2)
Optimized "write" that is only guaranteed to write if either amplitude is nonzero.
Definition: statevector.hpp:164
void copy_out(complex *copyOut)
Definition: statevector.hpp:208
void shuffle(StateVectorArrayPtr svp)
Definition: statevector.hpp:229
void shuffle(StateVectorPtr svp)
Definition: statevector.hpp:227
static std::unique_ptr< complex[], void(*)(complex *)> Alloc(bitCapIntOcl elemCount)
Definition: statevector.hpp:106
bool is_sparse()
Definition: statevector.hpp:245
void copy(StateVectorPtr toCopy)
Definition: statevector.hpp:219
void copy_in(const complex *copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:184
void copy_in(const complex *copyIn)
Definition: statevector.hpp:175
void write(const bitCapInt &i, const complex &c)
Definition: statevector.hpp:147
StateVectorArray(bitCapIntOcl cap)
Definition: statevector.hpp:132
complex read(const bitCapInt &i)
Definition: statevector.hpp:143
void write2(const bitCapInt &i1, const complex &c1, const bitCapInt &i2, const complex &c2)
Definition: statevector.hpp:148
Definition: statevector.hpp:248
void get_probs(real1 *outArray)
Definition: statevector.hpp:501
void shuffle(StateVectorPtr svp)
Definition: statevector.hpp:488
complex read(const bitCapInt &i)
Definition: statevector.hpp:350
size_t size()
Definition: statevector.hpp:282
std::vector< bitCapInt > iterable()
Definition: statevector.hpp:510
real1_f truncate_to_size(size_t maxAmps)
Definition: statevector.hpp:293
void copy_out(complex *copyOut)
Definition: statevector.hpp:466
void write2(const bitCapInt &i1, const complex &c1, const bitCapInt &i2, const complex &c2)
Definition: statevector.hpp:374
void write2(const bitCapIntOcl &i1, const complex &c1, const bitCapIntOcl &i2, const complex &c2)
Optimized "write" that is only guaranteed to write if either amplitude is nonzero.
Definition: statevector.hpp:277
void write(const bitCapInt &i, const complex &c)
Definition: statevector.hpp:362
void copy_out(complex *copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:473
void copy_in(const complex *copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:420
void copy_in(const complex *copyIn)
Definition: statevector.hpp:403
void write(const bitCapIntOcl &i, const complex &c)
Definition: statevector.hpp:276
std::mutex mtx
Definition: statevector.hpp:251
SparseStateVecMap amplitudes
Definition: statevector.hpp:250
StateVectorSparse(bitCapIntOcl cap)
Definition: statevector.hpp:266
complex readUnlocked(const bitCapInt &i)
Definition: statevector.hpp:253
complex read(const bitCapIntOcl &i)
Definition: statevector.hpp:272
void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
Definition: statevector.hpp:441
std::set< bitCapInt > iterable(const bitCapInt &setMask, const bitCapInt &filterMask=0, const bitCapInt &filterValues=0)
Returns empty if iteration should be over full set, otherwise just the iterable elements:
Definition: statevector.hpp:571
void copy(StateVectorSparsePtr toCopy)
Definition: statevector.hpp:482
void clear()
Definition: statevector.hpp:397
void mult(real1_f nrm)
Definition: statevector.hpp:284
bool is_sparse()
Definition: statevector.hpp:508
void shuffle(StateVectorSparsePtr svp)
Definition: statevector.hpp:490
complex readLocked(const bitCapInt &i)
Definition: statevector.hpp:259
void copy(const StateVectorPtr toCopy)
Definition: statevector.hpp:480
Definition: statevector.hpp:50
bitCapIntOcl capacity
Definition: statevector.hpp:52
virtual void get_probs(real1 *outArray)=0
virtual void shuffle(StateVectorPtr svp)=0
virtual complex read(const bitCapIntOcl &i)=0
virtual void write(const bitCapIntOcl &i, const complex &c)=0
virtual void copy_out(complex *outArray)=0
virtual void copy_in(const complex *copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)=0
virtual void copy_in(const complex *inArray)=0
virtual void copy_out(complex *copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)=0
virtual void write2(const bitCapIntOcl &i1, const complex &c1, const bitCapIntOcl &i2, const complex &c2)=0
Optimized "write" that is only guaranteed to write if either amplitude is nonzero.
virtual void copy(StateVectorPtr toCopy)=0
virtual void write(const bitCapInt &i, const complex &c)=0
virtual void write2(const bitCapInt &i1, const complex &c1, const bitCapInt &i2, const complex &c2)=0
virtual void clear()=0
StateVector(bitCapIntOcl cap)
Definition: statevector.hpp:57
bool isReadLocked
Definition: statevector.hpp:55
virtual void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)=0
virtual ~StateVector()
Definition: statevector.hpp:62
virtual bool is_sparse()=0
virtual complex read(const bitCapInt &i)=0
Half-precision floating-point type.
Definition: half.hpp:2206
GLOSSARY: bitLenInt - "bit-length integer" - unsigned integer ID of qubit position in register bitCap...
Definition: complex16x2simd.hpp:25
void U(quid sid, bitLenInt q, real1_f theta, real1_f phi, real1_f lambda)
(External API) 3-parameter unitary gate
Definition: wasm_api.cpp:1199
half_float::half real1
Definition: qrack_types.hpp:106
std::complex< real1 > complex
Definition: qrack_types.hpp:140
std::shared_ptr< StateVectorSparse > StateVectorSparsePtr
Definition: qrack_types.hpp:151
double norm(const complex2 &c)
Definition: complex16x2simd.hpp:122
QRACK_CONST real1 REAL1_EPSILON
Definition: qrack_types.hpp:203
QRACK_CONST real1 ONE_R1
Definition: qrack_types.hpp:188
QRACK_CONST real1 ZERO_R1
Definition: qrack_types.hpp:186
float real1_f
Definition: qrack_types.hpp:107
std::shared_ptr< StateVectorArray > StateVectorArrayPtr
Definition: qrack_types.hpp:150
float real1_s
Definition: qrack_types.hpp:108
std::shared_ptr< StateVector > StateVectorPtr
Definition: qrack_types.hpp:147
QRACK_CONST complex ZERO_CMPLX
Definition: qrack_types.hpp:258
const bitCapInt ZERO_BCI
Definition: qrack_types.hpp:142
uint32 sqrt(uint32 &r, int &exp)
Fixed point square root.
Definition: half.hpp:1638
HALF_CONSTEXPR half abs(half arg)
Absolute value.
Definition: half.hpp:2958
#define bitCapInt
Definition: qrack_types.hpp:65
#define bitCapIntOcl
Definition: qrack_types.hpp:53
#define ONE_R1_F
Definition: qrack_types.hpp:165
#define QRACK_ALIGN_SIZE
Definition: qrack_types.hpp:159
#define SparseStateVecMap
Definition: statevector.hpp:33
SIMD implementation of the double precision complex vector type of 2 complex numbers,...
Definition: complex16x2simd.hpp:30