-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDenseNDArray.hh
319 lines (247 loc) · 11.3 KB
/
DenseNDArray.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#ifndef __DENSE_NDARRAY_HH
#define __DENSE_NDARRAY_HH
#include <vector>
#include <numeric>
#include <algorithm>
#include <iostream>
#include "NDArray.hh"
#include "Serialization.hh"
template <class T, std::size_t dims>
class SparseNDArray;
// TODO: probably also want a fixed-sized version of NDArray that allows to specify the individual dimensions
// Useful for e.g. 3-dim vectors that don't need to be dynamic
template <class T, std::size_t dims>
class DenseNDArray : public NDArray<T, dims> {
public:
using shape_t = typename NDArray<T, dims>::shape_t;
private:
using stride_t = std::array<std::size_t, dims + 1>;
using data_t = std::vector<T>;
private:
friend struct stor::Traits<DenseNDArray<T, dims>>;
template <typename T0, std::size_t dims0, typename T1, typename T2>
friend inline DenseNDArray<T0, dims0> operator_binary(const DenseNDArray<T1, dims0>& lhs, const DenseNDArray<T2, dims0>& rhs, auto binary_op);
template <typename T0, std::size_t dims0, typename T1>
friend inline DenseNDArray<T0, dims0> operator_unary(const DenseNDArray<T1, dims0>& arg, auto unary_op);
template <typename T1> friend class DenseNDArray<T, dims>;
public:
using type = T;
DenseNDArray(std::size_t size, const T& value) requires(dims == 1) : DenseNDArray(shape_t({size}), value) { }
// TODO: This will move to use a fixed-size vector to specify the shape (and dims)
DenseNDArray(const shape_t& shape, const T& value) : NDArray<T, dims>(shape) {
m_strides[0] = 1;
std::partial_sum(shape.begin(), shape.end(), m_strides.begin() + 1, std::multiplies<std::size_t>());
m_data.resize(m_strides.back(), value);
}
static DenseNDArray<T, dims> From(const SparseNDArray<T, dims>& sparse_arr) {
DenseNDArray<T, dims> retval(sparse_arr.shape(), sparse_arr.m_default_value);
for (auto const& [key, val] : sparse_arr.m_data) {
retval(key) = val;
}
return retval;
}
static DenseNDArray<T, dims> FromSparseFile(std::iostream& stream) {
T default_value = stor::Traits<T>::deserialize(stream);
shape_t shape = stor::Traits<shape_t>::deserialize(stream);
DenseNDArray<T, dims> retval(shape, default_value);
std::vector<std::array<std::size_t, dims>> keys = stor::Traits<std::vector<std::array<std::size_t, dims>>>::deserialize(stream);
std::vector<T> values = stor::Traits<std::vector<T>>::deserialize(stream);
for(std::size_t el_ind = 0; el_ind < keys.size(); el_ind++) {
retval(keys[el_ind]) = values[el_ind];
}
return retval;
}
DenseNDArray(const shape_t& shape, std::vector<T>&& data) : NDArray<T, dims>(shape) {
m_strides[0] = 1;
std::partial_sum(shape.begin(), shape.end(), m_strides.begin() + 1, std::multiplies<std::size_t>());
if(m_strides.back() != data.size())
throw;
m_data = std::move(data);
}
DenseNDArray(std::initializer_list<T>&& data) requires(dims == 1) :
NDArray<T, 1>({data.size()}), m_strides({1, data.size()}), m_data(data.begin(), data.end()) { }
template <typename T1>
DenseNDArray(std::vector<T1>&& data) requires(dims == 1) :
NDArray<T, 1>({data.size()}), m_strides({1, data.size()}), m_data(data.begin(), data.end()) { }
template <typename T1, std::size_t length>
DenseNDArray(const std::array<T1, length>& data) requires(dims == 1) :
NDArray<T, 1>({length}), m_strides({1, length}), m_data(data.cbegin(), data.cend()) { }
template <typename T1>
DenseNDArray(const DenseNDArray<T1, dims>& other) :
NDArray<T, dims>(other.m_shape), m_strides(other.m_strides), m_data(other.m_data.cbegin(), other.m_data.cend()) { }
// Indexing with explicit pack of coordinates
template <typename... Inds>
const T& operator()(Inds... inds) const requires(sizeof...(Inds) == dims) {
std::size_t flat_ind = 0, dim = 0;
(..., (flat_ind += inds * m_strides[dim++]));
return m_data[flat_ind];
}
template <typename... Inds>
T& operator()(Inds... inds) requires(sizeof...(Inds) == dims) {
std::size_t flat_ind = 0, dim = 0;
(..., (flat_ind += inds * m_strides[dim++]));
return m_data[flat_ind];
}
// Indexing with a vector that holds the coordinates
// TODO: move to using vectors with compile-time size here and make sure the size matches
const T& operator()(DenseNDArray<std::size_t, 1>& inds) const {
if(inds.size() != dims)
throw;
std::size_t flat_ind = std::inner_product(inds.cbegin(), inds.cend(), m_strides.begin(), 0);
return m_data[flat_ind];
}
T& operator()(DenseNDArray<std::size_t, 1>& inds) {
if(inds.size() != dims)
throw;
std::size_t flat_ind = std::inner_product(inds.cbegin(), inds.cend(), m_strides.begin(), 0);
return m_data[flat_ind];
}
T& operator()(const std::array<std::size_t, dims>& inds) {
std::size_t flat_ind = std::inner_product(inds.cbegin(), inds.cend(), m_strides.begin(), 0);
return m_data[flat_ind];
}
// indexing for special (low-dimensional) cases
inline const T& operator()(std::size_t ind) const requires(dims == 1) {
return m_data[ind];
}
inline T& operator()(std::size_t ind) requires(dims == 1) {
return m_data[ind];
}
inline const T& operator()(DenseNDArray<std::size_t, 1>& inds) const requires(dims == 3) {
return m_data[inds.m_data[0] * m_strides[0] + inds.m_data[1] * m_strides[1] + inds.m_data[2] * m_strides[2]];
}
inline T& operator()(DenseNDArray<std::size_t, 1>& inds) requires(dims == 3) {
return m_data[inds.m_data[0] * m_strides[0] + inds.m_data[1] * m_strides[1] + inds.m_data[2] * m_strides[2]];
}
bool operator==(const DenseNDArray<T, dims>& rhs) {
return rhs.m_data == m_data;
}
// to assign blocks of data in an efficient way
void copy_from(const DenseNDArray<T, dims>& src,
const DenseNDArray<std::size_t, 1>& ind_src_start, const DenseNDArray<std::size_t, 1>& ind_src_stop,
const DenseNDArray<std::size_t, 1>& ind_dest_start, const DenseNDArray<std::size_t, 1>& ind_dest_stop) {
}
// template <std::size_t len>
// bool operator==(const std::array<T, len>& rhs) const requires(dims == 1) {
// if(len != m_data.size()) {
// return false;
// }
// for(std::size_t ind = 0; ind < len; ind++) {
// if(m_data[ind] != rhs[ind]) {
// return false;
// }
// }
// return true;
// }
// printing
void print() const {
for(const T& cur: m_data) {
std::cout << cur << " ";
}
std::cout << std::endl;
}
auto begin() {return m_data.begin();}
auto cbegin() {return m_data.cbegin();}
auto begin() const {return m_data.cbegin();}
auto end() {return m_data.end();}
auto cend() {return m_data.cend();}
auto end() const {return m_data.cend();}
const std::size_t size() const requires(dims == 1) {return m_data.size();}
const std::size_t volume() const {return m_strides.back();}
friend DenseNDArray<T, dims> operator+(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
return operator_binary<T, dims, T, T>(lhs, rhs, std::plus<>());
}
friend DenseNDArray<T, dims> operator+(const DenseNDArray<T, dims>& lhs, const T& rhs) {
auto plus_rhs = [&](const T& el){return std::plus<>()(el, rhs);};
return operator_unary<T, dims, T>(lhs, plus_rhs);
}
friend DenseNDArray<T, dims> operator-(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
return operator_binary<T, dims, T, T>(lhs, rhs, std::minus<>());
}
friend DenseNDArray<T, dims> operator-(const DenseNDArray<T, dims>& lhs, const T& rhs) {
auto minus_rhs = [&](const T& el){return std::minus<>()(el, rhs);};
return operator_unary<T, dims, T>(lhs, minus_rhs);
}
friend DenseNDArray<T, dims> operator*(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
return operator_binary<T, dims, T, T>(lhs, rhs, std::multiplies<>());
}
friend DenseNDArray<T, dims> operator*(const DenseNDArray<T, dims>& lhs, const T& rhs) {
auto multiplies_rhs = [&](const T& el){return std::multiplies<>()(el, rhs);};
return operator_unary<T, dims, T>(lhs, multiplies_rhs);
}
friend DenseNDArray<T, dims> operator/(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
return operator_binary<T, dims, T, T>(lhs, rhs, std::divides<>());
}
friend DenseNDArray<T, dims> operator/(const DenseNDArray<T, dims>& lhs, const T& rhs) {
auto divides_rhs = [&](const T& el){return std::divides<>()(el, rhs);};
return operator_unary<T, dims, T>(lhs, divides_rhs);
}
friend DenseNDArray<bool, dims> operator<(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
auto elementwise_lt = [&](const T& el_lhs, const T& el_rhs){return el_lhs < el_rhs;};
return operator_binary<bool, dims, T, T>(lhs, rhs, elementwise_lt);
}
friend DenseNDArray<bool, dims> operator<=(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
auto elementwise_leq = [&](const T& el_lhs, const T& el_rhs){return el_lhs <= el_rhs;};
return operator_binary<bool, dims, T, T>(lhs, rhs, elementwise_leq);
}
friend DenseNDArray<bool, dims> operator>(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
auto elementwise_gt = [&](const T& el_lhs, const T& el_rhs){return el_lhs > el_rhs;};
return operator_binary<bool, dims, T, T>(lhs, rhs, elementwise_gt);
}
friend DenseNDArray<bool, dims> operator>=(const DenseNDArray<T, dims>& lhs, const DenseNDArray<T, dims>& rhs) {
auto elementwise_geq = [&](const T& el_lhs, const T& el_rhs){return el_lhs >= el_rhs;};
return operator_binary<bool, dims, T, T>(lhs, rhs, elementwise_geq);
}
private:
stride_t m_strides = {};
data_t m_data = {};
};
template <typename T0, std::size_t dims0, typename T1, typename T2>
inline DenseNDArray<T0, dims0> operator_binary(const DenseNDArray<T1, dims0>& lhs, const DenseNDArray<T2, dims0>& rhs,
auto binary_op) {
DenseNDArray<T0, dims0> result(lhs.m_shape, T0());
std::transform(lhs.m_data.begin(), lhs.m_data.end(), rhs.m_data.begin(), result.m_data.begin(), binary_op);
return result;
}
template <typename T0, std::size_t dims0, typename T1>
inline DenseNDArray<T0, dims0> operator_unary(const DenseNDArray<T1, dims0>& arg, auto unary_op) {
DenseNDArray<T0, dims0> result(arg.m_shape, T0());
std::transform(arg.m_data.begin(), arg.m_data.end(), result.m_data.begin(), unary_op);
return result;
}
template <std::size_t dims>
bool all(const DenseNDArray<bool, dims>& mask) {
for(bool cur : mask) {
if(cur == false) {
return false;
}
}
return true;
}
namespace stor {
template<typename T, std::size_t dims>
struct Traits<DenseNDArray<T, dims>> {
using type = DenseNDArray<T, dims>;
using shape_t = typename type::shape_t;
using data_t = typename type::data_t;
static void serialize(std::iostream& stream, const type& val) {
Traits<shape_t>::serialize(stream, val.m_shape);
Traits<data_t>::serialize(stream, val.m_data);
}
static type deserialize(std::iostream& stream) {
shape_t shape = Traits<shape_t>::deserialize(stream);
data_t data = Traits<data_t>::deserialize(stream);
return DenseNDArray<T, dims>(shape, std::move(data));
}
};
}
// Some type shortcuts
template <class T>
using DenseVector = DenseNDArray<T, 1>;
using IndexVector = DenseVector<std::size_t>;
using GridVector = DenseVector<unsigned int>;
template <class T>
using ScalarField3D = DenseNDArray<T, 3>;
template <class T>
using ScalarField2D = DenseNDArray<T, 2>;
#endif