WaveSimPP 1.0
A C library for solving the wave equation and reconstructing the wave field
All Classes Namespaces Functions Modules Pages
np.hpp
1#ifndef NP_H_
2#define NP_H_
3
4#include "boost/multi_array.hpp"
5#include "boost/array.hpp"
6#include "boost/cstdlib.hpp"
7#include <type_traits>
8#include <cassert>
9#include <iostream>
10#include <functional>
11#include <type_traits>
12
19namespace np
20{
21
22 typedef double ndArrayValue;
23
25 template <std::size_t ND>
26 inline boost::multi_array<ndArrayValue, ND>::index
27 getIndex(const boost::multi_array<ndArrayValue, ND> &m, const ndArrayValue *requestedElement, const unsigned short int direction)
28 {
29 int offset = requestedElement - m.origin();
30 return (offset / m.strides()[direction] % m.shape()[direction] + m.index_bases()[direction]);
31 }
32
34 template <std::size_t ND>
35 inline boost::array<typename boost::multi_array<ndArrayValue, ND>::index, ND>
36 getIndexArray(const boost::multi_array<ndArrayValue, ND> &m, const ndArrayValue *requestedElement)
37 {
38 using indexType = boost::multi_array<ndArrayValue, ND>::index;
39 boost::array<indexType, ND> _index;
40 for (unsigned int dir = 0; dir < ND; dir++)
41 {
42 _index[dir] = getIndex(m, requestedElement, dir);
43 }
44
45 return _index;
46 }
47
50 template <typename Array, typename Element, typename Functor>
51 inline void for_each(const boost::type<Element> &type_dispatch,
52 Array A, Functor &xform)
53 {
54 for_each(type_dispatch, A.begin(), A.end(), xform);
55 }
56
58 template <typename Element, typename Functor>
59 inline void for_each(const boost::type<Element> &, Element &Val, Functor &xform)
60 {
61 Val = xform(Val);
62 }
63
65 template <typename Element, typename Iterator, typename Functor>
66 inline void for_each(const boost::type<Element> &type_dispatch,
67 Iterator begin, Iterator end,
68 Functor &xform)
69 {
70 while (begin != end)
71 {
72 for_each(type_dispatch, *begin, xform);
73 ++begin;
74 }
75 }
76
79 template <typename Array, typename Functor>
80 inline void for_each(Array &A, Functor xform)
81 {
82 // Dispatch to the proper function
83 for_each(boost::type<typename Array::element>(), A.begin(), A.end(), xform);
84 }
85
89 template <typename T, long unsigned int ND>
90 requires std::is_floating_point<T>::value inline constexpr std::vector<boost::multi_array<T, ND>> gradient(boost::multi_array<T, ND> inArray, std::initializer_list<T> args)
91 {
92 // static_assert(args.size() == ND, "Number of arguments must match the number of dimensions of the array");
93 using arrayIndex = boost::multi_array<T, ND>::index;
94
95 using ndIndexArray = boost::array<arrayIndex, ND>;
96
97 // constexpr std::size_t n = sizeof...(Args);
98 std::size_t n = args.size();
99 // std::tuple<Args...> store(args...);
100 std::vector<T> arg_vector = args;
101 boost::multi_array<T, ND> my_array;
102 std::vector<boost::multi_array<T, ND>> output_arrays;
103 for (std::size_t i = 0; i < n; i++)
104 {
105 boost::multi_array<T, ND> dfdh = inArray;
106 output_arrays.push_back(dfdh);
107 }
108
109 ndArrayValue *p = inArray.data();
110 ndIndexArray index;
111 for (std::size_t i = 0; i < inArray.num_elements(); i++)
112 {
113 index = getIndexArray(inArray, p);
114 /*
115 std::cout << "Index: ";
116 for (std::size_t j = 0; j < n; j++)
117 {
118 std::cout << index[j] << " ";
119 }
120 std::cout << "\n";
121 */
122 // Calculating the gradient now
123 // j is the axis/dimension
124 for (std::size_t j = 0; j < n; j++)
125 {
126 ndIndexArray index_high = index;
127 T dh_high;
128 if ((long unsigned int)index_high[j] < inArray.shape()[j] - 1)
129 {
130 index_high[j] += 1;
131 dh_high = arg_vector[j];
132 }
133 else
134 {
135 dh_high = 0;
136 }
137 ndIndexArray index_low = index;
138 T dh_low;
139 if (index_low[j] > 0)
140 {
141 index_low[j] -= 1;
142 dh_low = arg_vector[j];
143 }
144 else
145 {
146 dh_low = 0;
147 }
148
149 T dh = dh_high + dh_low;
150 T gradient = (inArray(index_high) - inArray(index_low)) / dh;
151 // std::cout << gradient << "\n";
152 output_arrays[j](index) = gradient;
153 }
154 // std::cout << " value = " << inArray(index) << " check = " << *p << std::endl;
155 ++p;
156 }
157 return output_arrays;
158 }
159
161 inline boost::multi_array<double, 1> linspace(double start, double stop, long unsigned int num)
162 {
163 double step = (stop - start) / (num - 1);
164 boost::multi_array<double, 1> output(boost::extents[num]);
165 for (std::size_t i = 0; i < num; i++)
166 {
167 output[i] = start + i * step;
168 }
169 return output;
170 }
171
172 enum indexing
173 {
174 xy,
175 ij
176 };
177
183 template <typename T, long unsigned int ND>
184 requires std::is_arithmetic<T>::value inline constexpr std::vector<boost::multi_array<T, ND>> meshgrid(const boost::multi_array<T, 1> (&cinput)[ND], bool sparsing = false, indexing indexing_type = xy)
185 {
186 using arrayIndex = boost::multi_array<T, ND>::index;
187 using oneDArrayIndex = boost::multi_array<T, 1>::index;
188 using ndIndexArray = boost::array<arrayIndex, ND>;
189 std::vector<boost::multi_array<T, ND>> output_arrays;
190 boost::multi_array<T, 1> ci[ND];
191 // Copy elements of cinput to ci, do the proper inversions
192 for (std::size_t i = 0; i < ND; i++)
193 {
194 std::size_t source = i;
195 if (indexing_type == xy && (ND == 3 || ND == 2))
196 {
197 if (i == 0)
198 source = 1;
199 else if (i == 1)
200 source = 0;
201 else
202 source = i;
203 }
204 ci[i] = boost::multi_array<T, 1>();
205 ci[i].resize(boost::extents[cinput[source].num_elements()]);
206 ci[i] = cinput[source];
207 }
208 // Deducing the extents of the N-Dimensional output
209 boost::detail::multi_array::extent_gen<ND> output_extents;
210 std::vector<size_t> shape_list;
211 for (std::size_t i = 0; i < ND; i++)
212 {
213 shape_list.push_back(ci[i].shape()[0]);
214 }
215 std::copy(shape_list.begin(), shape_list.end(), output_extents.ranges_.begin());
216
217 // Creating the output arrays
218 for (std::size_t i = 0; i < ND; i++)
219 {
220 boost::multi_array<T, ND> output_array(output_extents);
221 ndArrayValue *p = output_array.data();
222 ndIndexArray index;
223 // Looping through the elements of the output array
224 for (std::size_t j = 0; j < output_array.num_elements(); j++)
225 {
226 index = getIndexArray(output_array, p);
227 oneDArrayIndex index_1d;
228 index_1d = index[i];
229 output_array(index) = ci[i][index_1d];
230 ++p;
231 }
232 output_arrays.push_back(output_array);
233 }
234 if (indexing_type == xy && (ND == 3 || ND == 2))
235 {
236 std::swap(output_arrays[0], output_arrays[1]);
237 }
238 return output_arrays;
239 }
240
242 template <class T, long unsigned int ND>
243 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> element_wise_apply(const boost::multi_array<T, ND> &input_array, std::function<T(T)> func)
244 {
245
246 // Create output array copying extents
247 using arrayIndex = boost::multi_array<double, ND>::index;
248 using ndIndexArray = boost::array<arrayIndex, ND>;
249 boost::detail::multi_array::extent_gen<ND> output_extents;
250 std::vector<size_t> shape_list;
251 for (std::size_t i = 0; i < ND; i++)
252 {
253 shape_list.push_back(input_array.shape()[i]);
254 }
255 std::copy(shape_list.begin(), shape_list.end(), output_extents.ranges_.begin());
256 boost::multi_array<T, ND> output_array(output_extents);
257
258 // Looping through the elements of the output array
259 const T *p = input_array.data();
260 ndIndexArray index;
261 for (std::size_t i = 0; i < input_array.num_elements(); i++)
262 {
263 index = getIndexArray(input_array, p);
264 output_array(index) = func(input_array(index));
265 ++p;
266 }
267 return output_array;
268 }
269
270 // Complex operations
271
273 template <class T, long unsigned int ND>
274 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> sqrt(const boost::multi_array<T, ND> &input_array)
275 {
276 std::function<T(T)> func = (T(*)(T))std::sqrt;
277 return element_wise_apply(input_array, func);
278 }
279
281 template <class T>
282 requires std::is_arithmetic<T>::value inline constexpr T sqrt(const T input)
283 {
284 return std::sqrt(input);
285 }
286
288 template <class T, long unsigned int ND>
289 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> exp(const boost::multi_array<T, ND> &input_array)
290 {
291 std::function<T(T)> func = (T(*)(T))std::exp;
292 return element_wise_apply(input_array, func);
293 }
294
296 template <class T>
297 requires std::is_arithmetic<T>::value inline constexpr T exp(const T input)
298 {
299 return std::exp(input);
300 }
301
303 template <class T, long unsigned int ND>
304 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> log(const boost::multi_array<T, ND> &input_array)
305 {
306 std::function<T(T)> func = std::log<T>();
307 return element_wise_apply(input_array, func);
308 }
309
311 template <class T>
312 requires std::is_arithmetic<T>::value inline constexpr T log(const T input)
313 {
314 return std::log(input);
315 }
316
318 template <class T, long unsigned int ND>
319 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> pow(const boost::multi_array<T, ND> &input_array, const T exponent)
320 {
321 std::function<T(T)> pow_func = [exponent](T input)
322 { return std::pow(input, exponent); };
323 return element_wise_apply(input_array, pow_func);
324 }
325
327 template <class T>
328 requires std::is_arithmetic<T>::value inline constexpr T pow(const T input, const T exponent)
329 {
330 return std::pow(input, exponent);
331 }
332
336 template <class T, long unsigned int ND>
337 inline constexpr boost::multi_array<T, ND> element_wise_duo_apply(boost::multi_array<T, ND> const &lhs, boost::multi_array<T, ND> const &rhs, std::function<T(T, T)> func)
338 {
339 // Create output array copying extents
340 using arrayIndex = boost::multi_array<double, ND>::index;
341 using ndIndexArray = boost::array<arrayIndex, ND>;
342 boost::detail::multi_array::extent_gen<ND> output_extents;
343 std::vector<size_t> shape_list;
344 for (std::size_t i = 0; i < ND; i++)
345 {
346 shape_list.push_back(lhs.shape()[i]);
347 }
348 std::copy(shape_list.begin(), shape_list.end(), output_extents.ranges_.begin());
349 boost::multi_array<T, ND> output_array(output_extents);
350
351 // Looping through the elements of the output array
352 const T *p = lhs.data();
353 ndIndexArray index;
354 for (std::size_t i = 0; i < lhs.num_elements(); i++)
355 {
356 index = getIndexArray(lhs, p);
357 output_array(index) = func(lhs(index), rhs(index));
358 ++p;
359 }
360 return output_array;
361 }
362
364 template <typename T, typename inT, long unsigned int ND>
365 requires std::is_integral<inT>::value && std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> zeros(inT (&dimensions_input)[ND])
366 {
367 // Deducing the extents of the N-Dimensional output
368 boost::detail::multi_array::extent_gen<ND> output_extents;
369 std::vector<size_t> shape_list;
370 for (std::size_t i = 0; i < ND; i++)
371 {
372 shape_list.push_back(dimensions_input[i]);
373 }
374 std::copy(shape_list.begin(), shape_list.end(), output_extents.ranges_.begin());
375 // Applying a function to return zero always to all of its elements
376 boost::multi_array<T, ND> output_array(output_extents);
377 std::function<T(T)> zero_func = [](T input)
378 { return 0; };
379 return element_wise_apply(output_array, zero_func);
380 }
381
383 template <typename T, long unsigned int ND>
384 requires std::is_arithmetic<T>::value inline constexpr T max(boost::multi_array<T, ND> const &input_array)
385 {
386 T max = 0;
387 bool max_not_set = true;
388 const T *data_pointer = input_array.data();
389 for (std::size_t i = 0; i < input_array.num_elements(); i++)
390 {
391 T element = *data_pointer;
392 if (max_not_set || element > max)
393 {
394 max = element;
395 max_not_set = false;
396 }
397 ++data_pointer;
398 }
399 return max;
400 }
401
403 template <class T, class... Ts, class = std::enable_if_t<(std::is_same_v<T, Ts> && ...)>>
404 requires std::is_arithmetic<T>::value inline constexpr T max(T input1, Ts... inputs)
405 {
406 T max = input1;
407 for (T input : {inputs...})
408 {
409 if (input > max)
410 {
411 max = input;
412 }
413 }
414 return max;
415 }
416
418 template <typename T, long unsigned int ND>
419 requires std::is_arithmetic<T>::value inline constexpr T min(boost::multi_array<T, ND> const &input_array)
420 {
421 T min = 0;
422 bool min_not_set = true;
423 const T *data_pointer = input_array.data();
424 for (std::size_t i = 0; i < input_array.num_elements(); i++)
425 {
426 T element = *data_pointer;
427 if (min_not_set || element < min)
428 {
429 min = element;
430 min_not_set = false;
431 }
432 ++data_pointer;
433 }
434 return min;
435 }
436
438 template <class T, class... Ts, class = std::enable_if_t<(std::is_same_v<T, Ts> && ...)>>
439 inline constexpr T min(T input1, Ts... inputs) requires std::is_arithmetic<T>::value
440 {
441 T min = input1;
442 for (T input : {inputs...})
443 {
444 if (input < min)
445 {
446 min = input;
447 }
448 }
449 return min;
450 }
451
453 template <typename T, long unsigned int ND>
454 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND> abs(boost::multi_array<T, ND> const &input_array)
455 {
456 std::function<T(T)> abs_func = [](T input)
457 { return std::abs(input); };
458 return element_wise_apply(input_array, abs_func);
459 }
460
462 template <typename T>
463 requires std::is_arithmetic<T>::value inline constexpr T abs(T input)
464 {
465 return std::abs(input);
466 }
467
469 template <typename T, long unsigned int ND>
470 requires std::is_arithmetic<T>::value inline constexpr boost::multi_array<T, ND - 1> slice(boost::multi_array<T, ND> const &input_array, std::size_t slice_index)
471 {
472
473 // Deducing the extents of the N-Dimensional output
474 boost::detail::multi_array::extent_gen<ND - 1> output_extents;
475 std::vector<size_t> shape_list;
476 for (std::size_t i = 1; i < ND; i++)
477 {
478 shape_list.push_back(input_array.shape()[i]);
479 }
480 std::copy(shape_list.begin(), shape_list.end(), output_extents.ranges_.begin());
481
482 boost::multi_array<T, ND - 1> output_array(output_extents);
483
484 const T *p = input_array.data();
485 boost::array<std::size_t, ND> index;
486 for (std::size_t i = 0; i < input_array.num_elements(); i++)
487 {
488 index = getIndexArray(input_array, p);
489 output_array(index) = input_array[slice_index](index);
490 p++;
491 }
492 return output_array;
493 }
494
495}
496
497// Override of operators in the boost::multi_array class to make them more np-like
498// Basic operators
499// All of the are element-wise
500
501// Multiplication operator
503template <class T, long unsigned int ND>
504inline boost::multi_array<T, ND> operator*(boost::multi_array<T, ND> const &lhs, boost::multi_array<T, ND> const &rhs)
505{
506 std::function<T(T, T)> func = std::multiplies<T>();
507 return np::element_wise_duo_apply(lhs, rhs, func);
508}
509
511template <class T, long unsigned int ND>
512inline boost::multi_array<T, ND> operator*(T const &lhs, boost::multi_array<T, ND> const &rhs)
513{
514 std::function<T(T)> func = [lhs](T item)
515 { return lhs * item; };
516 return np::element_wise_apply(rhs, func);
517}
519template <class T, long unsigned int ND>
520inline boost::multi_array<T, ND> operator*(boost::multi_array<T, ND> const &lhs, T const &rhs)
521{
522 return rhs * lhs;
523}
524
525// Plus operator
527template <class T, long unsigned int ND>
528boost::multi_array<T, ND> operator+(boost::multi_array<T, ND> const &lhs, boost::multi_array<T, ND> const &rhs)
529{
530 std::function<T(T, T)> func = std::plus<T>();
531 return np::element_wise_duo_apply(lhs, rhs, func);
532}
533
535template <class T, long unsigned int ND>
536inline boost::multi_array<T, ND> operator+(T const &lhs, boost::multi_array<T, ND> const &rhs)
537{
538 std::function<T(T)> func = [lhs](T item)
539 { return lhs + item; };
540 return np::element_wise_apply(rhs, func);
541}
542
544template <class T, long unsigned int ND>
545inline boost::multi_array<T, ND> operator+(boost::multi_array<T, ND> const &lhs, T const &rhs)
546{
547 return rhs + lhs;
548}
549
550// Subtraction operator
552template <class T, long unsigned int ND>
553boost::multi_array<T, ND> operator-(boost::multi_array<T, ND> const &lhs, boost::multi_array<T, ND> const &rhs)
554{
555 std::function<T(T, T)> func = std::minus<T>();
556 return np::element_wise_duo_apply(lhs, rhs, func);
557}
558
560template <class T, long unsigned int ND>
561inline boost::multi_array<T, ND> operator-(T const &lhs, boost::multi_array<T, ND> const &rhs)
562{
563 std::function<T(T)> func = [lhs](T item)
564 { return lhs - item; };
565 return np::element_wise_apply(rhs, func);
566}
567
569template <class T, long unsigned int ND>
570inline boost::multi_array<T, ND> operator-(boost::multi_array<T, ND> const &lhs, T const &rhs)
571{
572 return rhs - lhs;
573}
574
575// Division operator
577template <class T, long unsigned int ND>
578boost::multi_array<T, ND> operator/(boost::multi_array<T, ND> const &lhs, boost::multi_array<T, ND> const &rhs)
579{
580 std::function<T(T, T)> func = std::divides<T>();
581 return np::element_wise_duo_apply(lhs, rhs, func);
582}
583
585template <class T, long unsigned int ND>
586inline boost::multi_array<T, ND> operator/(T const &lhs, boost::multi_array<T, ND> const &rhs)
587{
588 std::function<T(T)> func = [lhs](T item)
589 { return lhs / item; };
590 return np::element_wise_apply(rhs, func);
591}
592
594template <class T, long unsigned int ND>
595inline boost::multi_array<T, ND> operator/(boost::multi_array<T, ND> const &lhs, T const &rhs)
596{
597 std::function<T(T)> func = [rhs](T item)
598 { return item / rhs; };
599 return np::element_wise_apply(lhs, func);
600}
601
603#endif
boost::multi_array< T, ND > operator*(boost::multi_array< T, ND > const &lhs, boost::multi_array< T, ND > const &rhs)
Multiplication operator between two multi arrays, element-wise.
Definition: np.hpp:504
boost::multi_array< T, ND > operator/(boost::multi_array< T, ND > const &lhs, boost::multi_array< T, ND > const &rhs)
Division between two multi arrays, element wise.
Definition: np.hpp:578
boost::multi_array< T, ND > operator-(boost::multi_array< T, ND > const &lhs, boost::multi_array< T, ND > const &rhs)
Minus operator between two multi arrays, element-wise.
Definition: np.hpp:553
boost::multi_array< T, ND > operator+(boost::multi_array< T, ND > const &lhs, boost::multi_array< T, ND > const &rhs)
Addition operator between two multi arrays, element wise.
Definition: np.hpp:528
Custom implementation of numpy in C++.
Definition: np.hpp:20
::value constexpr boost::multi_array< T, ND > pow(const boost::multi_array< T, ND > &input_array, const T exponent)
Implements the numpy pow function on multi arrays.
Definition: np.hpp:319
::value constexpr std::vector< boost::multi_array< T, ND > > gradient(boost::multi_array< T, ND > inArray, std::initializer_list< T > args)
Definition: np.hpp:90
boost::array< typename boost::multi_array< ndArrayValue, ND >::index, ND > getIndexArray(const boost::multi_array< ndArrayValue, ND > &m, const ndArrayValue *requestedElement)
Gets the index of one element in a multi_array.
Definition: np.hpp:36
::value constexpr std::vector< boost::multi_array< T, ND > > meshgrid(const boost::multi_array< T, 1 >(&cinput)[ND], bool sparsing=false, indexing indexing_type=xy)
Definition: np.hpp:184
::value constexpr boost::multi_array< T, ND > log(const boost::multi_array< T, ND > &input_array)
Implements the numpy log function on multi arrays.
Definition: np.hpp:304
::value constexpr boost::multi_array< T, ND > sqrt(const boost::multi_array< T, ND > &input_array)
Implements the numpy sqrt function on multi arrays.
Definition: np.hpp:274
::value constexpr boost::multi_array< T, ND > exp(const boost::multi_array< T, ND > &input_array)
Implements the numpy exp function on multi arrays.
Definition: np.hpp:289
::value constexpr T abs(T input)
Implements the numpy abs function for a scalar.
Definition: np.hpp:463
boost::multi_array< double, 1 > linspace(double start, double stop, long unsigned int num)
Implements the numpy linspace function.
Definition: np.hpp:161
boost::multi_array< ndArrayValue, ND >::index getIndex(const boost::multi_array< ndArrayValue, ND > &m, const ndArrayValue *requestedElement, const unsigned short int direction)
Gets the index of one element in a multi_array in one axis.
Definition: np.hpp:27
::value constexpr T min(boost::multi_array< T, ND > const &input_array)
Implements the numpy min function for an n-dimensionl multi array.
Definition: np.hpp:419
::value constexpr boost::multi_array< T, ND - 1 > slice(boost::multi_array< T, ND > const &input_array, std::size_t slice_index)
Slices the array through one dimension and returns a ND - 1 dimensional array.
Definition: np.hpp:470
::value &&std::is_arithmetic< T >::value constexpr boost::multi_array< T, ND > zeros(inT(&dimensions_input)[ND])
Implements the numpy zeros function for an n-dimensionl multi array.
Definition: np.hpp:365
void for_each(const boost::type< Element > &type_dispatch, Array A, Functor &xform)
Definition: np.hpp:51
constexpr boost::multi_array< T, ND > element_wise_duo_apply(boost::multi_array< T, ND > const &lhs, boost::multi_array< T, ND > const &rhs, std::function< T(T, T)> func)
Definition: np.hpp:337
::value constexpr boost::multi_array< T, ND > element_wise_apply(const boost::multi_array< T, ND > &input_array, std::function< T(T)> func)
Creates a new array and fills it with the values of the result of the function called on the input ar...
Definition: np.hpp:243
::value constexpr T max(boost::multi_array< T, ND > const &input_array)
Implements the numpy max function for an n-dimensionl multi array.
Definition: np.hpp:384