Stratax 0.3.1
Loading...
Searching...
No Matches
Broadcasting.hpp
1#pragma once
2
3#include <algorithm>
4#include <cstddef>
5#include <vector>
6
7#include <stratax/core/dtypes/Concepts.hpp>
8#include <stratax/exceptions/Exceptions.hpp>
9#include <stratax/core/Shape.hpp>
10#include <stratax/core/ArrayTraits.hpp>
11#include <stratax/core/dtypes/Promotion.hpp>
12
13namespace stratax::core::broadcast_detail {
14
29inline std::size_t dimension_from_right(
30 const Shape& shape,
31 std::size_t offset)
32{
33 return offset < shape.rank()
34 ? shape[shape.rank() - 1 - offset]
35 : 1;
36}
37
61inline std::size_t flat_operand_index(
62 std::size_t result_index,
63 const stratax::core::Shape& result_shape,
64 const stratax::core::Shape& operand_shape)
65{
66 std::size_t operand_index = 0;
67 std::size_t operand_stride = 1;
68
69 for (std::size_t result_axis = result_shape.rank(); result_axis-- > 0;)
70 {
71 const std::size_t coordinate =
72 result_index % result_shape[result_axis];
73 result_index /= result_shape[result_axis];
74
75 const std::size_t offset = result_shape.rank() - 1 - result_axis;
76 if (offset >= operand_shape.rank())
77 {
78 continue;
79 }
80
81 const std::size_t operand_axis = operand_shape.rank() - 1 - offset;
82 const std::size_t operand_dimension = operand_shape[operand_axis];
83 const std::size_t operand_coordinate =
84 operand_dimension == 1 ? 0 : coordinate;
85
86 operand_index += operand_coordinate * operand_stride;
87 operand_stride *= operand_dimension;
88 }
89
90 return operand_index;
91}
92
93} // namespace stratax::core::broadcast_detail
94
111inline bool broadcastable(
112 const stratax::core::Shape& shape1,
113 const stratax::core::Shape& shape2
114)
115{
116 const std::size_t result_rank = std::max(shape1.rank(), shape2.rank());
117
118 for (std::size_t offset = 0; offset < result_rank; ++offset)
119 {
120 const std::size_t left =
121 stratax::core::broadcast_detail::dimension_from_right(shape1, offset);
122 const std::size_t right =
123 stratax::core::broadcast_detail::dimension_from_right(shape2, offset);
124
125 if (left != right && left != 1 && right != 1)
126 {
127 return false;
128 }
129 }
130
131 return true;
132}
133
150inline stratax::core::Shape broadcasted_shape(
151 const stratax::core::Shape& shape1,
152 const stratax::core::Shape& shape2)
153{
154 if (!broadcastable(shape1, shape2))
155 {
156 throw Exceptions::BroadcastError("Shapes are not broadcastable.");
157 }
158
159 const std::size_t result_rank = std::max(shape1.rank(), shape2.rank());
160 std::vector<std::size_t> result(result_rank);
161
162 for (std::size_t offset = 0; offset < result_rank; ++offset)
163 {
164 const std::size_t left =
165 stratax::core::broadcast_detail::dimension_from_right(shape1, offset);
166 const std::size_t right =
167 stratax::core::broadcast_detail::dimension_from_right(shape2, offset);
168
169 result[result_rank - 1 - offset] = left == 1 ? right : left;
170 }
171
172 return stratax::core::Shape{result};
173}
174
175namespace stratax::core {
176
177template<
178 DType Result,
179 Array L,
180 Array R,
181 typename Op>
182auto broadcasted_op(
183 const L& lhs,
184 const R& rhs,
185 Op op)
186{
187 using result_type =
188 promote_array_t<L, R, Result>;
189
190 const auto result_shape =
191 broadcasted_shape(lhs.shape(), rhs.shape());
192
193 result_type result(result_shape);
194
195 for (std::size_t i = 0; i < result.size(); ++i)
196 {
197 const auto lhs_index =
198 broadcast_detail::flat_operand_index(
199 i,
200 result_shape,
201 lhs.shape());
202
203 const auto rhs_index =
204 broadcast_detail::flat_operand_index(
205 i,
206 result_shape,
207 rhs.shape());
208
209 result[i] = static_cast<Result>(
210 op(lhs[lhs_index], rhs[rhs_index]));
211 }
212
213 return result;
214}
215
227template<Array L, Array R, typename Op>
228requires (
231)
232auto broadcasted_op(
233 const L& lhs,
234 const R& rhs,
235 Op op)
236{
237 using result_value_type =
238 promote_t<
239 typename L::value_type,
240 typename R::value_type>;
241
242 return broadcasted_op<result_value_type>(
243 lhs,
244 rhs,
245 op);
246}
247
248}
249
274template<Array L, Numeric S, typename Op>
275auto broadcasted_op(const L& lhs, const S& rhs, Op op)
276{
277 using result_value_type =
278 stratax::core::promote_t<
279 typename L::value_type,
280 S>;
281
282 using result_type =
283 stratax::core::rebind_array_t<
284 L,
285 result_value_type>;
286
287 result_type result(lhs.shape());
288
289 for (std::size_t i = 0; i < result.size(); ++i)
290 {
291 result[i] = static_cast<result_value_type>(
292 op(lhs[i], rhs));
293 }
294
295 return result;
296}
297
323template<Numeric S, Array R, typename Op>
324auto broadcasted_op(const S& lhs, const R& rhs, Op op)
325{
326 using result_value_type =
327 stratax::core::promote_t<
328 S,
329 typename R::value_type>;
330
331 using result_type =
332 stratax::core::rebind_array_t<
333 R,
334 result_value_type>;
335
336 result_type result(rhs.shape());
337
338 for (std::size_t i = 0; i < result.size(); ++i)
339 {
340 result[i] = static_cast<result_value_type>(
341 op(lhs, rhs[i]));
342 }
343
344 return result;
345}
Stores the dimensions of a multidimensional array.
Definition Shape.hpp:33
size_type rank() const noexcept
Returns the number of dimensions.
Definition Shape.hpp:121
Identifies array-like types through their common logical interface.
Definition Concepts.hpp:123