Ensign
Low-rank framework for multi-core CPUs and GPUs
Loading...
Searching...
No Matches
storage.hpp
Go to the documentation of this file.
1
6#pragma once
7
8#include <generic/common.hpp>
9#include <generic/utility.hpp>
10#include <generic/kernels.hpp>
11#include <iomanip>
12
13namespace Ensign {
14
15
16
17/* Dense d-dimensional array stored contiguously in column-major order (i.e. the leftmost index is contiguous in memory).
18*
19* The memory resides either on the host (CPU) or the device (GPU),
20* depending on stloc. Memory for a larger shape (emax) can be reserved
21* and the current shape (e) changed without reallocation.
22*/
23template<class T, size_t d>
25 array<Index,d> e;
26 array<Index,d> emax;
27 T* v;
29
30 multi_array(stloc _sl=stloc::host) : v(nullptr), sl(_sl) {
31 fill(e.begin(), e.end(), 0);
32 fill(emax.begin(), emax.end(), 0);
33 }
34
35 multi_array(array<Index,d> _e, stloc _sl=stloc::host) : v(nullptr), sl(_sl) {
36 resize(_e);
37 }
38
39 multi_array(array<Index,d> _emax, array<Index,d> _e, stloc _sl=stloc::host) : v(nullptr), sl(_sl) {
40 reserve(_emax,_e);
41 }
42
43 // copy constructor
44 multi_array(const multi_array& ma) : v(nullptr) {
45 sl = ma.sl;
46 resize(ma.e);
47 if(sl == stloc::host) {
48 std::copy(ma.data(), ma.data()+ma.num_elements(), v);
49 } else {
50 #ifdef __CUDA__
51 cudaMemcpy(v, ma.data(), sizeof(T)*ma.num_elements(),
52 cudaMemcpyDeviceToDevice);
53 #else
54 cout << "ERROR: compiled without GPU support" << __FILE__ << ":"
55 << __LINE__ << endl;
56 exit(1);
57 #endif
58 }
59 }
60
61 // copy operator
63 if(v == nullptr) {
64 sl = ma.sl;
65 resize(ma.e);
66 } else if(e != ma.e) {
67 cout << "ERROR: array dimensions do not match in assignment" << endl;
68 exit(1);
69 }
70
71 if(ma.sl == stloc::host && sl == stloc::host) { // both on CPU
72 std::copy(ma.data(), ma.data()+ma.num_elements(), v);
73 } else {
74 #ifdef __CUDA__
75 if(sl == stloc::host){ // dst on CPU
76 cudaMemcpy(v, ma.data(), sizeof(T)*ma.num_elements(),
77 cudaMemcpyDeviceToHost);
78 } else if(ma.sl == stloc::host){ // src on CPU
79 cudaMemcpy(v, ma.data(), sizeof(T)*ma.num_elements(),
80 cudaMemcpyHostToDevice);
81 } else { // both src and dst on GPU
82 cudaMemcpy(v, ma.data(), sizeof(T)*ma.num_elements(),
83 cudaMemcpyDeviceToDevice);
84 }
85 #else
86 cout << "ERROR: compiled without GPU support" << endl;
87 exit(1);
88 #endif
89 }
90
91 return *this;
92 }
93
94 void resize(array<Index,d> _e) {
95 e = _e;
96 emax = _e;
98 if(sl == stloc::host) {
99 if(v != nullptr) {
100 free(v);
101 }
102 v = (T*)malloc(sizeof(T)*num_elements);
103 } else {
104 #ifdef __CUDA__
105 if(v != nullptr) {
106 cudaFree(v);
107 }
108 v = (T*)gpu_malloc(sizeof(T)*num_elements);
109 #else
110 cout << "ERROR: compiled without GPU support" << __FILE__ << ":"
111 << __LINE__ << endl;
112 exit(1);
113 #endif
114 }
115 set_zero();
116 }
117
118 void reserve(array<Index,d> _emax, array<Index,d> _e) {
119 // Reserve memory for emax for a multiarray of actual size e
120 e = _e;
121 emax = _emax;
123 if(sl == stloc::host) {
124 if(v != nullptr) {
125 free(v);
126 }
127 v = (T*)malloc(sizeof(T)*num_elements);
128 } else {
129 #ifdef __CUDA__
130 if(v != nullptr) {
131 cudaFree(v);
132 }
133 v = (T*)gpu_malloc(sizeof(T)*num_elements);
134 #else
135 cout << "ERROR: compiled without GPU support" << __FILE__ << ":"
136 << __LINE__ << endl;
137 exit(1);
138 #endif
139 }
140 set_zero();
141 }
142
143 void swap(multi_array& ma) {
144 if (sl != ma.sl) {
145 cout << "ERROR: can't swap DEVICE and HOST multi_arrays" << __FILE__ << ":"
146 << __LINE__ << endl;
147 exit(1);
148 }
149 std::swap(ma.e,e);
150 std::swap(ma.emax,emax);
151 std::swap(ma.v,v);
152 }
153
154 void update_shape(array<Index,d> _e){
155 e = _e;
156 }
157
159 if(v != nullptr) {
160 if(sl == stloc::host) {
161 free(v);
162 } else {
163 #ifdef __CUDA__
164 cudaFree(v);
165 #else
166 cout << "ERROR: compiled without GPU support" << __FILE__ << ":"
167 << __LINE__ << endl;
168 exit(1);
169 #endif
170 }
171 }
172 }
173
174 void set_zero() {
175 if(sl == stloc::host) {
176 Index n = num_elements();
177 for(Index i=0;i<n;i++)
178 v[i] = T(0.0);
179 } else {
180 #ifdef __CUDA__
181 cudaMemset(v, 0, sizeof(T)*num_elements());
182 #endif
183 }
184 }
185
186 Index linear_idx(array<Index,d> idx) const {
187 Index k=0;
188 Index stride = 1;
189 for(size_t i=0;i<d;i++) {
190 k += stride*idx[i];
191 stride *= e[i];
192 }
193 assert(k < num_elements() && k >= 0);
194 return k;
195 }
196
197 void to_cplx(multi_array<complex<T>,d>& out) const {
198 std::transform(begin(), end(), out.begin(), [](const T& c){return complex<T>(c,T(0.0));} );
199 }
200
201 void save_vector(std::string fn) {
202 std::ofstream fs(fn.c_str(), std::ios::binary);
203 fs.write((char*)data(), sizeof(T)*num_elements());
204 }
205
206 T& operator()(array<Index,d> idx) {
207 return v[linear_idx(idx)];
208 }
209
210 const T& operator()(array<Index,d> idx) const {
211 return v[linear_idx(idx)];
212 }
213
214 // TODO: if called as (z,0) this gives a -Wnarrowing warning. These warnings
215 // are turned off in the build system at the moment.
216 template<typename... Ints>
217 T& operator()(Ints&&... idx) {
218 static_assert(sizeof...(Ints) == d, "wrong number of arguments to ().");
219 return v[linear_idx(array<Index,d>({idx...}))];
220 }
221
222 template<typename... Ints>
223 const T& operator()(Ints&&... idx) const {
224 static_assert(sizeof...(Ints) == d, "wrong number of arguments to ().");
225 return v[linear_idx(array<Index,d>({idx...}))];
226 }
227
228
229 T* extract(array<Index,d-1> idx_r) {
230 array<Index,d> idx;
231 std::copy(std::begin(idx_r), std::end(idx_r), std::begin(idx)+1);
232 idx[0] = 0;
233 return &v[linear_idx(idx)];
234 }
235
236 const T* extract(array<Index,d-1> idx_r) const {
237 array<Index,d> idx;
238 std::copy(std::begin(idx_r), std::end(idx_r), std::begin(idx)+1);
239 idx[0] = 0;
240 return &v[linear_idx(idx)];
241 }
242
243
244 array<Index,d> shape() const {
245 return e;
246 }
247
248 array<Index,d> shape_alloc() const {
249 return emax;
250 }
251
252 T* data() {
253 if(v == nullptr) {
254 cout << "ERROR: data() called on an uninitialized multi_array" << endl;
255 exit(1);
256 }
257 return &v[0];
258 }
259
260 const T* data() const {
261 if(v == nullptr) {
262 cout << "ERROR: data() called on an uninitialized multi_array" << endl;
263 exit(1);
264 }
265 return &v[0];
266 }
267
269 if(v == nullptr) {
270 cout << "ERROR: begin() called on an uninitialized multi_array" << endl;
271 exit(1);
272 }
273 return prod(e);
274 }
275
276 T* begin() const noexcept {
277 if(v == nullptr) {
278 cout << "ERROR: begin() called on an uninitialized multi_array" << endl;
279 exit(1);
280 }
281 return &v[0];
282 }
283
284 T* end() const noexcept {
285 if(v == nullptr) {
286 cout << "ERROR: end() called on an uninitialized multi_array" << endl;
287 exit(1);
288 }
289 return &v[num_elements()];
290 }
291
293 if(sl == stloc::host && lhs.sl == stloc::host) {
294 #ifdef __OPENMP__
295 #pragma omp parallel for simd
296 #endif
297 for(Index i=0;i<num_elements();i++)
298 v[i] += lhs.v[i];
299 } else if(sl == stloc::device && lhs.sl == stloc::device) {
300 #ifdef __CUDA__
301 if(std::is_same<T, complex<double>>::value)
302 ptw_sum_complex<<<(num_elements()+n_threads-1)/n_threads,n_threads>>>(num_elements(), (cuDoubleComplex*)v, (cuDoubleComplex*)lhs.begin());
303 else if(std::is_same<T, double>::value)
304 ptw_sum<<<(num_elements()+n_threads-1)/n_threads,n_threads>>>(num_elements(), v, lhs.begin());
305 else {
306 cout << "ERROR: single precision in operator+= currently not implemented" << endl;
307 exit(1);
308 }
309 #endif
310 } else {
311 cout << "ERROR: for operator+= both operands need to be either on the device or on the host." << endl;
312 exit(1);
313 }
314 return *this;
315 }
316
317 multi_array& operator+=(const T scalar) {
318 if(sl == stloc::host){
319 #ifdef __OPENMP__
320 #pragma omp parallel for simd
321 #endif
322 for(Index i=0;i<num_elements();i++)
323 v[i] += scalar;
324 }else{
325 #ifdef __CUDA__
326 ptw_sum_scal<<<(num_elements()+n_threads-1)/n_threads,n_threads>>>(num_elements(),begin(),scalar);
327 #endif
328 }
329 return *this;
330 }
331
333 if(sl == stloc::host){
334 #ifdef __OPENMP__
335 #pragma omp parallel for simd
336 #endif
337 for(Index i=0;i<num_elements();i++)
338 v[i] -= lhs.v[i];
339 } else {
340 #ifdef __CUDA__
341 if(std::is_same<T, double>::value)
342 ptw_diff<<<(num_elements()+n_threads-1)/n_threads,n_threads>>>(num_elements(),v,lhs.begin());
343 else {
344 cout << "ERROR: attempted operation not implemented on device (operator+=)" << endl;
345 exit(1);
346 }
347 #endif
348 }
349 return *this;
350 }
351
353 if(sl == stloc::host){
354 #ifdef __OPENMP__
355 #pragma omp parallel for simd
356 #endif
357 for(Index i=0;i<num_elements();i++)
358 v[i] = scalar*v[i];
359 }else{
360 #ifdef __CUDA__
361 run_ptw_mult_scal(num_elements(), v, scalar);
362 #endif
363 }
364 return *this;
365 }
366
367 multi_array& operator/=(const T scalar) {
368 if(sl == stloc::host){
369 #ifdef __OPENMP__
370 #pragma omp parallel for simd
371 #endif
372 for(Index i=0;i<num_elements();i++)
373 v[i] /= scalar;
374 } else {
375 cout << "ERROR: operator/= is not implemented on the device." << endl;
376 exit(1);
377 }
378 return *this;
379 }
380
382 if(sl == stloc::host){
383 multi_array<T,d> out(e);
384 #ifdef __OPENMP__
385 #pragma omp parallel for simd
386 #endif
387 for(Index i=0;i<num_elements();i++)
388 out.v[i] = v[i] + lhs.v[i];
389 return out;
390 } else {
391 cout << "ERROR: operator+ is not implemented on the device." << endl;
392 exit(1);
393 }
394 }
395
397 if(sl == stloc::host){
398 multi_array<T,d> out(e);
399 #ifdef __OPENMP__
400 #pragma omp parallel for simd
401 #endif
402 for(Index i=0;i<num_elements();i++)
403 out.v[i] = v[i] - lhs.v[i];
404 return out;
405 } else {
406 cout << "ERROR: operator- is not implemented on the device." << endl;
407 exit(1);
408 }
409 }
410
411 multi_array operator*(const T scalar) {
412 if(sl == stloc::host){
413 multi_array<T,d> out(e);
414 #ifdef __OPENMP__
415 #pragma omp parallel for simd
416 #endif
417 for(Index i=0;i<num_elements();i++)
418 out.v[i] = v[i]*scalar;
419 return out;
420 } else {
421 cout << "ERROR: operator* is not implemented on the device." << endl;
422 exit(1);
423 }
424 }
425
426 friend std::ostream &operator<<(std::ostream &os, const multi_array &A) {
427
428 if(A.shape().size() == 1){
429 for (Index i=0; i < A.shape()[0]; i++) {
430 os << A.v[i] << '\n';
431 }
432 }else if(A.shape().size() == 2){
433 for (Index i=0; i < A.shape()[0]; i++) {
434 for (Index j=0; j < A.shape()[1]; j++) {
435 os << std::setw(20) << A.v[i + j*A.shape()[0]];
436 }
437 os << '\n';
438 }
439 } else {
440 cout << "ERROR: dimension for cout not yet supported" << endl;
441 exit(1);
442 }
443 return os;
444 }
445
446 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
447 bool operator==(const multi_array& lhs) const {
448 if(v == nullptr || lhs.v == nullptr) {
449 cout << "ERROR: comparison == for two multi_array where one is not initialized" << endl;
450 exit(1);
451 } else if (lhs.shape() != shape()) {
452 return false;
453 } else {
454 for(Index i=0;i<lhs.num_elements();i++){
455 if(std::isnan(lhs.v[i]) || std::isnan(v[i]))
456 return false;
457 if(std::abs((lhs.v[i] - v[i])) > T(10000)*std::numeric_limits<T>::epsilon())
458 return false;
459 }
460 return true;
461 }
462 }
463
464 void sadd(const T scalar, const multi_array& ma) {
465 if(sl == stloc::host && ma.sl == stloc::host) {
467 for(Index i=0;i<num_elements;i++) {
468 v[i] += scalar*ma.v[i];
469 }
470 } else {
471 cout << "ERROR: multi_array::sadd is not implemented for GPUs" << endl;
472 exit(1);
473 }
474 }
475
476
477private:
478 #ifdef __CUDA__
479 void run_ptw_mult_scal(Index n, double* v, double scalar) {
480 ptw_mult_scal<<<(n+n_threads-1)/n_threads,n_threads>>>(n, v, scalar);
481 }
482
483 void run_ptw_mult_scal(Index n, float* v, float scalar) {
484 ptw_mult_scal<<<(n+n_threads-1)/n_threads,n_threads>>>(n, v, scalar);
485 }
486
487 void run_ptw_mult_scal(Index n, complex<double>* v, complex<double> scalar) {
488 ptw_mult_scal_cplx<<<(n+n_threads-1)/n_threads,n_threads>>>(n, (cuDoubleComplex*)v, make_cuDoubleComplex(scalar.real(), scalar.imag()));
489 }
490 #endif
491};
492
493template<size_t d>
494void dump(string fn, const multi_array<double,d>& ma) {
496 h_ma = ma;
497
498 ofstream fs(fn);
499 fs.precision(16);
500 for(Index idx=0;idx<ma.num_elements();idx++)
501 fs << h_ma.v[idx] << endl;
502 fs.close();
503}
504
505template<size_t d>
506void dump(string fn, const multi_array<complex<double>,d>& ma) {
508 h_ma = ma;
509
510 ofstream fs(fn);
511 fs.precision(16);
512 for(Index idx=0;idx<ma.num_elements();idx++)
513 fs << h_ma.v[idx].real() << " " << h_ma.v[idx].imag() << endl;
514 fs.close();
515}
516
517template<class T>
518void print(const multi_array<T,2>& ma) {
519 for(Index i=0;i<ma.shape()[0];i++) {
520 for(Index j=0;j<ma.shape()[1];j++)
521 cout << ma(i,j) << " ";
522 cout << endl;
523 }
524 cout << endl;
525}
526
527} // namespace Ensign
Provides shared types, platform headers, and basic definitions.
ptrdiff_t Index
Definition common.hpp:83
stloc
Definition common.hpp:84
Declares CUDA kernels for elementwise operations and numerical updates.
Core namespace for Ensign's numerical algorithms and data structures.
Definition common.hpp:96
T prod(array< T, d > a)
Definition utility.hpp:14
void print(const multi_array< T, 2 > &ma)
Definition storage.hpp:518
void dump(string fn, const multi_array< double, d > &ma)
Definition storage.hpp:494
Dense d-dimensional array stored contiguously in column-major order (i.e.
Definition storage.hpp:24
void save_vector(std::string fn)
Definition storage.hpp:201
array< Index, d > shape() const
Definition storage.hpp:244
const T & operator()(Ints &&... idx) const
Definition storage.hpp:223
void update_shape(array< Index, d > _e)
Definition storage.hpp:154
void swap(multi_array &ma)
Definition storage.hpp:143
void to_cplx(multi_array< complex< T >, d > &out) const
Definition storage.hpp:197
array< Index, d > shape_alloc() const
Definition storage.hpp:248
void sadd(const T scalar, const multi_array &ma)
Definition storage.hpp:464
multi_array operator*(const T scalar)
Definition storage.hpp:411
T & operator()(Ints &&... idx)
Definition storage.hpp:217
array< Index, d > emax
Definition storage.hpp:26
Index num_elements() const
Definition storage.hpp:268
multi_array & operator+=(const multi_array &lhs)
Definition storage.hpp:292
multi_array(stloc _sl=stloc::host)
Definition storage.hpp:30
multi_array(const multi_array &ma)
Definition storage.hpp:44
const T * extract(array< Index, d-1 > idx_r) const
Definition storage.hpp:236
multi_array operator+(const multi_array &lhs)
Definition storage.hpp:381
multi_array & operator+=(const T scalar)
Definition storage.hpp:317
multi_array & operator/=(const T scalar)
Definition storage.hpp:367
void reserve(array< Index, d > _emax, array< Index, d > _e)
Definition storage.hpp:118
Index linear_idx(array< Index, d > idx) const
Definition storage.hpp:186
const T * data() const
Definition storage.hpp:260
T & operator()(array< Index, d > idx)
Definition storage.hpp:206
multi_array & operator-=(const multi_array &lhs)
Definition storage.hpp:332
void resize(array< Index, d > _e)
Definition storage.hpp:94
T * extract(array< Index, d-1 > idx_r)
Definition storage.hpp:229
array< Index, d > e
Definition storage.hpp:25
const T & operator()(array< Index, d > idx) const
Definition storage.hpp:210
T * end() const noexcept
Definition storage.hpp:284
multi_array(array< Index, d > _e, stloc _sl=stloc::host)
Definition storage.hpp:35
multi_array(array< Index, d > _emax, array< Index, d > _e, stloc _sl=stloc::host)
Definition storage.hpp:39
T * begin() const noexcept
Definition storage.hpp:276
multi_array & operator*=(T scalar)
Definition storage.hpp:352
friend std::ostream & operator<<(std::ostream &os, const multi_array &A)
Definition storage.hpp:426
bool operator==(const multi_array &lhs) const
Definition storage.hpp:447
multi_array & operator=(const multi_array &ma)
Definition storage.hpp:62
multi_array operator-(const multi_array &lhs)
Definition storage.hpp:396
Provides numerical helpers and utilities for arrays and indices.