Stratax 0.2.0
Loading...
Searching...
No Matches
Reductions.hpp
1#pragma once
2
3#include <stratax/core/Concepts.hpp>
4#include <stratax/core/Exceptions.hpp>
5#include <stratax/core/containers/Shape.hpp>
7#include <stratax/core/containers/Tensor.hpp>
8#include <stratax/core/containers/Matrix.hpp>
9#include <stratax/core/containers/Vector.hpp>
10#include <stratax/core/Slice.hpp>
11#include <stratax/core/ops/Slice.hpp>
12#include "Conversions.hpp"
13
14#include <numeric>
15#include <algorithm>
16#include <cmath>
17#include <type_traits>
18#include <utility>
19
20namespace reduction {
21
30inline bool advance(const stratax::core::Shape& shape, std::vector<std::size_t>& indices)
31{
32 // Start from the rightmost dimension
33 for (int d = shape.rank() - 1; d >= 0; --d) {
34 indices[d]++;
35 if (indices[d] < shape(d)) {
36 return true; // Successfully advanced, more indices exist
37 }
38 indices[d] = 0; // Reset and carry over to next dimension
39 }
40
41 return false; // All dimensions wrapped around, no more indices
42}
43
53template<Array A>
54inline int normalize_axis(const A& arr, int axis)
55{
56 int init = axis;
57 if (axis < 0)
58 {
59 init += arr.rank();
60 }
61
62 return init;
63}
64
75template<Array A>
76inline std::vector<std::size_t> result_shape(const A& arr, int axis, bool keepdims)
77{
78 stratax::core::Shape input_shape = arr.shape();
79
80 std::vector<std::size_t> result_dimensions;
81
82 if (keepdims)
83 {
84 for (std::size_t dimension = 0; dimension < input_shape.rank(); ++dimension)
85 {
86 if (dimension == static_cast<std::size_t>(axis))
87 {
88 result_dimensions.push_back(1);
89 }
90
91 else
92 {
93 result_dimensions.push_back(input_shape[dimension]);
94 }
95 }
96 }
97
98 else
99 {
100 for (std::size_t dimension = 0; dimension < input_shape.rank(); ++dimension)
101 {
102 if (dimension != static_cast<std::size_t>(axis))
103 {
104 result_dimensions.push_back(input_shape[dimension]);
105 }
106 }
107 }
108
109 return result_dimensions;
110}
111
118template<Array A, typename Func>
119using axis_reduce_value_t =
120 decltype(std::declval<Func>()(
121 std::declval<const stratax::container::Tensor<typename A::value_type>&>()));
122
137template<Array A, typename Func>
138stratax::container::Tensor<axis_reduce_value_t<A, Func>>
139axis_reduce(const A& array, int axis, Func func, bool keepdims = false)
140{
141 using ResultType = axis_reduce_value_t<A, Func>;
142
143 int Axis = normalize_axis(array, axis);
144
145 if (Axis < 0 || Axis >= static_cast<int>(array.rank()))
146 {
147 throw Exceptions::AxisError("axis is out of range.");
148 }
149
150 stratax::container::Tensor<typename A::value_type> arr = to_tensor(array);
151
152 const stratax::core::Shape input_shape = arr.shape();
153
154 std::vector<std::size_t> result_dims = result_shape(array, Axis, keepdims);
155
156 // A zero-dimensional tensor cannot store values in the current API.
157 // Represent scalar reductions as a single-element tensor.
158 if (result_dims.empty())
159 {
160 ResultType scalar_result = func(arr);
161 return stratax::container::Tensor<ResultType>(stratax::core::Shape{1}, scalar_result);
162 }
163
164 stratax::container::Tensor<ResultType> result(stratax::core::Shape{result_dims});
165
166 std::vector<std::size_t> output_index(result.rank(), 0);
167 std::vector<stratax::core::Slice> slices;
168
169 do {
170 slices.clear();
171 std::size_t output_position = 0;
172 for (std::size_t dimension = 0; dimension < input_shape.rank(); ++dimension)
173 {
174 if (static_cast<int>(dimension) == Axis)
175 {
176 slices.push_back(stratax::core::Slice{static_cast<std::ptrdiff_t>(0),
177 static_cast<std::ptrdiff_t>(input_shape[dimension])});
178 }
179
180 else
181 {
182 const std::size_t index = keepdims
183 ? output_index[dimension]
184 : output_index[output_position++];
185
186 slices.push_back(stratax::core::Slice{
187 static_cast<std::ptrdiff_t>(index),
188 static_cast<std::ptrdiff_t>(index + 1)
189 });
190 }
191 }
192 auto s = slice(arr, slices);
193 ResultType value = func(s);
194 result(output_index) = value;
195 }
196 while (reduction::advance(result.shape(), output_index));
197
198 return result;
199}
200
201// Global Reductions
202
211template<Array A>
212typename A::value_type sum(const A& arr)
213{
214 return std::accumulate(
215 arr.begin(),
216 arr.end(),
217 typename A::value_type(0)
218 );
219}
220
229template<Array A>
230typename A::value_type prod(const A& arr)
231{
232 return std::accumulate(
233 arr.begin(),
234 arr.end(),
235 typename A::value_type(1),
236 std::multiplies<typename A::value_type>()
237 );
238}
239
248template<Array A>
249typename A::value_type max(const A& arr)
250{
251 auto result = std::max_element(
252 arr.begin(),
253 arr.end()
254 );
255
256 return *result;
257}
258
267template<Array A>
268typename A::value_type min(const A& arr)
269{
270 auto result = std::min_element(
271 arr.begin(),
272 arr.end()
273 );
274
275 return *result;
276}
277
286template<Array A>
287std::size_t argmax(const A& arr)
288{
289 auto result = std::max_element(
290 arr.begin(),
291 arr.end()
292 );
293
294 return static_cast<std::size_t>(std::distance(arr.begin(), result));
295}
296
305template<Array A>
306std::size_t argmin(const A& arr)
307{
308 auto result = std::min_element(
309 arr.begin(),
310 arr.end()
311 );
312
313 return static_cast<std::size_t>(std::distance(arr.begin(), result));
314}
315
324template<Array A>
325double mean(const A& arr)
326{
327 return static_cast<double>(sum(arr)) / static_cast<double>(arr.size());
328}
329
338template<Array A>
339double var(const A& arr)
340{
341 double count = 0.0;
342 double mean_value = 0.0;
343 double m2 = 0.0;
344
345 for (const auto& value : arr)
346 {
347 count += 1.0;
348 const double delta = static_cast<double>(value) - mean_value;
349 mean_value += delta / count;
350 const double delta2 = static_cast<double>(value) - mean_value;
351 m2 += delta * delta2;
352 }
353
354 return count > 0.0 ? m2 / count : 0.0;
355}
356
365template<Array A>
366double std(const A& arr)
367{
368 auto vars = var(arr);
369 return std::sqrt(vars);
370}
371
372
373// Axis Reductions
374
384template<Array A>
385stratax::container::Tensor<typename A::value_type> sum(const A& arr, int axis)
386{
387 return axis_reduce(arr, axis, [](const auto& s) { return reduction::sum(s); });
388}
389
400template<Array A>
401stratax::container::Tensor<typename A::value_type> sum(const A& arr, int axis, bool keepdims)
402{
403 return axis_reduce(arr, axis, [](const auto& s) { return reduction::sum(s); }, keepdims);
404}
405
415template<Array A>
416stratax::container::Tensor<typename A::value_type> prod(const A& arr, int axis)
417{
418 return axis_reduce(arr, axis, [](const auto& s) { return reduction::prod(s); });
419}
420
431template<Array A>
432stratax::container::Tensor<typename A::value_type> prod(const A& arr, int axis, bool keepdims)
433{
434 return axis_reduce(arr, axis, [](const auto& s) { return reduction::prod(s); }, keepdims);
435}
436
446template<Array A>
447stratax::container::Tensor<typename A::value_type> max(const A& arr, int axis)
448{
449 return axis_reduce(arr, axis, [](const auto& s) { return reduction::max(s); });
450}
451
462template<Array A>
463stratax::container::Tensor<typename A::value_type> max(const A& arr, int axis, bool keepdims)
464{
465 return axis_reduce(arr, axis, [](const auto& s) { return reduction::max(s); }, keepdims);
466}
467
477template<Array A>
478stratax::container::Tensor<typename A::value_type> min(const A& arr, int axis)
479{
480 return axis_reduce(arr, axis, [](const auto& s) { return reduction::min(s); });
481}
482
493template<Array A>
494stratax::container::Tensor<typename A::value_type> min(const A& arr, int axis, bool keepdims)
495{
496 return axis_reduce(arr, axis, [](const auto& s) { return reduction::min(s); }, keepdims);
497}
498
508template<Array A>
509stratax::container::Tensor<std::size_t> argmax(const A& arr, int axis)
510{
511 return axis_reduce(arr, axis, [](const auto& s) { return reduction::argmax(s); });
512}
513
524template<Array A>
525stratax::container::Tensor<std::size_t> argmax(const A& arr, int axis, bool keepdims)
526{
527 return axis_reduce(arr, axis, [](const auto& s) { return reduction::argmax(s); }, keepdims);
528}
529
539template<Array A>
540stratax::container::Tensor<std::size_t> argmin(const A& arr, int axis)
541{
542 return axis_reduce(arr, axis, [](const auto& s) { return reduction::argmin(s); });
543}
544
555template<Array A>
556stratax::container::Tensor<std::size_t> argmin(const A& arr, int axis, bool keepdims)
557{
558 return axis_reduce(arr, axis, [](const auto& s) { return reduction::argmin(s); }, keepdims);
559}
560
571template<Array A>
572stratax::container::Tensor<double> mean(const A& arr, int axis, bool keepdims)
573{
574 return axis_reduce(arr, axis, [](const auto& s) { return reduction::mean(s); }, keepdims);
575}
576
586template<Array A>
587stratax::container::Tensor<double> mean(const A& arr, int axis)
588{
589 return axis_reduce(arr, axis, [](const auto& s) { return reduction::mean(s); });
590}
591
602template<Array A>
603stratax::container::Tensor<double> var(const A& arr, int axis, bool keepdims)
604{
605 return axis_reduce(arr, axis, [](const auto& s) { return reduction::var(s); }, keepdims);
606}
607
617template<Array A>
618stratax::container::Tensor<double> var(const A& arr, int axis)
619{
620 return axis_reduce(arr, axis, [](const auto& s) { return reduction::var(s); });
621}
622
633template<Array A>
634stratax::container::Tensor<double> std(const A& arr, int axis, bool keepdims)
635{
636 return axis_reduce(arr, axis, [](const auto& s) { return reduction::std(s); }, keepdims);
637}
638
648template<Array A>
649stratax::container::Tensor<double> std(const A& arr, int axis)
650{
651 return axis_reduce(arr, axis, [](const auto& s) { return reduction::std(s); });
652}
653
654
655}
Shared runtime validation helpers.
const core::Shape & shape() const noexcept
Returns the tensor shape.
Definition Tensor.hpp:159
std::size_t rank() const
Returns the number of stored dimensions.
Definition Shape.hpp:176