OpenKalman
chipwise_operation.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2022 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_CHIPWISE_OPERATION_HPP
17 #define OPENKALMAN_CHIPWISE_OPERATION_HPP
18 
19 namespace OpenKalman
20 {
21  namespace detail
22  {
23  //-- chipwise_vector_space_descriptor_for --//
24 
25  template<std::size_t ix, typename Best_d>
26  const Best_d& chipwise_vector_space_descriptor_for(const Best_d& best_d) { return best_d; }
27 
28  template<std::size_t ix, typename Best_d, typename Arg, typename...Args>
29  decltype(auto) chipwise_vector_space_descriptor_for(const Best_d& best_d, const Arg& arg, const Args&...args)
30  {
31  auto d = get_vector_space_descriptor<ix>(arg);
32  using D = decltype(d);
33  if constexpr (fixed_pattern<Best_d>)
34  {
35  if constexpr (fixed_pattern<D>)
36  static_assert(coordinates::dimension_of_v<D> == coordinates::dimension_of_v<Best_d>,
37  "Arguments to chipwise_operation must have matching vector space descriptors.");
38  else
39  if (d != best_d) throw std::invalid_argument {"Arguments to chipwise_operation must have matching vector space descriptors."};
40  return chipwise_vector_space_descriptor_for<ix>(best_d, args...);
41  }
42  else // dynamic_pattern<Best_d>
43  {
44  if (d != best_d) throw std::invalid_argument {"Arguments to chipwise_operation must have matching vector space descriptors."};
45  if constexpr (fixed_pattern<D>)
46  return chipwise_vector_space_descriptor_for<ix>(d, args...);
47  else
48  return chipwise_vector_space_descriptor_for<ix>(best_d, args...);
49  }
50  }
51 
52  //-- make_chipwise_default --//
53 
54  template<std::size_t...ix, typename Arg, typename...Args>
55  auto make_chipwise_default(std::index_sequence<ix...>, const Arg& arg, const Args&...args)
56  {
57  return make_dense_object<Arg>(chipwise_vector_space_descriptor_for<ix>(get_vector_space_descriptor<ix>(arg), args...)...);
58  }
59 
60  //-- chipwise_op_chip --//
61 
62  template<bool uses_indices, std::size_t...indices, std::size_t...indices_ix,
63  typename Ix_tup, typename M, typename Op, typename...Args>
64  constexpr void
65  chipwise_op_chip(std::index_sequence<indices...>, std::index_sequence<indices_ix...>, const Ix_tup& ix_tup,
66  M& m, const Op& op, Args&&...args)
67  {
68  if constexpr (uses_indices)
69  {
70  auto chip = op(get_chip<indices...>(std::forward<Args>(args), std::get<indices_ix>(ix_tup)...)..., std::get<indices_ix>(ix_tup)...);
71  set_chip<indices...>(m, std::move(chip), std::get<indices_ix>(ix_tup)...);
72  }
73  else
74  {
75  auto chip = op(get_chip<indices...>(std::forward<Args>(args), std::get<indices_ix>(ix_tup)...)...);
76  set_chip<indices...>(m, std::move(chip), std::get<indices_ix>(ix_tup)...);
77  }
78  }
79 
80  //-- chipwise_op --//
81 
82  template<bool uses_indices, typename Indices, typename Ix_tup, typename M, typename Op, typename...Args>
83  constexpr void
84  chipwise_op(Indices indices, const Ix_tup& ix_tup, M& m, const Op& op, Args&&...args)
85  {
86  constexpr auto num_indices = Indices::size();
87  static_assert(std::tuple_size_v<Ix_tup> == num_indices);
88  std::make_index_sequence<num_indices> indices_seq;
89  chipwise_op_chip<uses_indices>(indices, indices_seq, ix_tup, m, op, std::forward<Args>(args)...);
90  }
91 
92  template<bool uses_indices, std::size_t index, std::size_t...indices,
93  typename Indices_seq, typename Ix_tup, typename M, typename Op, typename...Args>
94  constexpr void
95  chipwise_op(Indices_seq indices_seq, const Ix_tup& ix_tup, M& m, const Op& op, Args&&...args)
96  {
97  for (std::size_t i = 0; i < get_index_dimension_of<index>(m); ++i)
98  chipwise_op<uses_indices, indices...>(indices_seq, std::tuple_cat(ix_tup, std::tuple{i}), m, op, args...);
99  }
100 
101  } // namespace detail
102 
103 
120 #ifdef __cpp_concepts
121  template<std::size_t...indices, typename Operation, indexible...Args> requires (sizeof...(Args) > 0)
122 #else
123  template<std::size_t...indices, typename Operation, typename...Args, std::enable_if_t<
124  (indexible<Args> and ...) and (sizeof...(Args) > 0), int> = 0>
125 #endif
126  constexpr auto
127  chipwise_operation(const Operation& operation, Args&&...args)
128  {
129  if constexpr (sizeof...(indices) > 0)
130  {
131  auto m = detail::make_chipwise_default(std::make_index_sequence<std::max({index_count_v<Args>...})>{}, args...);
132 
133  constexpr bool uses_indices = std::is_invocable_v<Operation,
134  decltype(get_chip<indices...>(std::declval<Args>(), std::integral_constant<decltype(indices), 0>{}...))...,
135  std::integral_constant<decltype(indices), 0>...>;
136 
137  std::index_sequence<indices...> indices_seq;
138  detail::chipwise_op<uses_indices, indices...>(indices_seq, std::tuple{}, m, operation, std::forward<Args>(args)...);
139  return m;
140  }
141  else
142  {
143  return operation(std::forward<Args>(args)...);
144  }
145  }
146 
147 
148  namespace detail
149  {
150  template<std::size_t op_ix, typename OpResult>
151  auto nullary_chipwise_vector_space_descriptor(const OpResult& op_result)
152  {
153  return get_vector_space_descriptor<op_ix>(op_result);
154  }
155 
156  template<std::size_t op_ix, std::size_t index, std::size_t...indices, typename OpResult, typename I, typename...Is>
157  auto nullary_chipwise_vector_space_descriptor(const OpResult& op_result, I i, Is...is)
158  {
159  if constexpr (op_ix == index) return get_vector_space_descriptor<op_ix>(op_result) * i;
160  else return nullary_chipwise_vector_space_descriptor<op_ix, indices...>(op_result, is...);
161  }
162 
163  template<std::size_t...indices, std::size_t...op_ix, typename OpResult, typename...Is>
164  auto make_nullary_chipwise_default(std::index_sequence<op_ix...>, const OpResult& op_result, Is...is)
165  {
166  return make_dense_object<OpResult>(nullary_chipwise_vector_space_descriptor<op_ix, indices...>(op_result, is...)...);
167  }
168 
169 
170  template<bool uses_indices, std::size_t...indices, std::size_t...index_ixs,
171  typename Ix_tup, typename M, typename Op, typename...Args>
172  constexpr void
173  nullary_chipwise_op_chip(std::index_sequence<indices...>, std::index_sequence<index_ixs...>, const Ix_tup& ix_tup,
174  M& m, const Op& op)
175  {
176  [](M& m, const Op& op, auto...ix){
177  if constexpr (uses_indices)
178  set_chip<indices...>(m, op(ix...), ix...);
179  else
180  set_chip<indices...>(m, op(), ix...);
181  }(m, op, std::get<index_ixs>(ix_tup)...);
182  }
183 
184  template<bool uses_indices, bool first, typename All_index_seq, typename Ix_tup, typename M, typename Op>
185  constexpr void
186  nullary_chipwise_op(All_index_seq all_index_seq, const Ix_tup& ix_tup, M& m, const Op& op)
187  {
188  if constexpr (not first)
189  {
190  std::make_index_sequence<std::tuple_size_v<Ix_tup>> index_ix_seq;
191  nullary_chipwise_op_chip<uses_indices>(all_index_seq, index_ix_seq, ix_tup, m, op);
192  }
193  }
194 
195  template<bool uses_indices, bool first, std::size_t index, std::size_t...indices, typename All_index_seq,
196  typename Ix_tup, typename M, typename Op, typename I, typename...Is>
197  constexpr void
198  nullary_chipwise_op(All_index_seq all_index_seq, const Ix_tup& ix_tup, M& m, const Op& op, I i, Is...is)
199  {
200  if constexpr (first)
201  {
202  auto new_ix_tup = std::tuple_cat(ix_tup, std::tuple{std::integral_constant<std::size_t, 0> {}});
203  nullary_chipwise_op<uses_indices, true, indices...>(all_index_seq, new_ix_tup, m, op, is...);
204  }
205  constexpr std::size_t begin = first ? 1 : 0;
206 
207  for (std::size_t j = begin; j < static_cast<std::size_t>(i); ++j)
208  {
209  auto new_ix_tup = std::tuple_cat(ix_tup, std::tuple{j});
210  nullary_chipwise_op<uses_indices, false, indices...>(all_index_seq, new_ix_tup, m, op, is...);
211  }
212  }
213  } // namespace detail
214 
215 
225 #ifdef __cpp_concepts
226  template<std::size_t...indices, typename Operation, values::index...Is> requires (sizeof...(Is) == sizeof...(indices))
227 #else
228  template<std::size_t...indices, typename Operation, typename...Is, std::enable_if_t<
229  (values::index<Is> and ...) and (sizeof...(Is) == sizeof...(indices)), int> = 0>
230 #endif
231  constexpr auto
232  chipwise_operation(const Operation& operation, Is...is)
233  {
234  constexpr bool uses_indices = std::is_invocable_v<Operation, std::integral_constant<decltype(indices), 0>...>;
235 
236  auto op_result = [](const Operation& operation){
237  if constexpr (uses_indices) return operation(std::integral_constant<decltype(indices), 0> {}...);
238  else return operation();
239  }(operation);
240  using OpResult = decltype(op_result);
241 
242  static_assert((dimension_size_of_index_is<OpResult, indices, 1, Applicability::permitted> and ...),
243  "Operator must return a chip, meaning that the dimension is 1 for each of the specified indices.");
244  // Note: set_chip also includes a runtime check that operation() is a chip.
245 
246  constexpr std::size_t num_result_indices = std::max({index_count_v<OpResult>, (indices + 1)...});
247  auto m = detail::make_nullary_chipwise_default<indices...>(std::make_index_sequence<num_result_indices>{}, op_result, is...);
248  set_chip<indices...>(m, std::move(op_result), std::integral_constant<decltype(indices), 0> {}...);
249 
250  detail::nullary_chipwise_op<uses_indices, true, indices...>(std::index_sequence<indices...> {}, std::tuple{}, m, operation, is...);
251  return m;
252  }
253 
254 
255 } // namespace OpenKalman
256 
257 #endif //OPENKALMAN_CHIPWISE_OPERATION_HPP
constexpr bool indexible
T is a generalized tensor type.
Definition: indexible.hpp:32
constexpr auto chipwise_operation(const Operation &operation, Args &&...args)
Perform a chipwise n-ary operation (n>0) on one or more indexible objects.
Definition: chipwise_operation.hpp:127
The root namespace for OpenKalman.
Definition: basics.hpp:34
constexpr bool size
T is either an index representing a size, or void which represents that there is no size...
Definition: size.hpp:32
constexpr bool index
T is an index value.
Definition: index.hpp:56
constexpr Arg && set_chip(Arg &&arg, Chip &&chip, Ixs...ixs)
Set a sub-array having rank less than the rank of the input object.
Definition: set_chip.hpp:64
operation(const Operation &, const Args &...) -> operation< Operation, Args... >
Deduction guide.
constexpr bool index
An object describing a collection of /ref values::index objects.
Definition: index.hpp:75