Stratax 0.4.0rc1
Loading...
Searching...
No Matches
Broadcasting.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include <algorithm>
7#include <cstddef>
8#include <vector>
9
15
16namespace stratax::core::broadcast_detail {
17
32inline std::size_t dimension_from_right(
33 const Shape& shape,
34 std::size_t offset)
35{
36 return offset < shape.rank()
37 ? shape[shape.rank() - 1 - offset]
38 : 1;
39}
40
64inline std::size_t flat_operand_index(
65 std::size_t result_index,
66 const stratax::core::Shape& result_shape,
67 const stratax::core::Shape& operand_shape)
68{
69 std::size_t operand_index = 0;
70 std::size_t operand_stride = 1;
71
72 for (std::size_t result_axis = result_shape.rank(); result_axis-- > 0;)
73 {
74 const std::size_t coordinate =
75 result_index % result_shape[result_axis];
76 result_index /= result_shape[result_axis];
77
78 const std::size_t offset = result_shape.rank() - 1 - result_axis;
79 if (offset >= operand_shape.rank())
80 {
81 continue;
82 }
83
84 const std::size_t operand_axis = operand_shape.rank() - 1 - offset;
85 const std::size_t operand_dimension = operand_shape[operand_axis];
86 const std::size_t operand_coordinate =
87 operand_dimension == 1 ? 0 : coordinate;
88
89 operand_index += operand_coordinate * operand_stride;
90 operand_stride *= operand_dimension;
91 }
92
93 return operand_index;
94}
95
96} // namespace stratax::core::broadcast_detail
97
114inline bool broadcastable(
115 const stratax::core::Shape& shape1,
116 const stratax::core::Shape& shape2
117)
118{
119 const std::size_t result_rank = std::max(shape1.rank(), shape2.rank());
120
121 for (std::size_t offset = 0; offset < result_rank; ++offset)
122 {
123 const std::size_t left =
124 stratax::core::broadcast_detail::dimension_from_right(shape1, offset);
125 const std::size_t right =
126 stratax::core::broadcast_detail::dimension_from_right(shape2, offset);
127
128 if (left != right && left != 1 && right != 1)
129 {
130 return false;
131 }
132 }
133
134 return true;
135}
136
154 const stratax::core::Shape& shape1,
155 const stratax::core::Shape& shape2)
156{
157 if (!broadcastable(shape1, shape2))
158 {
159 throw Exceptions::BroadcastError("Shapes are not broadcastable.");
160 }
161
162 const std::size_t result_rank = std::max(shape1.rank(), shape2.rank());
163 std::vector<std::size_t> result(result_rank);
164
165 for (std::size_t offset = 0; offset < result_rank; ++offset)
166 {
167 const std::size_t left =
168 stratax::core::broadcast_detail::dimension_from_right(shape1, offset);
169 const std::size_t right =
170 stratax::core::broadcast_detail::dimension_from_right(shape2, offset);
171
172 result[result_rank - 1 - offset] = left == 1 ? right : left;
173 }
174
175 return stratax::core::Shape{result};
176}
177
178namespace stratax::core {
179
180namespace broadcast_detail {
181
189template<Array L, Array R>
190Shape array_result_shape(const L& lhs, const R& rhs)
191{
192 auto result = broadcasted_shape(lhs.shape(), rhs.shape());
193 if (result.elements() != 0 && (lhs.empty() || rhs.empty()))
194 {
195 throw Exceptions::BroadcastError("An empty array cannot broadcast to a nonempty result.");
196 }
197 return result;
198}
199
200} // namespace broadcast_detail
201
216template<
217 DType Result,
218 Array L,
219 Array R,
220 typename Op>
222 const L& lhs,
223 const R& rhs,
224 Op op)
225{
226 using result_type =
228
229 const auto result_shape =
230 broadcast_detail::array_result_shape(lhs, rhs);
231
232 result_type result(result_shape);
233
234 for (std::size_t i = 0; i < result.size(); ++i)
235 {
236 const auto lhs_index =
237 broadcast_detail::flat_operand_index(
238 i,
239 result_shape,
240 lhs.shape());
241
242 const auto rhs_index =
243 broadcast_detail::flat_operand_index(
244 i,
245 result_shape,
246 rhs.shape());
247
248 result[i] = static_cast<Result>(
249 op(lhs[lhs_index], rhs[rhs_index]));
250 }
251
252 return result;
253}
254
266template<Array L, Array R, typename Op>
267requires (
270)
272 const L& lhs,
273 const R& rhs,
274 Op op)
275{
276 using result_value_type =
277 promote_t<
278 typename L::value_type,
279 typename R::value_type>;
280
281 return broadcasted_op<result_value_type>(
282 lhs,
283 rhs,
284 op);
285}
286
287}
288
313template<Array L, Numeric S, typename Op>
314auto broadcasted_op(const L& lhs, const S& rhs, Op op)
315{
316 using result_value_type =
318 typename L::value_type,
319 S>;
320
321 using result_type =
323 L,
324 result_value_type>;
325
326 result_type result(lhs.shape());
327
328 for (std::size_t i = 0; i < result.size(); ++i)
329 {
330 result[i] = static_cast<result_value_type>(
331 op(lhs[i], rhs));
332 }
333
334 return result;
335}
336
362template<Numeric S, Array R, typename Op>
363auto broadcasted_op(const S& lhs, const R& rhs, Op op)
364{
365 using result_value_type =
367 S,
368 typename R::value_type>;
369
370 using result_type =
372 R,
373 result_value_type>;
374
375 result_type result(rhs.shape());
376
377 for (std::size_t i = 0; i < result.size(); ++i)
378 {
379 result[i] = static_cast<result_value_type>(
380 op(lhs, rhs[i]));
381 }
382
383 return result;
384}
Result-container rebinding and promotion for owning arrays.
typename PromoteArray< std::remove_cvref_t< L >, std::remove_cvref_t< R >, std::remove_cvref_t< T > >::type promote_array_t
Convenience alias for the promoted array container type.
typename RebindArray< std::remove_cvref_t< A >, std::remove_cvref_t< T > >::type rebind_array_t
Convenience alias for rebinding an array to a new value type.
stratax::core::Shape broadcasted_shape(const stratax::core::Shape &shape1, const stratax::core::Shape &shape2)
Computes the common shape produced by broadcasting two shapes.
bool broadcastable(const stratax::core::Shape &shape1, const stratax::core::Shape &shape2)
Tests whether two shapes satisfy trailing-dimension broadcasting.
std::size_t flat_operand_index(std::size_t result_index, const stratax::core::Shape &result_shape, const stratax::core::Shape &operand_shape)
Maps a flat result index to the corresponding operand index.
Shape array_result_shape(const L &lhs, const R &rhs)
Validates shape broadcasting and the availability of operand values.
std::size_t dimension_from_right(const Shape &shape, std::size_t offset)
Returns a shape dimension counted from the trailing axis.
auto broadcasted_op(const L &lhs, const R &rhs, Op op)
Applies a binary callable with broadcasting and an explicit result dtype.
auto broadcasted_op(const L &lhs, const S &rhs, Op op)
Applies a binary callable to every array element and a right scalar.
Dtype and array interface concepts.
Stratax exception types derived from std::runtime_error.
Pairwise dtype promotion rules.
typename Promote< std::remove_cvref_t< L >, std::remove_cvref_t< R > >::type promote_t
Convenience alias for the promoted dtype of two types.
Immutable dimension metadata and checked row-major strides.
Operands cannot broadcast to the requested result.
Stores the dimensions of a multidimensional array.
Definition Shape.hpp:36
size_type rank() const noexcept
Returns the number of dimensions.
Definition Shape.hpp:124
Identifies array-like types through their common logical interface.
Definition Concepts.hpp:134
Supported numeric or boolean element dtype.
Definition Concepts.hpp:74
Supported integer, floating-point, or complex dtype, excluding bool.
Definition Concepts.hpp:67