1 #ifndef XTENSOR_XBLOCKWISE_REDUCER_HPP
2 #define XTENSOR_XBLOCKWISE_REDUCER_HPP
3
4 #include "xtl/xclosure.hpp"
5 #include "xtl/xsequence.hpp"
6
7 #include "xshape.hpp"
8 #include "xblockwise_reducer_functors.hpp"
9 #include "xmultiindex_iterator.hpp"
10 #include "xreducer.hpp"
11
12 namespace xt
13 {
14
15 template<class CT, class F, class X, class O>
16 class xblockwise_reducer
17 {
18 public:
19
20 using self_type = xblockwise_reducer<CT, F, X, O>;
21 using raw_options_type = std::decay_t<O>;
22 using keep_dims = xtl::mpl::contains<raw_options_type, xt::keep_dims_type>;
23 using xexpression_type = std::decay_t<CT>;
24 using shape_type = typename xreducer_shape_type<typename xexpression_type::shape_type, std::decay_t<X>, keep_dims>::type;
25 using functor_type = F;
26 using value_type = typename functor_type::value_type;
27 using input_shape_type = typename xexpression_type::shape_type;
28 using input_chunk_index_type = filter_fixed_shape_t<input_shape_type>;
29 using input_grid_strides = filter_fixed_shape_t<input_shape_type>;
30 using axes_type = X;
31 using chunk_shape_type = filter_fixed_shape_t<shape_type>;
32
33
34 template<class E, class BS, class XX, class OO, class FF>
35 xblockwise_reducer(E && e, BS && block_shape, XX && axes, OO && options, FF && functor);
36
37 const input_shape_type & input_shape()const;
38 const axes_type & axes() const;
39
40 std::size_t dimension() const;
41
42 const shape_type & shape() const;
43
44 const chunk_shape_type & chunk_shape() const;
45
46 template<class R>
47 void assign_to(R & result) const;
48
49 private:
50 using mapping_type = filter_fixed_shape_t<shape_type>;
51 using input_chunked_view_type = xchunked_view<const std::decay_t<CT> &>;
52 using input_const_chunked_iterator_type = typename input_chunked_view_type::const_chunk_iterator;
53 using input_chunk_range_type = std::array<xmultiindex_iterator<input_chunk_index_type>, 2>;
54
55 template<class CI>
56 void assign_to_chunk(CI & result_chunk_iter) const;
57
58 template<class CI>
59 input_chunk_range_type compute_input_chunk_range(CI & result_chunk_iter) const;
60
61 input_const_chunked_iterator_type get_input_chunk_iter(input_chunk_index_type input_chunk_index) const;
62 void init_shapes();
63
64 CT m_e;
65 xchunked_view<const std::decay_t<CT> &> m_e_chunked_view;
66 axes_type m_axes;
67 raw_options_type m_options;
68 functor_type m_functor;
69 shape_type m_result_shape;
70 chunk_shape_type m_result_chunk_shape;
71 mapping_type m_mapping;
72 input_grid_strides m_input_grid_strides;
73 };
74
75
76 template<class CT, class F, class X, class O>
77 template<class E, class BS, class XX, class OO, class FF>
xblockwise_reducer(E && e,BS && block_shape,XX && axes,OO && options,FF && functor)78 xblockwise_reducer<CT, F, X, O>::xblockwise_reducer(E && e, BS && block_shape, XX && axes, OO && options, FF && functor)
79 : m_e(std::forward<E>(e)),
80 m_e_chunked_view(m_e, std::forward<BS>(block_shape)),
81 m_axes(std::forward<XX>(axes)),
82 m_options(std::forward<OO>(options)),
83 m_functor(std::forward<FF>(functor)),
84 m_result_shape(),
85 m_result_chunk_shape(),
86 m_mapping(),
87 m_input_grid_strides()
88 {
89 init_shapes();
90 resize_container(m_input_grid_strides, m_e.dimension());
91 std::size_t stride = 1;
92
93 for (std::size_t i = m_input_grid_strides.size(); i != 0; --i)
94 {
95 m_input_grid_strides[i-1] = stride;
96 stride *= m_e_chunked_view.grid_shape()[i - 1];
97 }
98 }
99
100 template<class CT, class F, class X, class O>
input_shape() const101 inline auto xblockwise_reducer<CT, F, X, O>::input_shape()const -> const input_shape_type &
102 {
103 return m_e.shape();
104 }
105
106 template<class CT, class F, class X, class O>
axes() const107 inline auto xblockwise_reducer<CT, F, X, O>::axes() const -> const axes_type &
108 {
109 return m_axes;
110 }
111
112 template<class CT, class F, class X, class O>
dimension() const113 inline std::size_t xblockwise_reducer<CT, F, X, O>::dimension() const
114 {
115 return m_result_shape.size();
116 }
117
118 template<class CT, class F, class X, class O>
shape() const119 inline auto xblockwise_reducer<CT, F, X, O>::shape() const -> const shape_type &
120 {
121 return m_result_shape;
122 }
123
124 template<class CT, class F, class X, class O>
chunk_shape() const125 inline auto xblockwise_reducer<CT, F, X, O>::chunk_shape() const -> const chunk_shape_type &
126 {
127 return m_result_chunk_shape;
128 }
129
130 template<class CT, class F, class X, class O>
131 template<class R>
assign_to(R & result) const132 inline void xblockwise_reducer<CT, F, X, O>::assign_to(R & result) const
133 {
134 auto result_chunked_view = as_chunked(result, m_result_chunk_shape);
135 for(auto chunk_iter = result_chunked_view.chunk_begin(); chunk_iter != result_chunked_view.chunk_end(); ++chunk_iter)
136 {
137 assign_to_chunk(chunk_iter);
138 }
139 }
140
141 template<class CT, class F, class X, class O>
get_input_chunk_iter(input_chunk_index_type input_chunk_index) const142 auto xblockwise_reducer<CT, F, X, O>::get_input_chunk_iter(input_chunk_index_type input_chunk_index) const ->input_const_chunked_iterator_type
143 {
144 std::size_t chunk_linear_index = 0;
145 for(std::size_t i=0; i<m_e_chunked_view.dimension(); ++i)
146 {
147 chunk_linear_index += input_chunk_index[i] * m_input_grid_strides[i];
148 }
149 return input_const_chunked_iterator_type(m_e_chunked_view, std::move(input_chunk_index), chunk_linear_index);
150 }
151
152 template<class CT, class F, class X, class O>
153 template<class CI>
assign_to_chunk(CI & result_chunk_iter) const154 void xblockwise_reducer<CT, F, X, O>::assign_to_chunk(CI & result_chunk_iter) const
155 {
156 auto result_chunk_view = *result_chunk_iter;
157 auto reduction_variable = m_functor.reduction_variable(result_chunk_view);
158
159 // get the range of input chunks we need to compute the desired ouput chunk
160 auto range = compute_input_chunk_range(result_chunk_iter);
161
162 // iterate over input chunk (indics)
163 auto first = true;
164 // std::for_each(std::get<0>(range), std::get<1>(range), [&](auto && input_chunk_index)
165 auto iter = std::get<0>(range);
166 while(iter != std::get<1>(range))
167 {
168 const auto & input_chunk_index = *iter;
169 // get input chunk iterator from chunk index
170 auto chunked_input_iter = this->get_input_chunk_iter(input_chunk_index);
171 auto input_chunk_view = *chunked_input_iter;
172
173 // compute the per block result
174 auto block_res = m_functor.compute(input_chunk_view, m_axes, m_options);
175
176 // merge
177 m_functor.merge(block_res, first, result_chunk_view, reduction_variable);
178 first = false;
179 ++iter;
180 }
181
182 // finalize (ie smth like normalization)
183 m_functor.finalize(reduction_variable, result_chunk_view, *this);
184 }
185
186 template<class CT, class F, class X, class O>
187 template<class CI>
compute_input_chunk_range(CI & result_chunk_iter) const188 auto xblockwise_reducer<CT, F, X, O>::compute_input_chunk_range(CI & result_chunk_iter) const -> input_chunk_range_type
189 {
190 auto input_chunks_begin = xtl::make_sequence<input_chunk_index_type>(m_e_chunked_view.dimension(), 0);
191 auto input_chunks_end = xtl::make_sequence<input_chunk_index_type>(m_e_chunked_view.dimension());
192
193 XTENSOR_ASSERT(input_chunks_begin.size() == m_e_chunked_view.dimension());
194 XTENSOR_ASSERT(input_chunks_end.size() == m_e_chunked_view.dimension());
195
196 std::copy(m_e_chunked_view.grid_shape().begin(), m_e_chunked_view.grid_shape().end(), input_chunks_end.begin());
197
198 const auto & chunk_index = result_chunk_iter.chunk_index();
199 for(std::size_t result_ax_index=0; result_ax_index<m_result_shape.size(); ++result_ax_index)
200 {
201 if(m_result_shape[result_ax_index] != 1)
202 {
203 const auto input_ax_index = m_mapping[result_ax_index];
204 input_chunks_begin[input_ax_index] = chunk_index[result_ax_index];
205 input_chunks_end[input_ax_index] = chunk_index[result_ax_index] + 1;
206 }
207 }
208 return input_chunk_range_type{
209 multiindex_iterator_begin<input_chunk_index_type>(input_chunks_begin, input_chunks_end),
210 multiindex_iterator_end<input_chunk_index_type>(input_chunks_begin, input_chunks_end)
211 };
212 }
213
214
215 template<class CT, class F, class X, class O>
init_shapes()216 void xblockwise_reducer<CT, F, X, O>::init_shapes()
217 {
218 const auto & shape = m_e.shape();
219 const auto dimension = m_e.dimension();
220 const auto & block_shape = m_e_chunked_view.chunk_shape();
221 if (xtl::mpl::contains<raw_options_type, xt::keep_dims_type>::value)
222 {
223 resize_container(m_result_shape, dimension);
224 resize_container(m_result_chunk_shape, dimension);
225 resize_container(m_mapping, dimension);
226 for (std::size_t i = 0; i < dimension; ++i)
227 {
228 m_mapping[i] = i;
229 if (std::find(m_axes.begin(), m_axes.end(), i) == m_axes.end())
230 {
231 // i not in m_axes!
232 m_result_shape[i] = shape[i];
233 m_result_chunk_shape[i] = block_shape[i];
234 }
235 else
236 {
237 m_result_shape[i] = 1;
238 m_result_chunk_shape[i] = 1;
239 }
240 }
241 }
242 else
243 {
244 const auto result_dim = dimension - m_axes.size();
245 resize_container(m_result_shape, result_dim);
246 resize_container(m_result_chunk_shape, result_dim);
247 resize_container(m_mapping, result_dim);
248
249 for (std::size_t i = 0, idx = 0; i < dimension; ++i)
250 {
251 if (std::find(m_axes.begin(), m_axes.end(), i) == m_axes.end())
252 {
253 // i not in axes!
254 m_result_shape[idx] = shape[i];
255 m_result_chunk_shape[idx] = block_shape[i];
256 m_mapping[idx] = i;
257 ++idx;
258 }
259 }
260 }
261 }
262
263 template<class E, class CS, class A, class O, class FF>
blockwise_reducer(E && e,CS && chunk_shape,A && axes,O && raw_options,FF && functor)264 inline auto blockwise_reducer(E && e, CS && chunk_shape, A && axes, O && raw_options, FF && functor)
265 {
266 using functor_type = std::decay_t<FF>;
267 using closure_type = xtl::const_closure_type_t<E>;
268 using axes_type = std::decay_t<A>;
269
270 return xblockwise_reducer<
271 closure_type,
272 functor_type,
273 axes_type,
274 O
275 >(
276 std::forward<E>(e),
277 std::forward<CS>(chunk_shape),
278 std::forward<A>(axes),
279 std::forward<O>(raw_options),
280 std::forward<FF>(functor)
281 );
282 }
283
284 namespace blockwise
285 {
286
287 #define XTENSOR_BLOCKWISE_REDUCER_FUNC(FNAME, FUNCTOR)\
288 template<class T=void, class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
289 XTL_REQUIRES(xtl::negation<is_reducer_options<X>>, xtl::negation<xtl::is_integral<std::decay_t<X>>>)\
290 >\
291 auto FNAME(E && e, BS && block_shape, X && axes, O options = O())\
292 {\
293 using input_expression_type = std::decay_t<E>;\
294 using functor_type = FUNCTOR <typename input_expression_type::value_type, T>;\
295 return blockwise_reducer(\
296 std::forward<E>(e), \
297 std::forward<BS>(block_shape), \
298 std::forward<X>(axes),\
299 std::forward<O>(options),\
300 functor_type());\
301 }\
302 template<class T=void, class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
303 XTL_REQUIRES(xtl::is_integral<std::decay_t<X>>)\
304 >\
305 auto FNAME(E && e, BS && block_shape, X axis, O options = O())\
306 {\
307 std::array<X,1> axes{axis};\
308 using input_expression_type = std::decay_t<E>;\
309 using functor_type = FUNCTOR <typename input_expression_type::value_type, T>;\
310 return blockwise_reducer(\
311 std::forward<E>(e), \
312 std::forward<BS>(block_shape), \
313 axes,\
314 std::forward<O>(options),\
315 functor_type());\
316 }\
317 template<class T=void, class E, class BS, class O = DEFAULT_STRATEGY_REDUCERS,\
318 XTL_REQUIRES(is_reducer_options<O>, xtl::negation<xtl::is_integral<std::decay_t<O>>>)\
319 >\
320 auto FNAME(E && e, BS && block_shape, O options = O())\
321 {\
322 using input_expression_type = std::decay_t<E>;\
323 using axes_type = filter_fixed_shape_t<typename input_expression_type::shape_type>;\
324 axes_type axes = xtl::make_sequence<axes_type>(e.dimension());\
325 XTENSOR_ASSERT(axes.size() == e.dimension());\
326 std::iota(axes.begin(), axes.end(), 0);\
327 using functor_type = FUNCTOR <typename input_expression_type::value_type, T>;\
328 return blockwise_reducer(\
329 std::forward<E>(e), \
330 std::forward<BS>(block_shape), \
331 std::move(axes),\
332 std::forward<O>(options),\
333 functor_type());\
334 }\
335 template<class T=void, class E, class BS, class I, std::size_t N, class O = DEFAULT_STRATEGY_REDUCERS>\
336 auto FNAME(E && e, BS && block_shape, const I (&axes)[N], O options = O())\
337 {\
338 using input_expression_type = std::decay_t<E>;\
339 using functor_type = FUNCTOR <typename input_expression_type::value_type, T>;\
340 using axes_type = std::array<std::size_t, N>;\
341 auto ax = xt::forward_normalize<axes_type>(e, axes);\
342 return blockwise_reducer(\
343 std::forward<E>(e), \
344 std::forward<BS>(block_shape), \
345 std::move(ax),\
346 std::forward<O>(options),\
347 functor_type());\
348 }
349 XTENSOR_BLOCKWISE_REDUCER_FUNC(sum, xt::detail::blockwise::sum_functor)
350 XTENSOR_BLOCKWISE_REDUCER_FUNC(prod, xt::detail::blockwise::prod_functor)
351 XTENSOR_BLOCKWISE_REDUCER_FUNC(amin, xt::detail::blockwise::amin_functor)
352 XTENSOR_BLOCKWISE_REDUCER_FUNC(amax, xt::detail::blockwise::amax_functor)
353 XTENSOR_BLOCKWISE_REDUCER_FUNC(mean, xt::detail::blockwise::mean_functor)
354 XTENSOR_BLOCKWISE_REDUCER_FUNC(variance, xt::detail::blockwise::variance_functor)
355 XTENSOR_BLOCKWISE_REDUCER_FUNC(stddev, xt::detail::blockwise::stddev_functor)
356
357 #undef XTENSOR_BLOCKWISE_REDUCER_FUNC
358
359
360 // norm reducers do *not* allow to to pass a template
361 // parameter to specifiy the internal computation type
362 #define XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(FNAME, FUNCTOR)\
363 template<class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
364 XTL_REQUIRES(xtl::negation<is_reducer_options<X>>, xtl::negation<xtl::is_integral<std::decay_t<X>>>)\
365 >\
366 auto FNAME(E && e, BS && block_shape, X && axes, O options = O())\
367 {\
368 using input_expression_type = std::decay_t<E>;\
369 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
370 return blockwise_reducer(\
371 std::forward<E>(e), \
372 std::forward<BS>(block_shape), \
373 std::forward<X>(axes),\
374 std::forward<O>(options),\
375 functor_type());\
376 }\
377 template<class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
378 XTL_REQUIRES(xtl::is_integral<std::decay_t<X>>)\
379 >\
380 auto FNAME(E && e, BS && block_shape, X axis, O options = O())\
381 {\
382 std::array<X,1> axes{axis};\
383 using input_expression_type = std::decay_t<E>;\
384 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
385 return blockwise_reducer(\
386 std::forward<E>(e), \
387 std::forward<BS>(block_shape), \
388 axes,\
389 std::forward<O>(options),\
390 functor_type());\
391 }\
392 template<class E, class BS, class O = DEFAULT_STRATEGY_REDUCERS,\
393 XTL_REQUIRES(is_reducer_options<O>, xtl::negation<xtl::is_integral<std::decay_t<O>>>)\
394 >\
395 auto FNAME(E && e, BS && block_shape, O options = O())\
396 {\
397 using input_expression_type = std::decay_t<E>;\
398 using axes_type = filter_fixed_shape_t<typename input_expression_type::shape_type>;\
399 axes_type axes = xtl::make_sequence<axes_type>(e.dimension());\
400 XTENSOR_ASSERT(axes.size() == e.dimension());\
401 std::iota(axes.begin(), axes.end(), 0);\
402 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
403 return blockwise_reducer(\
404 std::forward<E>(e), \
405 std::forward<BS>(block_shape), \
406 std::move(axes),\
407 std::forward<O>(options),\
408 functor_type());\
409 }\
410 template<class E, class BS, class I, std::size_t N, class O = DEFAULT_STRATEGY_REDUCERS>\
411 auto FNAME(E && e, BS && block_shape, const I (&axes)[N], O options = O())\
412 {\
413 using input_expression_type = std::decay_t<E>;\
414 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
415 using axes_type = std::array<std::size_t, N>;\
416 auto ax = xt::forward_normalize<axes_type>(e, axes);\
417 return blockwise_reducer(\
418 std::forward<E>(e), \
419 std::forward<BS>(block_shape), \
420 std::move(ax),\
421 std::forward<O>(options),\
422 functor_type());\
423 }
424 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_l0, xt::detail::blockwise::norm_l0_functor)
425 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_l1, xt::detail::blockwise::norm_l1_functor)
426 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_l2, xt::detail::blockwise::norm_l2_functor)
427 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_sq, xt::detail::blockwise::norm_sq_functor)
428 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_linf, xt::detail::blockwise::norm_linf_functor)
429
430 #undef XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC
431
432
433 #define XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(FNAME, FUNCTOR)\
434 template<class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
435 XTL_REQUIRES(xtl::negation<is_reducer_options<X>>, xtl::negation<xtl::is_integral<std::decay_t<X>>>)\
436 >\
437 auto FNAME(E && e, BS && block_shape, double p, X && axes, O options = O())\
438 {\
439 using input_expression_type = std::decay_t<E>;\
440 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
441 return blockwise_reducer(\
442 std::forward<E>(e), \
443 std::forward<BS>(block_shape), \
444 std::forward<X>(axes),\
445 std::forward<O>(options),\
446 functor_type(p));\
447 }\
448 template<class E, class BS, class X, class O = DEFAULT_STRATEGY_REDUCERS,\
449 XTL_REQUIRES(xtl::is_integral<std::decay_t<X>>)\
450 >\
451 auto FNAME(E && e, BS && block_shape, double p, X axis, O options = O())\
452 {\
453 std::array<X,1> axes{axis};\
454 using input_expression_type = std::decay_t<E>;\
455 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
456 return blockwise_reducer(\
457 std::forward<E>(e), \
458 std::forward<BS>(block_shape), \
459 axes,\
460 std::forward<O>(options),\
461 functor_type(p));\
462 }\
463 template<class E, class BS, class O = DEFAULT_STRATEGY_REDUCERS,\
464 XTL_REQUIRES(is_reducer_options<O>, xtl::negation<xtl::is_integral<std::decay_t<O>>>)\
465 >\
466 auto FNAME(E && e, BS && block_shape, double p, O options = O())\
467 {\
468 using input_expression_type = std::decay_t<E>;\
469 using axes_type = filter_fixed_shape_t<typename input_expression_type::shape_type>;\
470 axes_type axes = xtl::make_sequence<axes_type>(e.dimension());\
471 XTENSOR_ASSERT(axes.size() == e.dimension());\
472 std::iota(axes.begin(), axes.end(), 0);\
473 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
474 return blockwise_reducer(\
475 std::forward<E>(e), \
476 std::forward<BS>(block_shape), \
477 std::move(axes),\
478 std::forward<O>(options),\
479 functor_type(p));\
480 }\
481 template<class E, class BS, class I, std::size_t N, class O = DEFAULT_STRATEGY_REDUCERS>\
482 auto FNAME(E && e, BS && block_shape, double p, const I (&axes)[N], O options = O())\
483 {\
484 using input_expression_type = std::decay_t<E>;\
485 using functor_type = FUNCTOR <typename input_expression_type::value_type>;\
486 using axes_type = std::array<std::size_t, N>;\
487 auto ax = xt::forward_normalize<axes_type>(e, axes);\
488 return blockwise_reducer(\
489 std::forward<E>(e), \
490 std::forward<BS>(block_shape), \
491 std::move(ax),\
492 std::forward<O>(options),\
493 functor_type(p));\
494 }
495
496 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_lp_to_p, xt::detail::blockwise::norm_lp_to_p_functor);
497 XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC(norm_lp, xt::detail::blockwise::norm_lp_functor);
498
499 #undef XTENSOR_BLOCKWISE_NORM_REDUCER_FUNC
500 }
501
502 }
503
504 #endif