OpenKalman
SamplePointsTransform.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) 2017-2021 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_SAMPLEPOINTSTRANSFORM_HPP
17 #define OPENKALMAN_SAMPLEPOINTSTRANSFORM_HPP
18 
19 #include <functional>
20 #include <tuple>
22 
23 namespace OpenKalman
24 {
25  namespace oin = OpenKalman::internal;
26 
36  template<typename SamplePointsType>
38 
39 
41 
44 
45 
46  template<typename SamplePointsType>
47  struct SamplePointsTransform : oin::TransformBase<SamplePointsTransform<SamplePointsType>>
48  {
49 
50  private:
51 
52  // Count the number of augmented input dimensions.
53  template<typename T, std::size_t...ints>
54  static constexpr auto count_dim_impl(std::index_sequence<ints...>)
55  {
56  return (0 + ... + index_dimension_of_v<std::tuple_element_t<1 + ints, T>, 0>);
57  }
58 
59 
60  // Count the number of augmented input dimensions.
61  template<typename In, typename...Ts>
62  static constexpr auto count_dim()
63  {
64  return (index_dimension_of_v<In, 0> + ... +
65  count_dim_impl<Ts>(std::make_index_sequence<std::tuple_size_v<Ts> - 1> {}));
66  }
67 
68 
69  // Reconstruct the flattened augmented sample points into a tuple of tuples.
70  template<std::size_t pos = 0, typename T, typename...Ts, typename FlattenedPs>
71  static auto construct_ps(FlattenedPs&& flattened_ps)
72  {
73  constexpr auto group_size = std::tuple_size_v<std::decay_t<T>> - 1; // The size of the noise terms only
74  return std::tuple_cat(
75  oin::tuple_slice<pos, pos + group_size>(std::forward<FlattenedPs>(flattened_ps)),
76  construct_ps<pos + group_size, Ts...>(std::forward<FlattenedPs>(flattened_ps)));
77  }
78 
79 
80  // Reconstruct the flattened augmented sample points into a tuple of tuples.
81  template<std::size_t pos = 0, typename FlattenedPs>
82  static auto construct_ps(FlattenedPs&& flattened_ps)
83  {
84  static_assert(pos == std::tuple_size_v<FlattenedPs>);
85  return std::tuple {};
86  }
87 
88 
89  template<typename G, typename Dists, typename Points, std::size_t...ints>
90  static constexpr auto y_means_impl(const G& g, const Dists& dists, const Points& points, std::index_sequence<ints...>)
91  {
92  constexpr auto count = index_dimension_of_v<decltype(std::get<0>(points)), 1>;
93  return apply_columnwise<count>([&](std::size_t i) {
94  return g((column(std::get<ints>(points), i) + mean_of(std::get<ints>(dists)))...);
95  });
96  }
97 
98 
115 #ifdef __cpp_concepts
116  template<std::size_t dim, typename InputDist, bool return_cross, std::size_t i = 0,
117  typename Gs, typename D, typename Ds, typename P, typename Ps> requires
118  (std::tuple_size_v<std::decay_t<Gs>> == std::tuple_size_v<std::decay_t<Ds>>) and
119  (std::tuple_size_v<std::decay_t<Gs>> == std::tuple_size_v<std::decay_t<Ps>>)
120 #else
121  template<std::size_t dim, typename InputDist, bool return_cross, std::size_t i = 0,
122  typename Gs, typename D, typename Ds, typename P, typename Ps, std::enable_if_t<
123  (std::tuple_size_v<std::decay_t<Gs>> == std::tuple_size_v<std::decay_t<Ds>>) and
124  (std::tuple_size_v<std::decay_t<Gs>> == std::tuple_size_v<std::decay_t<Ps>>), int> = 0>
125 #endif
126  static auto transform_impl(Gs&& gs, D&& d, Ds&& ds, P&& p, Ps&& ps)
127  {
128  auto g = std::get<i>(std::forward<Gs>(gs));
129 
130  auto xdists_tup = std::tuple_cat(std::forward_as_tuple(std::forward<D>(d)), std::get<i>(std::forward<Ds>(ds)));
131 
132  auto xpoints_tup = std::tuple_cat(std::forward_as_tuple(p), std::get<i>(std::forward<Ps>(ps)));
133 
134  constexpr std::size_t N = std::tuple_size_v<decltype(xpoints_tup)>;
135  static_assert(N == std::tuple_size_v<decltype(xdists_tup)>);
136 
137  auto y_means = Mean { y_means_impl(g, std::move(xdists_tup), std::move(xpoints_tup), std::make_index_sequence<N> {})};
138 
139  const auto y_mean = from_euclidean(SamplePointsType::template weighted_means<dim>(to_euclidean(y_means)));
140 
141  // Each column is a deviation from y mean for each transformed sigma point:
142  auto ypoints = apply_columnwise(
143  [&](const auto& col) { return make_self_contained(col - y_mean);}, std::move(y_means));
144 
145  if constexpr (i + 1 < std::tuple_size_v<std::decay_t<Gs>>)
146  {
147  auto y_covariance = SamplePointsType::template covariance<dim, InputDist, false>(std::forward<P>(p), ypoints);
148  auto y = GaussianDistribution {make_self_contained(std::move(y_mean)), std::move(y_covariance)};
149  return transform_impl<dim, InputDist, return_cross, i + 1>(
150  std::forward<Gs>(gs), std::move(y), std::forward<Ds>(ds), std::move(ypoints), std::forward<Ps>(ps));
151  }
152  else // processing for the last transformation in Gs:
153  {
154  auto y_covariance = SamplePointsType::template covariance<dim, InputDist, return_cross>(
155  std::forward<P>(p), std::move(ypoints));
156 
157  if constexpr (return_cross)
158  {
159  auto [y_cov, cross] = std::move(y_covariance);
160  return std::tuple {GaussianDistribution {make_self_contained(std::move(y_mean)), std::move(y_cov)},
161  std::move(cross)};
162  }
163  else
164  {
165  return GaussianDistribution {make_self_contained(std::move(y_mean)), std::move(y_covariance)};
166  }
167  }
168  }
169 
170 
181  template<bool return_cross, typename InputDist, typename...Ts>
182  auto transform(InputDist&& x, Ts&&...ts) const
183  {
184  static_assert(sizeof...(Ts) > 0);
185 
186  // Extract a flattened 1D tuple of noise terms.
187  auto flattened_ds = std::tuple_cat(oin::tuple_slice<1, std::tuple_size_v<std::decay_t<Ts>>>(ts)...);
188 
189  // Create a tuple comprising the augmented sample points (based on input x and each noise term in flattened ds):
190  auto points_tuple = std::apply([](const auto&...args) {
191  return SamplePointsType::sample_points(args...);
192  }, std::tuple_cat(std::forward_as_tuple(x), std::move(flattened_ds)));
193 
194  // The augmented sample points corresponding to input x.
195  auto p = std::get<0>(std::move(points_tuple));
196 
197  // The augmented sample points corresponding to each noise term in flattened_ds.
198  auto flattened_ps = oin::tuple_slice<1, std::tuple_size_v<decltype(points_tuple)>>(std::move(points_tuple));
199 
200  // The augmented sample points, reconstructed into the same tuple-of-tuples structure as ds.
201  auto ps = construct_ps<0, Ts...>(std::move(flattened_ps));
202 
203  // Extract the transformations.
204  auto gs = std::forward_as_tuple(std::get<0>(std::forward<Ts>(ts))...);
205 
206  // Extract the noise terms.
207  auto ds = std::tuple {oin::tuple_slice<1, std::tuple_size_v<std::decay_t<Ts>>>(std::forward<Ts>(ts))...};
208 
209  // Number of augmented input dimensions.
210  constexpr auto dim = count_dim<InputDist, Ts...>();
211 
212  return SamplePointsTransform::transform_impl<dim, InputDist, return_cross>(
213  std::move(gs), std::forward<InputDist>(x), std::move(ds), std::move(p), std::move(ps));
214  }
215 
216  public:
217 
225 #ifdef __cpp_concepts
226  template<gaussian_distribution InputDist, typename...Ts> requires (... and collections::tuple_like<std::decay_t<Ts>>)
227 #else
228  template<typename InputDist, typename...Ts, std::enable_if_t<
229  gaussian_distribution<InputDist> and (collections::tuple_like<std::decay_t<Ts>> and ...), int> = 0>
230 #endif
231  auto operator()(InputDist&& x, Ts&&...ts) const
232  {
233  return transform<false>(std::forward<InputDist>(x), std::forward<Ts>(ts)...);
234  }
235 
236 
244 #ifdef __cpp_concepts
245  template<gaussian_distribution InputDist, typename Trans, gaussian_distribution ... NoiseDists> requires
246  requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
247 #else
248  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
249  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
250  std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
251  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
252 #endif
253  auto operator()(InputDist&& x, Trans&& g, NoiseDists&&...n) const
254  {
255  return transform<false>(
256  std::forward<InputDist>(x), std::forward_as_tuple(std::forward<Trans>(g), std::forward<NoiseDists>(n)...));
257  }
258 
259 
267 #ifdef __cpp_concepts
268  template<gaussian_distribution InputDist, collections::tuple_like...Ts> requires (... and collections::tuple_like<std::decay_t<Ts>>)
269 #else
270  template<typename InputDist, typename...Ts, std::enable_if_t<
271  gaussian_distribution<InputDist> and (collections::tuple_like<std::decay_t<Ts>> and ...), int> = 0>
272 #endif
273  auto transform_with_cross_covariance(InputDist&& x, Ts&&...ts) const
274  {
275  return transform<true>(std::forward<InputDist>(x), std::forward<Ts>(ts)...);
276  }
277 
278 
286 #ifdef __cpp_concepts
287  template<gaussian_distribution InputDist, typename Trans, gaussian_distribution ... NoiseDists> requires
288  requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
289 #else
290  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
291  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
292  std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
293  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
294 #endif
295  auto transform_with_cross_covariance(InputDist&& x, Trans&& g, NoiseDists&&...n) const
296  {
297  return transform<true>(
298  std::forward<InputDist>(x), std::forward_as_tuple(std::forward<Trans>(g), std::forward<NoiseDists>(n)...));
299  }
300 
301  };
302 
303 }
304 
305 
306 #endif //OPENKALMAN_SAMPLEPOINTSTRANSFORM_HPP
Definition for collections::tuple_like.
decltype(auto) constexpr from_euclidean(Arg &&arg, const V &v)
Project the Euclidean vector space associated with index 0 to coordinates::pattern v after applying d...
Definition: from_euclidean.hpp:35
A set of one or more column vectors, each representing a statistical mean.
Definition: forward-class-declarations.hpp:477
auto operator()(InputDist &&x, Ts &&...ts) const
Perform one or more consecutive sample points transforms.
Definition: SamplePointsTransform.hpp:231
auto transform_with_cross_covariance(InputDist &&x, Trans &&g, NoiseDists &&...n) const
Perform one sample points transform, also returning the cross-covariance.
Definition: SamplePointsTransform.hpp:295
Definition: TransformBase.hpp:30
Definition: tuple_reverse.hpp:103
A Gaussian distribution, defined in terms of a Mean and a Covariance.
Definition: GaussianDistribution.hpp:42
The root namespace for OpenKalman.
Definition: basics.hpp:34
Scaled points transform.
Definition: SamplePointsTransform.hpp:37
decltype(auto) constexpr to_euclidean(Arg &&arg)
Project the vector space associated with index 0 to a Euclidean space for applying directional statis...
Definition: to_euclidean.hpp:38
auto transform_with_cross_covariance(InputDist &&x, Ts &&...ts) const
Perform one or more consecutive sample points transforms, also returning the cross-covariance.
Definition: SamplePointsTransform.hpp:273
constexpr bool gaussian_distribution
T is a Gaussian distribution.
Definition: object-types.hpp:182
constexpr auto tuple_slice(Arg &&arg)
Takes a slice of a tuple, given an index range.
Definition: tuple_slice.hpp:132
Mean(V &&) -> Mean< Dimensions< index_dimension_of_v< V, 0 >>, passable_t< V >>
Deduce template parameters from a typed_matrix_nestable, assuming untyped coordinates::pattern.
Definition: basics.hpp:48
constexpr bool tuple_like
T is a non-empty tuple, pair, array, or other type that acts like a tuple.
Definition: tuple_like.hpp:51