OpenKalman
LinearTransformBase.hpp
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_LINEARTRANSFORMBASE_HPP
17 #define OPENKALMAN_LINEARTRANSFORMBASE_HPP
18 
19 #include <functional>
20 #include <tuple>
22 
23 namespace OpenKalman::internal
24 {
30  template<typename T, typename F>
31  struct needs_additive_correction : std::false_type {};
32 
33 
44  template<typename Derived>
46  {
47 
48  private:
49 
51 
52  template<std::size_t return_cross, typename J, typename D, std::size_t...ints>
53  static auto sum_noise_terms(const J& j, D&& d, std::index_sequence<ints...>)
54  {
55  using InputDist = std::tuple_element_t<0, D>;
56  if constexpr(cholesky_form<InputDist>)
57  {
58  if constexpr (return_cross)
59  {
60  auto sqrt_c0 =square_root(covariance_of(std::get<0>(std::forward<D>(d))));
61  auto term0 = std::get<0>(j) * sqrt_c0;
62  return std::tuple {
63  make_self_contained(LQ_decomposition(concatenate_horizontal(term0, Matrix(
64  (std::get<ints+1>(j) * (square_root(covariance_of(std::get<ints+1>(std::forward<D>(d)))))))...))),
65  make_self_contained(std::move(sqrt_c0) * adjoint(term0))};
66  }
67  else
68  {
69  return make_self_contained(LQ_decomposition(concatenate_horizontal(
70  std::get<0>(j) * square_root(covariance_of(std::get<0>(std::forward<D>(d)))),
71  Matrix((std::get<ints+1>(j) * (square_root(covariance_of(std::get<ints+1>(std::forward<D>(d)))))))...)));
72  }
73  }
74  else
75  {
76  if constexpr (return_cross)
77  {
78  auto cross = make_self_contained(covariance_of(std::get<0>(std::forward<D>(d))) * adjoint(std::get<0>(j)));
79  auto cov = make_covariance(make_self_contained((
80  (std::get<0>(j) * cross) +
81  ... + (std::get<ints+1>(j) * (covariance_of(std::get<ints+1>(std::forward<D>(d))) * adjoint(std::get<ints+1>(j)))))));
82  return std::tuple {std::move(cov), std::move(cross)};
83  }
84  else
85  {
86  return make_covariance(make_self_contained((
87  (std::get<0>(j) * covariance_of(std::get<0>(std::forward<D>(d))) * adjoint(std::get<0>(j))) + ... +
88  (std::get<ints+1>(j) * (covariance_of(std::get<ints+1>(std::forward<D>(d))) * adjoint(std::get<ints+1>(j)))))));
89  }
90  }
91  }
92 
93 
94  /*
95  * Linearly transform one statistical distribution to another.
96  * \tparam Trans The linear or linearized tests on which the transform is based
97  * (e.g., LinearTransformation).
98  * \tparam InputDist The prior distribution.
99  * \tparam NoiseDist Zero or more noise distributions.
100  **/
101 #ifdef __cpp_concepts
102  template<std::size_t return_cross, linearized_function<1> Trans,
103  gaussian_distribution InputDist, gaussian_distribution ... NoiseDists>
104 #else
105  template<std::size_t return_cross, typename Trans, typename InputDist, typename ... NoiseDists, std::enable_if_t<
106  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
107  linearized_function<Trans, 1>, int> = 0>
108 #endif
109  auto transform(const Trans& g, const InputDist& in, const NoiseDists& ...n) const
110  {
111  typename Derived::template TransformModel<Trans> transform_model {g};
112  auto [mean_output, jacobians] = transform_model(mean_of(in), mean_of(n)...);
113  using re = typename DistributionTraits<InputDist>::random_number_engine;
114 
115  if constexpr (return_cross)
116  {
117  auto [cov_out, cross_covariance] = sum_noise_terms<true>(jacobians, std::forward_as_tuple(in, n...),
118  std::make_index_sequence<std::min(sizeof...(NoiseDists), std::tuple_size_v<decltype(jacobians)> - 1)> {});
119 
120  auto out = make_GaussianDistribution<re>(std::move(mean_output), std::move(cov_out));
121 
123  return std::tuple {make_self_contained(out + transform_model.add_correction(in, n...)), cross_covariance};
124  else
125  return std::tuple {out, cross_covariance};
126  }
127  else
128  {
129  auto cov_out = sum_noise_terms<false>(jacobians, std::forward_as_tuple(in, n...),
130  std::make_index_sequence<std::min(sizeof...(NoiseDists), std::tuple_size_v<decltype(jacobians)> - 1)> {});
131 
132  auto out = make_GaussianDistribution<re>(std::move(mean_output), std::move(cov_out));
133 
135  {
136  return make_self_contained(out + transform_model.add_correction(in, n...));
137  }
138  else
139  {
140  return out;
141  }
142  }
143  }
144 
145  public:
146 
154 #ifdef __cpp_concepts
155  template<gaussian_distribution InputDist, tuple_like...Ts>
156 #else
157  template<typename InputDist, typename...Ts, std::enable_if_t<
158  gaussian_distribution<InputDist> and (tuple_like<Ts> and ...), int> = 0>
159 #endif
160  auto operator()(const InputDist& x, const Ts&...ts) const
161  {
162  return Base::operator()(x, ts...);
163  }
164 
165 
174 #ifdef __cpp_concepts
175  template<gaussian_distribution InputDist, linearized_function<1> Trans, gaussian_distribution ... NoiseDists>
176  requires requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
177 #else
178  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
179  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
180  linearized_function<Trans, 1> and std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
181  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
182 #endif
183  auto operator()(const InputDist& x, const Trans& g, const NoiseDists&...ns) const
184  {
185  return transform<false>(g, x, ns...);
186  }
187 
188 
196 #ifdef __cpp_concepts
197  template<gaussian_distribution InputDist, tuple_like...Ts>
198 #else
199  template<typename InputDist, typename...Ts, std::enable_if_t<
200  gaussian_distribution<InputDist> and (tuple_like<Ts> and ...), int> = 0>
201 #endif
202  auto transform_with_cross_covariance(const InputDist& x, const Ts&...ts) const
203  {
204  return Base::transform_with_cross_covariance(x, ts...);
205  }
206 
207 
216 #ifdef __cpp_concepts
217  template<gaussian_distribution InputDist, linearized_function<1> Trans, gaussian_distribution ... NoiseDists>
218  requires requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
219 #else
220  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
221  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
222  linearized_function<Trans, 1> and std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
223  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
224 #endif
225  auto transform_with_cross_covariance(const InputDist& x, const Trans& g, const NoiseDists&...ns) const
226  {
227  return transform<true>(g, x, ns...);
228  }
229 
230  };
231 
232 
233 }
234 
235 #endif //OPENKALMAN_LINEARTRANSFORMBASE_HPP
Definition for collections::tuple_like.
Definition: TransformBase.hpp:30
decltype(auto) constexpr concatenate_horizontal(V &&v, Vs &&... vs)
Concatenate one or more matrix objects vertically.
Definition: typed-matrix-overloads.hpp:308
Matrix(M &&) -> Matrix< Dimensions< index_dimension_of_v< M, 0 >>, Dimensions< index_dimension_of_v< M, 1 >>, passable_t< M >>
Deduce parameter types from a typed_matrix_nestable.
decltype(auto) constexpr adjoint(Arg &&arg)
Take the adjoint of a matrix.
Definition: adjoint.hpp:33
auto operator()(const InputDist &x, const Ts &...ts) const
Perform one or more consecutive linear(ized) transforms.
Definition: LinearTransformBase.hpp:160
decltype(auto) constexpr LQ_decomposition(A &&a)
Perform an LQ decomposition of matrix A=[L,0]Q, L is a lower-triangular matrix, and Q is orthogonal...
Definition: LQ_decomposition.hpp:33
auto transform_with_cross_covariance(const InputDist &x, const Ts &...ts) const
Perform one or more consecutive linear(ized) transforms, also returning the cross-covariance.
Definition: LinearTransformBase.hpp:202
auto make_covariance(Arg &&arg)
Make a Covariance from a covariance_nestable, specifying the fixed_pattern.
Definition: Covariance.hpp:735
constexpr bool gaussian_distribution
T is a Gaussian distribution.
Definition: object-types.hpp:182
auto transform_with_cross_covariance(const InputDist &x, const Trans &g, const NoiseDists &...ns) const
Perform a linear(ized) transform, also returning the cross-covariance.
Definition: LinearTransformBase.hpp:225
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: LinearTransformBase.hpp:45
Definition: basics.hpp:48
Definition: LinearTransformBase.hpp:31
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