OpenKalman
ScaledSigmaPointsBase.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 
17 #ifndef OPENKALMAN_SCALEDSIGMAPOINTSBASE_HPP
18 #define OPENKALMAN_SCALEDSIGMAPOINTSBASE_HPP
19 
20 namespace OpenKalman::internal
21 {
22 
36  template<typename Derived>
38  {
39 
40  private:
41 
44 
45 
46  /*
47  * \brief Weight for each sigma point other than the first one, when calculating posterior mean and covariance.
48  * \details See Julier Eq. 15 (not Eq. 24, which appears to be wrong), Eq. 27.
49  * \tparam dim Number of dimensions of the input variables (including noise).
50  * \tparam Scalar Scalar type (e.g., double).
51  */
52  template<std::size_t dim>
53  static constexpr auto W()
54  {
55  return Derived::template unscaled_W<dim>() / (Derived::alpha * Derived::alpha);
56  }
57 
58 
59  /*
60  * \brief Weight for the first sigma point when calculating the posterior mean.
61  * \details See Julier Eq. 15 (not Eq. 24, which appears to be wrong).
62  * \tparam dim Number of dimensions of the input variables (including noise).
63  * \tparam Scalar Scalar type (e.g., double).
64  */
65  template<std::size_t dim>
66  static constexpr auto W_m0()
67  {
68  return (Derived::template unscaled_W0<dim>() - 1) / (Derived::alpha * Derived::alpha) + 1;
69  }
70 
71 
72  /*
73  * \brief Weight for the first sigma point when calculating the posterior covariance.
74  * \details See Julier Eq. 27.
75  * \tparam dim Number of dimensions of the input variables (including noise).
76  * \tparam Scalar Scalar type (e.g., double).
77  * \return Weight for the first sigma point.
78  */
79  template<std::size_t dim>
80  static constexpr auto W_c0()
81  {
82  return W_m0<dim>() + 1 - Derived::alpha * Derived::alpha + Derived::beta;
83  }
84 
85 
86  template<std::size_t, typename Scalar>
87  static constexpr auto cat_dummy_function(const Scalar w) { return w; };
88 
89 
90  template<std::size_t dim, typename Weights, typename Scalar, std::size_t ... ints>
91  static auto cat_weights(const Scalar w0, std::index_sequence<ints...>)
92  {
93  return Weights {w0, cat_dummy_function<ints, Scalar>(W<dim>())...};
94  };
95 
96 
97  template<std::size_t dim, typename Weights>
98  static auto mean_weights()
99  {
100  using Scalar = scalar_type_of_t<Weights>;
101  constexpr auto count = index_dimension_of_v<Weights, 0>;
102  return cat_weights<dim, Weights, Scalar>(W_m0<dim>(), std::make_index_sequence<count - 1> {});
103  };
104 
105 
106  template<std::size_t dim, typename Weights>
107  static auto covariance_weights()
108  {
109  using Scalar = scalar_type_of_t<Weights>;
110  constexpr auto count = index_dimension_of_v<Weights, 0>;
111  return cat_weights<dim, Weights, Scalar>(W_c0<dim>(), std::make_index_sequence<count - 1> {});
112  };
113 
114  public:
115 
116 #ifdef __cpp_concepts
117  template<std::size_t dim, typed_matrix YMeans> requires has_untyped_index<YMeans, 1> and
118  (index_dimension_of_v<YMeans, 0> == coordinates::stat_dimension_of_v<vector_space_descriptor_of_t<YMeans, 0>>)
119 #else
120  template<std::size_t dim, typename YMeans, std::enable_if_t<typed_matrix<YMeans> and has_untyped_index<YMeans, 1> and
121  (index_dimension_of<YMeans, 0>::value == coordinates::stat_dimension_of_v<vector_space_descriptor_of_t<YMeans, 0>>), int> = 0>
122 #endif
123  static auto
124  weighted_means(YMeans&& y_means)
125  {
126  static_assert(index_dimension_of_v<YMeans, 1> == Derived::template sigma_point_count<dim>);
128  std::tuple<index_dimension_of<YMeans, 1>, Axis>>;
129  return make_self_contained(std::forward<YMeans>(y_means) * mean_weights<dim, Weights>());
130  }
131 
132 
143 #ifdef __cpp_concepts
144  template<std::size_t dim, typename InputDist, bool return_cross = false, typed_matrix X, typed_matrix Y> requires
145  (index_dimension_of_v<X, 1> == index_dimension_of_v<Y, 1>) and
146  compares_with<vector_space_descriptor_of_t<X, 0>, typename DistributionTraits<InputDist>::StaticDescriptor>
147 #else
148  template<std::size_t dim, typename InputDist, bool return_cross = false, typename X, typename Y, std::enable_if_t<
149  typed_matrix<X> and typed_matrix<Y> and (index_dimension_of<X, 1>::value == index_dimension_of<Y, 1>::value) and
150  compares_with<vector_space_descriptor_of_t<X, 0>, typename DistributionTraits<InputDist>::StaticDescriptor>,
151  int> = 0>
152 #endif
153  static auto
154  covariance(const X& x_deviations, const Y& y_deviations)
155  {
156  static_assert(index_dimension_of_v<X, 1> == Derived::template sigma_point_count<dim>);
157  constexpr auto count = index_dimension_of_v<X, 1>;
158  using Weights = dense_writable_matrix_t<X, Layout::none, scalar_type_of_t<X>, std::tuple<Dimensions<count>, Axis>>;
159  auto weights = covariance_weights<dim, Weights>();
160 
161  if constexpr(cholesky_form<InputDist>)
162  {
163  if constexpr (W_c0<dim>() < 0)
164  {
165  // Discard first weight and first y-deviation column for now, since square root of weight would be negative.
166  const auto [y_deviations_head, y_deviations_tail] = split_horizontal<1, count - 1>(y_deviations);
167  const auto [weights_head, weights_tail] = split_vertical<Axis, Dimensions<count - 1>>(weights);
168  const auto sqrt_weights_tail = apply_coefficientwise([](const auto x){ return values::sqrt(x); }, weights_tail);
169  auto out_covariance = make_self_contained(square(LQ_decomposition(y_deviations_tail * to_diagonal(sqrt_weights_tail))));
170  static_assert(OpenKalman::covariance<decltype(out_covariance)>);
171 
172  // Factor first weight back in, using a rank update:
173  rank_update(out_covariance, y_deviations_head, W_c0<dim>());
174 
175  if constexpr (return_cross)
176  {
177  auto cross_covariance = make_self_contained(x_deviations * to_diagonal(weights) * adjoint(y_deviations));
178  return std::tuple {std::move(out_covariance), std::move(cross_covariance)};
179  }
180  else
181  {
182  return out_covariance;
183  }
184  }
185  else
186  {
187  const auto sqrt_weights = apply_coefficientwise([](const auto x){ return values::sqrt(x); }, weights);
188  auto out_covariance = make_self_contained(square(LQ_decomposition(y_deviations * to_diagonal(sqrt_weights))));
189  static_assert(OpenKalman::covariance<decltype(out_covariance)>);
190 
191  if constexpr (return_cross)
192  {
193  auto cross_covariance = make_self_contained(x_deviations * to_diagonal(weights) * adjoint(y_deviations));
194  return std::tuple {std::move(out_covariance), std::move(cross_covariance)};
195  }
196  else
197  {
198  return out_covariance;
199  }
200  }
201  }
202  else
203  {
204  const auto w_yT = to_diagonal(weights) * adjoint(y_deviations);
205  auto out_covariance = make_self_contained(make_covariance(y_deviations * w_yT));
206 
207  if constexpr (return_cross)
208  {
209  auto cross_covariance = make_self_contained(x_deviations * w_yT);
210  return std::tuple {std::move(out_covariance), std::move(cross_covariance)};
211  }
212  else
213  {
214  return out_covariance;
215  }
216  }
217  }
218 
219  };
220 
221 }
222 
223 #endif //OPENKALMAN_SCALEDSIGMAPOINTSBASE_HPP
auto split_horizontal(M &&m)
Split Covariance or SquareRootCovariance vertically. Result is a tuple of typed matrices.
Definition: covariance-overloads.hpp:355
Definition: ScaledSigmaPointsBase.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 to_diagonal(Arg &&arg)
Convert an indexible object into a diagonal matrix.
Definition: to_diagonal.hpp:32
static auto covariance(const X &x_deviations, const Y &y_deviations)
Calculate the posterior covariance, given prior and posterior deviations from the sigma points...
Definition: ScaledSigmaPointsBase.hpp:154
constexpr auto sqrt(const Arg &arg)
A constexpr alternative to std::sqrt.
Definition: sqrt.hpp:46
decltype(auto) constexpr adjoint(Arg &&arg)
Take the adjoint of a matrix.
Definition: adjoint.hpp:33
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
constexpr bool covariance
T is a specialization of either Covariance or SquareRootCovariance.
Definition: object-types.hpp:161
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
std::decay_t< decltype(make_dense_object< T, layout, S >(std::declval< D >()))> dense_writable_matrix_t
An alias for a dense, writable matrix, patterned on parameter T.
Definition: dense_writable_matrix_t.hpp:38
auto make_covariance(Arg &&arg)
Make a Covariance from a covariance_nestable, specifying the fixed_pattern.
Definition: Covariance.hpp:735
The dimension of an index for a matrix, expression, or array.
Definition: index_dimension_of.hpp:34
auto split_vertical(M &&m)
Split Covariance or SquareRootCovariance vertically. Result is a tuple of typed matrices.
Definition: covariance-overloads.hpp:340
Definition: basics.hpp:48