Qrack  9.13
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 
21 #ifdef ENABLE_PTHREAD
22 #include <future>
23 #endif
24 
25 #if ENABLE_COMPLEX_X2
26 #if FPPOW == 5
28 #elif FPPOW == 6
30 #endif
31 #endif
32 
33 namespace Qrack {
34 
35 class StateVectorArray;
36 
37 // This is a buffer struct that's capable of representing controlled single bit gates and arithmetic, when subclassed.
38 class StateVector : public ParallelFor {
39 protected:
41 
42 public:
44  : capacity(cap)
45  {
46  }
47  virtual ~StateVector()
48  {
49  // Intentionally left blank.
50  }
51 
52  virtual complex read(const bitCapIntOcl& i) = 0;
53 #if ENABLE_COMPLEX_X2
54  virtual complex2 read2(const bitCapIntOcl& i1, const bitCapIntOcl& i2) = 0;
55 #endif
56  virtual void write(const bitCapIntOcl& i, const complex& c) = 0;
59  virtual void write2(const bitCapIntOcl& i1, const complex& c1, const bitCapIntOcl& i2, const complex& c2) = 0;
60  virtual void clear() = 0;
61  virtual void copy_in(const complex* inArray) = 0;
62  virtual void copy_in(const complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length) = 0;
63  virtual void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset,
64  const bitCapIntOcl length) = 0;
65  virtual void copy_out(complex* outArray) = 0;
66  virtual void copy_out(complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length) = 0;
67  virtual void copy(StateVectorPtr toCopy) = 0;
68  virtual void shuffle(StateVectorPtr svp) = 0;
69  virtual void get_probs(real1* outArray) = 0;
70 };
71 
72 class StateVectorArray : public StateVector {
73 public:
74  std::unique_ptr<complex[], void (*)(complex*)> amplitudes;
75 
76 protected:
77 #if defined(__APPLE__)
78  complex* _aligned_state_vec_alloc(bitCapIntOcl allocSize)
79  {
80  void* toRet;
81  posix_memalign(&toRet, QRACK_ALIGN_SIZE, allocSize);
82  return (complex*)toRet;
83  }
84 #endif
85 
86  std::unique_ptr<complex[], void (*)(complex*)> Alloc(bitCapIntOcl elemCount)
87  {
88 #if defined(__ANDROID__)
89  return std::unique_ptr<complex[], void (*)(complex*)>(new complex[elemCount], [](complex* c) { delete c; });
90 #else
91  // elemCount is always a power of two, but might be smaller than QRACK_ALIGN_SIZE
92  size_t allocSize = sizeof(complex) * elemCount;
93  if (allocSize < QRACK_ALIGN_SIZE) {
94  allocSize = QRACK_ALIGN_SIZE;
95  }
96 #if defined(__APPLE__)
97  return std::unique_ptr<complex[], void (*)(complex*)>(
98  _aligned_state_vec_alloc(allocSize), [](complex* c) { free(c); });
99 #elif defined(_WIN32) && !defined(__CYGWIN__)
100  return std::unique_ptr<complex[], void (*)(complex*)>(
101  (complex*)_aligned_malloc(allocSize, QRACK_ALIGN_SIZE), [](complex* c) { _aligned_free(c); });
102 #else
103  return std::unique_ptr<complex[], void (*)(complex*)>(
104  (complex*)aligned_alloc(QRACK_ALIGN_SIZE, allocSize), [](complex* c) { free(c); });
105 #endif
106 #endif
107  }
108 
109  virtual void Free() { amplitudes = NULL; }
110 
111 public:
113  : StateVector(cap)
115  {
116  // Intentionally left blank.
117  }
118 
119  virtual ~StateVectorArray() { Free(); }
120 
121  complex read(const bitCapIntOcl& i) { return amplitudes.get()[i]; };
122 
123 #if ENABLE_COMPLEX_X2
124  complex2 read2(const bitCapIntOcl& i1, const bitCapIntOcl& i2)
125  {
126  return complex2(amplitudes.get()[i1], amplitudes.get()[i2]);
127  }
128 #endif
129 
130  void write(const bitCapIntOcl& i, const complex& c) { amplitudes.get()[i] = c; };
131 
132  void write2(const bitCapIntOcl& i1, const complex& c1, const bitCapIntOcl& i2, const complex& c2)
133  {
134  amplitudes.get()[i1] = c1;
135  amplitudes.get()[i2] = c2;
136  };
137 
138  void clear()
139  {
140  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = ZERO_CMPLX; });
141  }
142 
143  void copy_in(const complex* copyIn)
144  {
145  if (copyIn) {
146  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = copyIn[lcv]; });
147  } else {
148  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = ZERO_CMPLX; });
149  }
150  }
151 
152  void copy_in(const complex* copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
153  {
154  if (copyIn) {
155  par_for(0, length,
156  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + offset] = copyIn[lcv]; });
157  } else {
158  par_for(0, length,
159  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + offset] = ZERO_CMPLX; });
160  }
161  }
162 
163  void copy_in(
164  StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
165  {
166  if (copyInSv) {
167  const complex* copyIn = std::dynamic_pointer_cast<StateVectorArray>(copyInSv)->amplitudes.get() + srcOffset;
168  par_for(0, length,
169  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + dstOffset] = copyIn[lcv]; });
170  } else {
171  par_for(0, length,
172  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv + dstOffset] = ZERO_CMPLX; });
173  }
174  }
175 
176  void copy_out(complex* copyOut)
177  {
178  par_for(0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { copyOut[lcv] = amplitudes[lcv]; });
179  }
180 
181  void copy_out(complex* copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
182  {
183  par_for(
184  0, length, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { copyOut[lcv] = amplitudes[lcv + offset]; });
185  }
186 
187  void copy(StateVectorPtr toCopy) { copy(std::dynamic_pointer_cast<StateVectorArray>(toCopy)); }
188 
190  {
191  par_for(0, capacity,
192  [&](const bitCapIntOcl& lcv, const unsigned& cpu) { amplitudes[lcv] = toCopy->amplitudes[lcv]; });
193  }
194 
195  void shuffle(StateVectorPtr svp) { shuffle(std::dynamic_pointer_cast<StateVectorArray>(svp)); }
196 
198  {
199  const bitCapIntOcl offset = capacity >> 1U;
200  par_for(0, offset, [&](const bitCapIntOcl& lcv, const unsigned& cpu) {
201  const complex tmp = amplitudes[lcv + offset];
202  amplitudes[lcv + offset] = svp->amplitudes[lcv];
203  svp->amplitudes[lcv] = tmp;
204  });
205  }
206 
207  void get_probs(real1* outArray)
208  {
209  par_for(
210  0, capacity, [&](const bitCapIntOcl& lcv, const unsigned& cpu) { outArray[lcv] = norm(amplitudes[lcv]); });
211  }
212 };
213 } // namespace Qrack
Definition: parallel_for.hpp:19
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:72
std::unique_ptr< complex[], void(*)(complex *)> Alloc(bitCapIntOcl elemCount)
Definition: statevector.hpp:86
void copy(StateVectorArrayPtr toCopy)
Definition: statevector.hpp:189
virtual ~StateVectorArray()
Definition: statevector.hpp:119
std::unique_ptr< complex[], void(*)(complex *)> amplitudes
Definition: statevector.hpp:74
complex read(const bitCapIntOcl &i)
Definition: statevector.hpp:121
void write(const bitCapIntOcl &i, const complex &c)
Definition: statevector.hpp:130
void copy_out(complex *copyOut, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:181
void clear()
Definition: statevector.hpp:138
virtual void Free()
Definition: statevector.hpp:109
void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)
Definition: statevector.hpp:163
void get_probs(real1 *outArray)
Definition: statevector.hpp:207
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:132
void copy_out(complex *copyOut)
Definition: statevector.hpp:176
void shuffle(StateVectorArrayPtr svp)
Definition: statevector.hpp:197
void shuffle(StateVectorPtr svp)
Definition: statevector.hpp:195
void copy(StateVectorPtr toCopy)
Definition: statevector.hpp:187
void copy_in(const complex *copyIn, const bitCapIntOcl offset, const bitCapIntOcl length)
Definition: statevector.hpp:152
void copy_in(const complex *copyIn)
Definition: statevector.hpp:143
StateVectorArray(bitCapIntOcl cap)
Definition: statevector.hpp:112
Definition: statevector.hpp:38
bitCapIntOcl capacity
Definition: statevector.hpp:40
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 clear()=0
StateVector(bitCapIntOcl cap)
Definition: statevector.hpp:43
virtual void copy_in(StateVectorPtr copyInSv, const bitCapIntOcl srcOffset, const bitCapIntOcl dstOffset, const bitCapIntOcl length)=0
virtual ~StateVector()
Definition: statevector.hpp:47
Half-precision floating-point type.
Definition: half.hpp:2222
GLOSSARY: bitLenInt - "bit-length integer" - unsigned integer ID of qubit position in register bitCap...
Definition: complex16x2simd.hpp:25
std::complex< real1 > complex
Definition: qrack_types.hpp:128
double norm(const complex2 &c)
Definition: complex16x2simd.hpp:101
std::shared_ptr< StateVectorArray > StateVectorArrayPtr
Definition: qrack_types.hpp:146
std::shared_ptr< StateVector > StateVectorPtr
Definition: qrack_types.hpp:143
QRACK_CONST complex ZERO_CMPLX
Definition: qrack_types.hpp:253
#define bitCapIntOcl
Definition: qrack_types.hpp:50
#define QRACK_ALIGN_SIZE
Definition: qrack_types.hpp:157
SIMD implementation of the double precision complex vector type of 2 complex numbers,...
Definition: complex16x2simd.hpp:30