OpenKalman
concatenate.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_CONCATENATE_HPP
17 #define OPENKALMAN_CONCATENATE_HPP
18 
19 namespace OpenKalman
20 {
21 
22  namespace detail
23  {
24  template<typename T, typename U, std::size_t...indices, std::size_t...I>
25  constexpr bool concatenate_dimensions_match_impl(std::index_sequence<I...>)
26  {
27  return (([](std::size_t i){ return ((i != indices) and ...); }(I) or
28  dimension_size_of_index_is<T, I, index_dimension_of_v<U, I>, Applicability::permitted>) and ...);
29  }
30 
31 
32  template<typename T, typename U, std::size_t...indices>
33 #ifdef __cpp_concepts
34  concept concatenate_dimensions_match =
35 #else
36  constexpr bool concatenate_dimensions_match =
37 #endif
38  (concatenate_dimensions_match_impl<T, U, indices...>(std::make_index_sequence<index_count_v<T>> {}));
39 
40 
41  template<std::size_t I, std::size_t...indices, typename DTup, typename...DTups>
42  constexpr decltype(auto) concatenate_vector_space_descriptor_impl(DTup&& d_tup, DTups&&...d_tups)
43  {
44  if constexpr (((I == indices) or ...))
45  {
46  auto f = [](auto&& dtup){
47  if constexpr (I >= std::tuple_size_v<std::decay_t<decltype(dtup)>>) return coordinates::Axis {};
48  else return std::get<I>(std::forward<decltype(dtup)>(dtup));
49  };
50  return (f(std::forward<DTup>(d_tup)) + ... + f(std::forward<DTups>(d_tups)));
51  }
52  else
53  {
54  if constexpr (not (compares_with<std::tuple_element_t<I, DTup>, std::tuple_element_t<I, DTups>>and ...))
55  {
56  if (((std::get<I>(std::forward<DTup>(d_tup)) != std::get<I>(std::forward<DTups>(d_tups))) or ...))
57  throw std::invalid_argument {"Arguments to concatenate do not match in at least index " + std::to_string(I)};
58  }
59  return std::get<I>(std::forward<DTup>(d_tup));
60  }
61  }
62 
63 
64  template<std::size_t...indices, std::size_t...I, typename...DTups>
65  constexpr decltype(auto) concatenate_vector_space_descriptor(std::index_sequence<I...>, DTups&&...d_tups)
66  {
67  return std::tuple {concatenate_vector_space_descriptor_impl<I, indices...>(std::forward<DTups>(d_tups)...)...};
68  }
69 
70 
71 #ifdef __cpp_concepts
72  template<typename T, typename...Ts>
73  concept constant_concatenate_arguments =
74  (values::fixed<constant_coefficient<T>> and ... and values::fixed<constant_coefficient<Ts>>) and
75  (values::internal::near(constant_coefficient_v<T>, constant_coefficient_v<Ts>) and ...);
76 #else
77  template<typename T, typename = void, typename...Ts>
78  struct constant_concatenate_arguments_impl : std::false_type {};
79 
80  template<typename T, typename...Ts>
82  std::enable_if_t<(values::internal::near(constant_coefficient<T>::value, constant_coefficient<Ts>::value) and ...)>, Ts...>
83  : std::true_type {};
84 
85  template<typename T, typename...Ts>
86  constexpr bool constant_concatenate_arguments = constant_concatenate_arguments_impl<T, void, Ts...>::value;
87 #endif
88 
89 
90  template<std::size_t index, std::size_t...indices, typename Args_tup, std::size_t...all_indices, std::size_t...pos>
91  constexpr auto concatenate_diag_impl(Args_tup&& args_tup, std::index_sequence<all_indices...>, std::index_sequence<pos...>)
92  {
93  constexpr auto p_index = std::get<index>(std::tuple{pos...});
94  if constexpr (((p_index == std::get<indices>(std::tuple{pos...})) and ...))
95  {
96  return std::get<p_index>(args_tup);
97  }
98  else
99  {
100  using Pattern = std::tuple_element_t<0, Args_tup>;
101  return make_zero<Pattern>(get_vector_space_descriptor<all_indices>(std::get<pos>(args_tup))...);
102  }
103  }
104 
105 
106  template<std::size_t...indices, typename Ds_tup, typename Args_tup, std::size_t...all_indices, typename...Pos_seq>
107  constexpr auto concatenate_diag(Ds_tup&& ds_tup, Args_tup&& args_tup, std::index_sequence<all_indices...> all_indices_seq, Pos_seq...pos_seq)
108  {
109  return tile(ds_tup, concatenate_diag_impl<indices...>(std::forward<Args_tup>(args_tup), all_indices_seq, pos_seq)...);
110  }
111 
112 
113  // all indices are processed. return the resulting indices of a particular block
114  template<std::size_t...args_ix, std::size_t...pos>
115  constexpr auto get_cat_indices(
116  std::index_sequence<>,
117  std::index_sequence<>,
118  std::index_sequence<args_ix...>,
119  std::index_sequence<pos...> pos_seq)
120  {
121  return std::tuple {pos_seq};
122  }
123 
124 
125  // all specified indices are examined, but there are still other ix indices to process
126  template<std::size_t ix, std::size_t...ixs, std::size_t...args_ix, std::size_t...pos>
127  constexpr auto get_cat_indices(
128  std::index_sequence<ix, ixs...>,
129  std::index_sequence<> index_seq,
130  std::index_sequence<args_ix...> arg_ix_seq,
131  std::index_sequence<pos...>)
132  {
133  return get_cat_indices(
134  std::index_sequence<ixs...> {},
135  index_seq,
136  arg_ix_seq,
137  std::index_sequence<pos..., 0> {}
138  );
139  }
140 
141 
142  template<std::size_t ix, std::size_t...ixs, std::size_t index, std::size_t...indices, std::size_t...args_ix, std::size_t...pos>
143  constexpr auto get_cat_indices(
144  std::index_sequence<ix, ixs...> ix_seq,
145  std::index_sequence<index, indices...> index_seq,
146  std::index_sequence<args_ix...> arg_ix_seq,
147  std::index_sequence<pos...> pos_seq)
148  {
149  if constexpr (ix == index) // Increase the rank of the resulting matrix.
150  {
151  static_assert (((index != indices) and ...), "No duplicate indices for concatenate function.");
152  return std::tuple_cat(get_cat_indices(
153  std::index_sequence<ixs...> {},
154  std::index_sequence<indices...> {},
155  arg_ix_seq,
156  std::index_sequence<pos..., args_ix> {})...);
157  }
158  else if constexpr (((ix == indices) or ...)) // index, indices... are not in ascending order -- rotate and try again.
159  {
160  return get_cat_indices(
161  ix_seq,
162  std::index_sequence<indices..., index> {},
163  arg_ix_seq,
164  pos_seq);
165  }
166  else // index, indices... do not include ix.
167  {
168  return get_cat_indices(
169  std::index_sequence<ixs...> {},
170  index_seq,
171  arg_ix_seq,
172  std::index_sequence<pos..., 0> {});
173  }
174  }
175 
176  } // namespace detail
177 
178 
190 #ifdef __cpp_concepts
191  template<std::size_t...indices, indexible Arg, detail::concatenate_dimensions_match<Arg>...Args>
192  requires (sizeof...(indices) > 0)
193 #else
194  template<std::size_t...indices, typename Arg, typename...Args, std::enable_if_t<(sizeof...(indices) > 0) and
195  (indexible<Arg> and ... and detail::concatenate_dimensions_match<Arg, Args>), int> = 0>
196 #endif
197  constexpr decltype(auto)
198  concatenate(Arg&& arg, Args&&...args)
199  {
200  auto seq = std::make_index_sequence<std::max({index_count_v<Arg>, index_count_v<Args>..., indices...})> {};
201  auto d_tup = detail::concatenate_vector_space_descriptor<indices...>(
203 
204  if constexpr (sizeof...(Args) == 0)
205  {
206  return std::forward<Arg>(arg);
207  }
208  else if constexpr ((zero<Arg> and ... and zero<Args>))
209  {
210  return std::apply([](auto&&...ds){ return make_zero<Arg>(std::forward<decltype(ds)>(ds)...); }, d_tup);
211  }
212  else if constexpr (sizeof...(indices) == 1 and detail::constant_concatenate_arguments<Arg, Args...>)
213  {
214  return std::apply([](auto&&...ds){
215  return make_constant<Arg>(constant_coefficient<Arg>{}, std::forward<decltype(ds)>(ds)...);
216  }, d_tup);
217  }
218  else if constexpr (sizeof...(indices) == 2 and ((indices == 0) or ...) and ((indices == 1) or ...) and
219  (diagonal_matrix<Args> and ...))
220  {
221  return to_diagonal(concatenate<0>(diagonal_of(std::forward<Arg>(arg), std::forward<Args>(args)...)));
222  }
223  else if constexpr (sizeof...(indices) == 2 and ((indices == 0) or ...) and ((indices == 1) or ...) and
224  (triangle_type_of_v<Arg, Args...> != TriangleType::any) and (square_shaped<Arg> and ... and square_shaped<Args>))
225  {
226  return make_triangular_matrix<triangle_type_of_v<Arg>>(
227  concatenate<0, 1>(nested_object(std::forward<Arg>(arg)), nested_object(std::forward<Args>(args))...));
228  }
229  else if constexpr (sizeof...(indices) == 2 and ((indices == 0) or ...) and ((indices == 1) or ...) and
230  (hermitian_matrix<Arg> and ... and hermitian_matrix<Args>))
231  {
232  constexpr auto t = hermitian_adapter_type_of_v<Arg>;
233  auto maybe_transpose = [](auto&& m) {
234  using M = decltype(m);
235  if constexpr(t == hermitian_adapter_type_of_v<M>) return nested_object(std::forward<M>(m));
236  else return transpose(nested_object(std::forward<M>(m)));
237  };
238  return make_hermitian_matrix<t>(
239  concatenate_diagonal(nested_object(std::forward<Arg>(arg)), maybe_transpose(std::forward<Args>(args))...));
240  }
241  else if constexpr (sizeof...(indices) == 1)
242  {
243  return tile(d_tup, std::forward<Arg>(arg), std::forward<Args>(args)...);
244  }
245  else
246  {
247  auto pos_tup = detail::get_cat_indices(seq, std::index_sequence<indices...> {},
248  std::make_index_sequence<1 + sizeof...(Args)> {}, std::index_sequence<> {});
249 
250  return std::apply([&](auto...pos_seq){
251  return detail::concatenate_diag<indices...>(d_tup,
252  std::forward_as_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...), seq, pos_seq...);
253  }, pos_tup);
254  }
255  }
256 
257 
258 } // namespace OpenKalman
259 
260 #endif //OPENKALMAN_CONCATENATE_HPP
constexpr bool dimension_size_of_index_is
Specifies that a given index of T has a specified size.
Definition: dimension_size_of_index_is.hpp:44
decltype(auto) constexpr tile(const std::tuple< Ds... > &ds_tuple, Block &&block, Blocks &&...blocks)
Create a matrix or tensor by tiling individual blocks.
Definition: tile.hpp:111
Lower, upper, or diagonal matrix.
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
decltype(auto) constexpr to_diagonal(Arg &&arg)
Convert an indexible object into a diagonal matrix.
Definition: to_diagonal.hpp:32
The constant associated with T, assuming T is a constant_matrix.
Definition: constant_coefficient.hpp:36
decltype(auto) constexpr transpose(Arg &&arg)
Take the transpose of a matrix.
Definition: transpose.hpp:58
decltype(auto) constexpr all_vector_space_descriptors(const T &t)
Return a collection of coordinates::pattern objects associated with T.
Definition: all_vector_space_descriptors.hpp:52
The root namespace for OpenKalman.
Definition: basics.hpp:34
The concept, trait, or restraint is permitted, but whether it applies is not necessarily known at com...
decltype(auto) constexpr diagonal_of(Arg &&arg)
Extract a column vector (or column slice for rank>2 tensors) comprising the diagonal elements...
Definition: diagonal_of.hpp:33
constexpr bool near(const Arg1 &arg1, const Arg2 &arg2)
Determine whether two numbers are within a rounding tolerance.
Definition: near.hpp:36
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:34
decltype(auto) constexpr concatenate_diagonal(V &&v, Vs &&... vs)
Concatenate one or more typed matrices diagonally.
Definition: typed-matrix-overloads.hpp:340
constexpr bool index
An object describing a collection of /ref values::index objects.
Definition: index.hpp:75