16 #ifndef OPENKALMAN_N_ARY_OPERATION_HPP 17 #define OPENKALMAN_N_ARY_OPERATION_HPP 29 template<std::size_t...ixs,
typename DTup,
typename...Args>
30 constexpr
void check_n_ary_dims(std::index_sequence<ixs...>,
const DTup& d_tup,
const Args&...args)
32 return ([](
const DTup& d_tup,
const Args&...args){
33 constexpr
auto ix = ixs;
34 return ([](
const DTup& d_tup,
const auto& arg){
35 using Arg = decltype(arg);
36 if constexpr (dynamic_dimension<Arg, ix> or dynamic_pattern<std::tuple_element_t<ix, DTup>>)
38 auto arg_d = get_vector_space_descriptor<ix>(arg);
39 auto tup_d = std::get<ix>(d_tup);
42 if (not (arg_d == tup_d) and not internal::is_uniform_component_of(arg_d, tup_d))
43 throw std::logic_error {
"In an argument to n_ary_operation, the dimension of index " +
44 std::to_string(ix) +
" is " + std::to_string(dim_arg_d) +
", but should be 1 " +
45 (dim_tup_d == 1 ?
"" :
"or " + std::to_string(dim_tup_d)) +
46 "(the dimension of Ds number " + std::to_string(ix)};
50 using D_Arg = vector_space_descriptor_of_t<Arg, ix>;
51 using D = std::tuple_element_t<ix, DTup>;
52 static_assert(compares_with<D_Arg, D> or equivalent_to_uniform_static_vector_space_descriptor_component_of<D_Arg, D>or
53 (ix >= index_count_v<Arg> and uniform_static_vector_space_descriptor<D>),
54 "In argument to n_ary_operation, the dimension of each index must be either 1 or that of Ds.");
57 }(d_tup, args...),...);
61 template<
typename Op,
typename...Args, std::size_t...I>
62 constexpr
bool is_invocable_with_indices(std::index_sequence<I...>)
64 return std::is_invocable_v<Op, Args..., decltype(I)...>;
68 template<
typename Op, std::size_t...I,
typename...Args>
69 constexpr decltype(
auto) n_ary_invoke_op(const Op& op,
std::index_sequence<I...> seq, Args&&...args)
71 if constexpr (is_invocable_with_indices<const Op&, Args&&...>(seq))
72 return op(std::forward<Args>(args)...,
static_cast<decltype(I)
>(0)...);
74 return op(std::forward<Args>(args)...);
79 template<
typename Operation, std::size_t indices,
typename...Args>
81 template<
typename Operation, std::size_t indices,
typename = void,
typename...Args>
87 template<
typename Op, std::size_t indices,
typename...Args>
88 requires (is_invocable_with_indices<Op, Args...>(std::make_index_sequence<indices> {})) or
89 std::is_invocable_v<Op, Args...>
92 template<typename Op, std::size_t indices, typename...Args>
94 is_invocable_with_indices<Op, Args...>(std::make_index_sequence<indices> {}) or
95 std::is_invocable_v<Op, Args...>>, Args...>
98 using type = decltype(n_ary_invoke_op(std::declval<Op>(), std::make_index_sequence<indices> {}, std::declval<Args>()...));
102 template<
typename Op, std::size_t indices,
typename...Args>
104 #ifdef __cpp_concepts
111 #ifndef __cpp_concepts 112 template<
typename Op, std::size_t Indices,
typename = void,
typename...Args>
115 template<
typename Op, std::size_t Indices,
typename...Args>
117 std::is_invocable<Op, typename std::add_lvalue_reference<typename scalar_type_of<Args>::type>::type...>::value or
118 is_invocable_with_indices<Op, typename std::add_lvalue_reference<typename scalar_type_of<Args>::type>::type...>(
119 std::make_index_sequence<Indices> {})>, Args...>
124 template<
typename Op, std::size_t Indices,
typename...Args>
125 #ifdef __cpp_concepts 126 concept n_ary_operator = std::is_invocable_v<Op, std::add_lvalue_reference_t<scalar_type_of_t<Args>>...> or
127 is_invocable_with_indices<Op, std::add_lvalue_reference_t<scalar_type_of_t<Args>>...>(std::make_index_sequence<Indices> {});
133 template<
typename Arg, std::size_t...I,
typename...J>
134 inline auto n_ary_operation_get_component_impl(Arg&& arg, std::index_sequence<I...>, J...j)
136 if constexpr (
sizeof...(I) ==
sizeof...(J))
137 return get_component(std::forward<Arg>(arg), (j < get_index_dimension_of<I>(arg) ? j : 0)...);
139 return get_component(std::forward<Arg>(arg), [](
auto dim,
const auto& j_tup){
140 auto j = std::get<I>(j_tup);
141 if (j < dim)
return j;
143 }(get_index_dimension_of<I>(arg), std::tuple {j...})...);
147 template<
typename Op,
typename ArgsTup, std::size_t...ArgI,
typename...J>
148 inline auto n_ary_operation_get_component(
const Op& op, ArgsTup&& args_tup, std::index_sequence<ArgI...>, J...j)
150 if constexpr (std::is_invocable_v<
const Op&,
scalar_type_of_t<std::tuple_element_t<ArgI, std::decay_t<ArgsTup>>>..., J...>)
151 return op(n_ary_operation_get_component_impl(
152 std::get<ArgI>(std::forward<ArgsTup>(args_tup)),
153 std::make_index_sequence<index_count_v<std::tuple_element_t<ArgI, std::decay_t<ArgsTup>>>> {},
156 return op(n_ary_operation_get_component_impl(
157 std::get<ArgI>(std::forward<ArgsTup>(args_tup)),
158 std::make_index_sequence<index_count_v<std::tuple_element_t<ArgI, std::decay_t<ArgsTup>>>> {},
163 template<
typename M,
typename Op,
typename ArgsTup,
typename...J>
164 inline void n_ary_operation_iterate(M& m,
const Op& op, ArgsTup&& args_tup, std::index_sequence<>, J...j)
166 std::make_index_sequence<std::tuple_size_v<ArgsTup>> seq;
167 set_component(m, n_ary_operation_get_component(op, std::forward<ArgsTup>(args_tup), seq, j...), j...);
171 template<
typename M,
typename Op,
typename ArgsTup, std::size_t I, std::size_t...Is,
typename...J>
172 inline void n_ary_operation_iterate(M& m,
const Op& op, ArgsTup&& args_tup, std::index_sequence<I, Is...>, J...j)
174 for (std::size_t i = 0; i < get_index_dimension_of<I>(m); i++)
175 n_ary_operation_iterate(m, op, std::forward<ArgsTup>(args_tup), std::index_sequence<Is...> {}, j..., i);
180 template<
typename...Ds,
typename Arg, std::size_t...Ix_Ds>
181 static decltype(
auto)
182 replicate_arg(
const std::tuple<Ds...>& d_tup, Arg&& arg, std::index_sequence<Ix_Ds...>)
186 std::divides<scalar_type_of_t<Arg>>{},
187 get_dimension(std::get<Ix_Ds>(d_tup)),
188 get_index_dimension_of<Ix_Ds>(arg)}...);
192 template<
typename PatternMatrix,
typename...Ds,
typename Op,
typename...Args>
193 static constexpr
auto 194 n_ary_operation_impl(
const std::tuple<Ds...>& d_tup, Op&& op, Args&&...args)
196 constexpr std::index_sequence_for<Ds...> seq;
199 if constexpr (
sizeof...(Args) > 0 and (constant_matrix<Args> and ...) and not is_invocable_with_indices<Op,
scalar_type_of_t<Args>...>(seq))
203 [](
auto&&...as){
return make_constant<PatternMatrix>(std::forward<decltype(as)>(as)...); },
204 std::tuple_cat(std::tuple{std::move(c)}, d_tup));
207 else if constexpr (is_invocable_with_indices<Op, std::add_lvalue_reference_t<
scalar_type_of_t<Args>>...>(seq) and
208 interface::n_ary_operation_defined_for<PatternMatrix,
const std::tuple<Ds...>&, Op&&, Args&&...>)
212 return std::apply([](
auto&& a,
auto&&...vs){
214 }, std::tuple_cat(std::forward_as_tuple(std::move(ret)), d_tup));
221 if constexpr (((coordinates::dimension_of_v<Ds> == 1) and ...))
225 return make_dense_object_from<PatternMatrix, Layout::none, Scalar>(d_tup, e);
229 auto m = std::apply([](
auto&&...ds){
230 return make_dense_object<PatternMatrix, Layout::none, Scalar>(std::forward<decltype(ds)>(ds)...);
232 n_ary_operation_iterate(m, op, std::forward_as_tuple(std::forward<Args>(args)...), seq);
309 #ifdef __cpp_concepts 310 template<coordinates::pattern...Ds,
typename Operation,
indexible...Args> requires (
sizeof...(Args) > 0) and
311 detail::n_ary_operator<Operation,
sizeof...(Ds), Args...> and (... and (coordinates::dimension_of_v<Ds> != 0))
314 template<
typename...Ds,
typename Operation,
typename...Args, std::enable_if_t<
315 (coordinates::pattern<Ds> and ...) and (indexible<Args> and ...) and (
sizeof...(Args) > 0) and
316 detail::n_ary_operator<Operation,
sizeof...(Ds), Args...> and (... and (coordinates::dimension_of_v<Ds> != 0)),
int> = 0>
321 detail::check_n_ary_dims(std::index_sequence_for<Ds...> {}, d_tup, args...);
322 using Arg0 = std::decay_t<std::tuple_element_t<0, std::tuple<Args...>>>;
323 return detail::n_ary_operation_impl<Arg0>(d_tup, std::forward<Operation>(
operation), std::forward<Args>(args)...);
329 template<std::size_t ix,
typename Arg,
typename...Args>
330 constexpr
auto find_max_dim(
const Arg& arg,
const Args&...args)
332 if constexpr (
sizeof...(Args) == 0)
334 auto ret = get_vector_space_descriptor<ix>(arg);
335 if constexpr (dynamic_pattern<decltype(ret)>)
337 if (get_dimension(ret) == 0)
throw std::invalid_argument {
"A dimension of an arguments " 338 "to n_ary_operation is zero for at least index " + std::to_string(ix) +
"."};
340 else static_assert(index_dimension_of_v<Arg, ix> != 0,
"Arguments to n_ary_operation cannot have zero dimensions");
345 auto max_d = find_max_dim<ix>(args...);
347 using Max_D = decltype(max_d);
349 if constexpr (fixed_pattern<Arg_D> and fixed_pattern<Max_D>)
351 constexpr
auto dim_arg_d = coordinates::dimension_of_v<Arg_D>;
352 if constexpr (compares_with<Arg_D, Max_D>or (dim_arg_d == 1 and equivalent_to_uniform_static_vector_space_descriptor_component_of<Arg_D, Max_D>))
358 constexpr
auto dim_max_d = coordinates::dimension_of_v<Max_D>;
359 static_assert(dim_max_d != 1 or not equivalent_to_uniform_static_vector_space_descriptor_component_of<Max_D, Arg_D>,
360 "The dimension of arguments to n_ary_operation are not compatible with each other for at least one index.");
361 return get_vector_space_descriptor<ix>(arg);
364 else if constexpr (coordinates::euclidean_pattern<Arg_D> and coordinates::euclidean_pattern<Max_D>)
366 if constexpr (fixed_pattern<Arg_D>)
368 constexpr std::size_t a = coordinates::dimension_of_v<Arg_D>;
369 std::size_t m = get_dimension(max_d);
370 if (a != m and a != 1 and m != 1)
throw std::invalid_argument {
"The dimension of arguments to n_ary_operation " 371 "are not compatible with each other for at least index " + std::to_string(ix) +
"."};
373 if constexpr (a == 1)
return max_d;
374 else return get_vector_space_descriptor<ix>(arg);
376 else if constexpr (fixed_pattern<Max_D>)
378 auto arg_d = get_vector_space_descriptor<ix>(arg);
379 std::size_t a = get_dimension(arg_d);
380 constexpr std::size_t m = coordinates::dimension_of_v<Max_D>;
381 if (a != m and a != 1 and m != 1)
throw std::invalid_argument {
"The dimension of arguments to n_ary_operation " 382 "are not compatible with each other for at least index " + std::to_string(ix) +
"."};
384 if constexpr (m == 1)
return arg_d;
389 std::size_t a = get_index_dimension_of<ix>(arg);
390 std::size_t m = get_dimension(max_d);
391 if (a == m or a == 1)
return m;
392 else if (m == 1 and m <= a)
return a;
393 else throw std::invalid_argument {
"The dimension of arguments to n_ary_operation are not compatible with " 394 "each other for at least index " + std::to_string(ix) +
"."};
399 auto arg_d = get_vector_space_descriptor<ix>(arg);
401 if (internal::is_uniform_component_of(arg_d, max_d))
407 else if (internal::is_uniform_component_of(max_d, arg_d))
413 else throw std::invalid_argument {
"The dimension of arguments to n_ary_operation are not compatible with " 414 "each other for at least index " + std::to_string(ix) +
"."};
420 template<std::size_t...ixs,
typename...Args>
421 constexpr
auto find_max_dims(std::index_sequence<ixs...>,
const Args&...args)
423 return std::tuple {find_max_dim<ixs>(args...)...};
477 #ifdef __cpp_concepts 478 template<
typename Operation,
indexible...Args> requires (
sizeof...(Args) > 0) and
479 detail::n_ary_operator<Operation, std::max({index_count_v<Args>...}), Args...>
481 template<
typename Operation,
typename...Args, std::enable_if_t<(indexible<Args> and ...) and
482 (
sizeof...(Args) > 0) and detail::n_ary_operator<Operation, std::max({
index_count<Args>::value...}), Args...>,
int> = 0>
487 auto d_tup = detail::find_max_dims(std::make_index_sequence<std::max({index_count_v<Args>...})> {}, args...);
488 using Arg0 = std::decay_t<std::tuple_element_t<0, std::tuple<Args...>>>;
489 return detail::n_ary_operation_impl<Arg0>(std::move(d_tup), std::forward<Operation>(
operation), std::forward<Args>(args)...);
495 template<
typename M,
typename Operation,
typename Vs_tuple,
typename Index_seq,
typename K_seq,
typename...Is>
496 void nullary_set_components(M& m,
const Operation& op,
const Vs_tuple&, Index_seq, K_seq, Is...is)
498 constexpr
auto seq = std::make_index_sequence<
sizeof...(Is)> {};
499 if constexpr (detail::is_invocable_with_indices<Operation>(seq))
506 template<std::size_t DsIndex, std::size_t...DsIndices,
typename M,
typename Operation,
507 typename Vs_tuple, std::size_t...indices, std::size_t...Ks,
typename...Is>
508 void nullary_set_components(M& m,
const Operation& op,
const Vs_tuple& ds_tup,
509 std::index_sequence<indices...> index_seq, std::index_sequence<Ks...> k_seq, Is...is)
511 if constexpr (((DsIndex == indices) or ...))
513 constexpr std::integral_constant<size_t, ((DsIndex == indices ? Ks : 0) + ...)> i;
514 nullary_set_components<DsIndices...>(m, op, ds_tup, index_seq, k_seq, is..., i);
519 for (std::size_t i = 0; i < get_dimension(std::get<DsIndex>(ds_tup)); ++i)
521 nullary_set_components<DsIndices...>(m, op, ds_tup, index_seq, k_seq, is..., i);
527 template<std::size_t CurrentOpIndex, std::size_t factor,
typename M,
typename Operations_tuple,
528 typename Vs_tuple,
typename UniqueIndicesSeq, std::size_t...AllDsIndices,
typename K_seq>
529 void nullary_iterate(M& m,
const Operations_tuple& op_tup,
const Vs_tuple& ds_tup,
530 UniqueIndicesSeq unique_indices_seq, std::index_sequence<AllDsIndices...>, K_seq k_seq)
532 nullary_set_components<AllDsIndices...>(m, std::get<CurrentOpIndex>(op_tup), ds_tup, unique_indices_seq, k_seq);
536 template<std::size_t CurrentOpIndex, std::size_t factor, std::size_t
index, std::size_t...indices,
537 typename M,
typename Operations_tuple,
typename Vs_tuple,
typename UniqueIndicesSeq,
typename AllDsSeq,
538 std::size_t...Ks, std::size_t...Js,
typename...J_seqs>
539 void nullary_iterate(M& m,
const Operations_tuple& op_tup,
const Vs_tuple& ds_tup,
540 UniqueIndicesSeq unique_indices_seq, AllDsSeq all_ds_seq, std::index_sequence<Ks...>, std::index_sequence<Js...>,
543 constexpr std::size_t new_factor = factor / coordinates::dimension_of_v<std::tuple_element_t<index, Vs_tuple>>;
545 (nullary_iterate<CurrentOpIndex + new_factor * Js, new_factor, indices...>(
546 m, op_tup, ds_tup, unique_indices_seq, all_ds_seq, std::index_sequence<Ks..., Js>{}, j_seqs...),...);
626 #ifdef __cpp_concepts 627 template<
indexible PatternMatrix, std::size_t...indices, coordinates::pattern...Ds,
typename...Operations>
628 requires ((fixed_pattern<std::tuple_element_t<indices, std::tuple<Ds...>>>) and ...) and
629 (
sizeof...(Operations) == (1 * ... * coordinates::dimension_of_v<std::tuple_element_t<indices, std::tuple<Ds...>>>)) and
630 (detail::n_ary_operator<Operations,
sizeof...(Ds)> and ...)
632 template<
typename PatternMatrix, std::size_t...indices,
typename...Ds,
typename...Operations, std::enable_if_t<
633 indexible<PatternMatrix> and (coordinates::pattern<Ds> and ...) and
634 ((fixed_pattern<std::tuple_element_t<indices, std::tuple<Ds...>>>) and ...) and
636 (detail::n_ary_operator<Operations,
sizeof...(Ds)> and ...),
int> = 0>
644 if constexpr (
sizeof...(Operations) == 1)
646 return detail::n_ary_operation_impl<std::decay_t<PatternMatrix>>(d_tup, operations...);
649 else if constexpr (((not dynamic_pattern<Ds>) and ...) and
650 sizeof...(operations) == (coordinates::dimension_of_v<Ds> * ...) and
651 not (detail::is_invocable_with_indices<const Operations&>(std::make_index_sequence<
sizeof...(Ds)> {}) or ...))
653 return make_dense_object_from<PatternMatrix, Layout::none, Scalar>(d_tup, operations()...);
658 auto m = make_dense_object<PatternMatrix, Layout::none, Scalar>(d_tup);
659 detail::nullary_iterate<0,
sizeof...(Operations), indices...>(
661 std::forward_as_tuple(operations...),
663 std::index_sequence<indices...> {},
664 std::index_sequence_for<Ds...> {},
665 std::index_sequence<> {},
666 std::make_index_sequence<coordinates::dimension_of_v<std::tuple_element_t<indices, std::tuple<Ds...>>>> {}...);
722 #ifdef __cpp_concepts 723 template<
indexible PatternMatrix, std::size_t...indices, coordinates::pattern...Ds,
typename...Operations>
725 (
sizeof...(Operations) == (1 * ... * index_dimension_of_v<PatternMatrix, indices>))
727 template<
typename PatternMatrix, std::size_t...indices,
typename...Ds,
typename...Operations, std::enable_if_t<
728 indexible<PatternMatrix> and (coordinates::pattern<Ds> and ...) and
735 auto d_tup = all_vector_space_descriptors<PatternMatrix>();
736 return n_ary_operation<PatternMatrix, indices...>(d_tup, operations...);
746 template<
typename Operation,
typename Elem,
typename...J>
747 inline void do_elem_operation_in_place_impl(
const Operation&
operation, Elem& elem, J...j)
749 if constexpr (std::is_invocable_v<const Operation&, Elem&, J...>)
756 template<
typename Operation,
typename Arg,
typename...J>
757 inline void do_elem_operation_in_place(
const Operation& operation, Arg& arg, J...j)
760 if constexpr (std::is_assignable_v<decltype((elem)), std::decay_t<decltype(elem)>>)
762 do_elem_operation_in_place_impl(operation, elem, j...);
766 auto e {std::forward<decltype(elem)>(elem)};
767 static_assert(std::is_assignable_v<decltype((e)), std::decay_t<decltype(elem)>>);
768 do_elem_operation_in_place_impl(operation, e, j...);
774 template<
typename Operation,
typename Arg,
typename Count,
typename...J>
775 inline void unary_operation_in_place_impl(
const Operation& operation, Arg& arg,
const Count& count, J...j)
777 constexpr
auto n =
sizeof...(J);
780 for (std::size_t i = 0; i < get_index_dimension_of<n>(arg); ++i)
781 unary_operation_in_place_impl(operation, arg, count, j..., i);
785 do_elem_operation_in_place(operation, arg, j...);
826 #ifdef __cpp_concepts 827 template<
typename Operation, writable Arg> requires detail::n_ary_operator<Operation, index_count_v<Arg>, Arg>
829 template<
typename Operation,
typename Arg, std::enable_if_t<writable<Arg> and
830 detail::n_ary_operator<Operation, index_count_v<Arg>, Arg>,
int> = 0>
832 constexpr decltype(
auto)
837 detail::unary_operation_in_place_impl(operation, arg,
count_indices(arg));
838 return std::forward<Arg>(arg);
844 #endif //OPENKALMAN_N_ARY_OPERATION_HPP constexpr auto count_indices(const T &t)
Get the number of indices available to address the components of an indexible object.
Definition: count_indices.hpp:33
constexpr auto n_ary_operation(const Operations &...operations)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: n_ary_operation.hpp:733
constexpr auto n_ary_operation(const std::tuple< Ds... > &d_tup, Operation &&operation, Args &&...args)
Perform a component-wise n-ary operation, using broadcasting to match the size of a pattern matrix...
Definition: n_ary_operation.hpp:319
Definition: n_ary_operation.hpp:113
An operation involving some number of values.
Definition: operation.hpp:69
Arg && set_component(Arg &&arg, const scalar_type_of_t< Arg > &s, const Indices &indices)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: set_component.hpp:51
auto make_vector_space_adapter(Arg &&arg, Descriptors &&descriptors)
If necessary, wrap an object in a wrapper that adds vector space descriptors for each index...
Definition: make_vector_space_adapter.hpp:37
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
decltype(auto) constexpr unary_operation_in_place(const Operation &operation, Arg &&arg)
Perform a component-wise, in-place unary operation.
Definition: n_ary_operation.hpp:833
The coordinates::pattern for index N of object T.
Definition: vector_space_descriptor_of.hpp:34
constexpr bool indexible
T is a generalized tensor type.
Definition: indexible.hpp:32
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
The size of a coordinates::pattern.
Definition: dimension_of.hpp:37
decltype(auto) constexpr broadcast(Arg &&arg, const Factors &...factors)
Broadcast an object by replicating it by factors specified for each index.
Definition: broadcast.hpp:49
The constant associated with T, assuming T is a constant_matrix.
Definition: constant_coefficient.hpp:36
constexpr auto get_dimension(const Arg &arg)
Get the vector dimension of coordinates::pattern Arg.
Definition: get_dimension.hpp:55
The root namespace for OpenKalman.
Definition: basics.hpp:34
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:37
Definition: n_ary_operation.hpp:103
typename vector_space_descriptor_of< T, N >::type vector_space_descriptor_of_t
helper template for vector_space_descriptor_of.
Definition: vector_space_descriptor_of.hpp:56
The dimension of an index for a matrix, expression, or array.
Definition: index_dimension_of.hpp:34
decltype(auto) constexpr get_component(Arg &&arg, const Indices &indices)
Get a component of an object at a particular set of indices.
Definition: get_component.hpp:54
constexpr bool compatible_with_vector_space_descriptor_collection
indexible T is compatible with pattern_collection D.
Definition: compatible_with_vector_space_descriptor_collection.hpp:74
The minimum number of indices need to access all the components of an object.
Definition: index_count.hpp:33
operation(const Operation &, const Args &...) -> operation< Operation, Args... >
Deduction guide.
Definition: n_ary_operation.hpp:83
constexpr bool index
An object describing a collection of /ref values::index objects.
Definition: index.hpp:75