OpenKalman
MonteCarloTransform.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_MONTECARLOTRANSFORM_HPP
17 #define OPENKALMAN_MONTECARLOTRANSFORM_HPP
18 
19 #include <cmath>
20 #include <execution>
21 #include <iterator>
22 #include <numeric>
23 
24 namespace OpenKalman
25 {
26  namespace oin = OpenKalman::internal;
27 
35  struct MonteCarloTransform : oin::TransformBase<MonteCarloTransform>
36  {
37  private:
38 
40 
41 
42  template<bool return_cross, typename TransformationType, typename InputDistribution, typename...NoiseDistributions>
43  struct MonteCarloSet
44  {
45  private:
46 
47  using In_Mean = typename DistributionTraits<InputDistribution>::Mean;
48  using Out_Mean = std::invoke_result_t<TransformationType, In_Mean>;
49  using InputCoefficients = vector_space_descriptor_of_t<In_Mean, 0>;
50  using OutputCoefficients = vector_space_descriptor_of_t<Out_Mean, 0>;
51  using Scalar = scalar_type_of_t<In_Mean>;
52  static_assert((coordinates::compares_with<OutputCoefficients,
53  typename DistributionTraits<NoiseDistributions>::StaticDescriptor> and ...));
54 
55  using InputMeanMatrix = dense_writable_matrix_t<In_Mean, Layout::none, Scalar,
56  std::tuple<coordinates::dimension_of<InputCoefficients>, Axis>>;
58  using OutputCovarianceMatrix = dense_writable_matrix_t<InputMeanMatrix, Layout::none, Scalar,
59  std::tuple<coordinates::dimension_of<OutputCoefficients>, coordinates::dimension_of<OutputCoefficients>>>;
60  using OutputCovarianceSA = typename MatrixTraits<std::decay_t<OutputCovarianceMatrix>>::template SelfAdjointMatrixFrom<>;
61  using CrossCovarianceMatrix = dense_writable_matrix_t<InputMeanMatrix, Layout::none, Scalar,
62  std::tuple<coordinates::dimension_of<InputCoefficients>, coordinates::dimension_of<OutputCoefficients>>>;
63 
68 
69 
70  struct MonteCarloSum0
71  {
72  std::size_t count;
73  InputMean x;
74  OutputEuclideanMean y_E;
75  OutputCovariance yy;
76  };
77 
78 
79  struct MonteCarloSum1
80  {
81  std::size_t count;
82  InputMean x;
83  OutputEuclideanMean y_E;
84  OutputCovariance yy;
85  CrossCovariance xy;
86  };
87 
88  public:
89 
90  using MonteCarloSum = std::conditional_t<return_cross, MonteCarloSum1, MonteCarloSum0>;
91 
92 
93  static constexpr auto
94  zero()
95  {
96  if constexpr (return_cross)
97  return MonteCarloSum {
98  0, make_zero<InputMean>(), make_zero<OutputEuclideanMean>(),
99  make_zero<OutputCovariance>(), make_zero<CrossCovariance>()};
100  else
101  return MonteCarloSum {
102  0, make_zero<InputMean>(), make_zero<OutputEuclideanMean>(),
103  make_zero<OutputCovariance>()};
104  }
105 
106 
107  static auto
108  one(const TransformationType& trans, const InputDistribution& dist, const NoiseDistributions&...noise)
109  {
110  const auto x = dist();
111  const auto y = trans(x, noise()...);
112  if constexpr (return_cross)
113  return MonteCarloSum {1, x, to_euclidean(y), make_zero<OutputCovariance>(),
114  make_zero<CrossCovariance>()};
115  else
116  return MonteCarloSum {1, x, to_euclidean(y), make_zero<OutputCovariance>()};
117  }
118 
119 
121  {
122  auto operator()(const MonteCarloSum& set1, const MonteCarloSum& set2)
123  {
124  using Scalar = scalar_type_of_t<decltype(set1.y_E)>;
125  if (set2.count == 1) // This is most likely. Take advantage of prior knowledge of set2.
126  {
127  const auto count = set1.count + 1;
128  const Scalar s_count = count;
129  const auto x = (set1.count * set1.x + set2.x) / s_count;
130  const auto y_E = (set1.count * set1.y_E + set2.y_E) / s_count;
131  const auto delta = from_euclidean(set2.y_E) - from_euclidean(set1.y_E);
132  const auto delta_adj_factor = adjoint(delta) * set1.count / s_count;
133  const OutputCovariance yy {set1.yy + delta * delta_adj_factor};
134  if constexpr (return_cross)
135  {
136  const auto xy = set1.xy + (set2.x - set1.x) * delta_adj_factor;
137  return MonteCarloSum {count, x, y_E, yy, xy};
138  }
139  else
140  {
141  return MonteCarloSum {count, x, y_E, yy};
142  }
143  }
144  else
145  {
146  const auto count = set1.count + set2.count;
147  const Scalar s_count = count;
148  const auto x = (set1.count * set1.x + set2.count * set2.x) / s_count;
149  const auto y_E = (set1.count * set1.y_E + set2.count * set2.y_E) / s_count;
150  const auto delta = from_euclidean(set2.y_E) - from_euclidean(set1.y_E);
151  const auto delta_adj_factor = adjoint(delta) * set1.count * set2.count / s_count;
152  const OutputCovariance yy {set1.yy + set2.yy + delta * delta_adj_factor};
153  if constexpr (return_cross)
154  {
155  const auto xy = set1.xy + set2.xy + (set2.x - set1.x) * delta_adj_factor;
156  return MonteCarloSum {count, x, y_E, yy, xy};
157  }
158  else
159  {
160  return MonteCarloSum {count, x, y_E, yy};
161  }
162  }
163  }
164  };
165 
166 
167  struct iterator
168  {
169  iterator(const std::function<MonteCarloSum()>& g, std::size_t initial_position = 0)
170  : one_sum_generator(g), position(initial_position) {}
171 
172  using iterator_category = std::forward_iterator_tag;
173  using value_type = MonteCarloSum;
174  using reference = MonteCarloSum&;
175  using pointer = MonteCarloSum*;
176  using difference_type = std::ptrdiff_t;
177 
178  auto& operator=(const iterator& other) { if (this != &other) position = other.position; }
179 
180  auto operator*() const { return one_sum_generator(); }
181 
182  auto& operator++() { ++position; return *this; }
183  auto operator++(int) { const auto temp = *this; ++position; return temp; }
184 
185  auto operator==(const iterator& other) const { return position == other.position; }
186  auto operator!=(const iterator& other) const { return position != other.position; }
187 
188  private:
189 
190  const std::function<MonteCarloSum()>& one_sum_generator;
191 
192  std::size_t position;
193 
194  };
195 
196 
197  MonteCarloSet(const TransformationType& trans, std::size_t s, const InputDistribution& dist,
198  const NoiseDistributions&...noise)
199  : samples(s),
200  one_sum_generator([&trans, &dist, &noise...] { return one(trans, dist, noise...); })
201  {}
202 
203 
204  auto begin() { return iterator(one_sum_generator, 0); }
205 
206 
207  auto end() { return iterator(one_sum_generator, samples); }
208 
209  private:
210 
211  const std::size_t samples;
212 
213  const std::function<MonteCarloSum()> one_sum_generator;
214 
215  };
216 
217  public:
218 
223  explicit MonteCarloTransform(const std::size_t samples = 100000) : size {samples} {}
224 
225 
226  using Base::operator();
227 
228 
236 #ifdef __cpp_concepts
237  template<gaussian_distribution InputDist, linearized_function<1> Trans, gaussian_distribution ... NoiseDists>
238  requires requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
239 #else
240  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
241  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
242  linearized_function<Trans, 1> and std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
243  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
244 #endif
245  auto operator()(const InputDist& x, const Trans& transformation, const NoiseDists& ...n) const
246  {
247  using MSet = MonteCarloSet<false, Trans, InputDist, NoiseDists...>;
248  auto m_set = MSet(transformation, size, x, n...);
249  auto binary_op = typename MSet::MonteCarloBinaryOp();
250  using MSum = typename MSet::MonteCarloSum;
251 
252  MSum m_sum = std::reduce(std::execution::par_unseq, m_set.begin(), m_set.end(), MSet::zero(), binary_op);
253  auto mean_output = make_self_contained(from_euclidean(m_sum.y_E));
254  auto out_covariance = make_self_contained(m_sum.yy / (size - 1.));
255  return GaussianDistribution {mean_output, out_covariance};
256  }
257 
258 
260 
261 
269 #ifdef __cpp_concepts
270  template<gaussian_distribution InputDist, linearized_function<1> Trans, gaussian_distribution ... NoiseDists>
271  requires requires(Trans g, InputDist x, NoiseDists...n) { g(mean_of(x), mean_of(n)...); }
272 #else
273  template<typename InputDist, typename Trans, typename ... NoiseDists, std::enable_if_t<
274  (gaussian_distribution<InputDist> and ... and gaussian_distribution<NoiseDists>) and
275  linearized_function<Trans, 1> and std::is_invocable_v<Trans, typename DistributionTraits<InputDist>::Mean,
276  typename DistributionTraits<NoiseDists>::Mean...>, int> = 0>
277 #endif
278  auto transform_with_cross_covariance(const InputDist& x, const Trans& transformation, const NoiseDists& ...n) const
279  {
280  using MSet = MonteCarloSet<true, Trans, InputDist, NoiseDists...>;
281  auto m_set = MSet(transformation, size, x, n...);
282  auto binary_op = typename MSet::MonteCarloBinaryOp();
283  using MSum = typename MSet::MonteCarloSum;
284 
285  MSum m_sum = std::reduce(std::execution::par_unseq, m_set.begin(), m_set.end(), MSet::zero(), binary_op);
286  auto mean_output = make_self_contained(from_euclidean(m_sum.y_E));
287  auto out_covariance = make_self_contained(m_sum.yy / (size - 1.));
288  auto cross_covariance = make_self_contained(m_sum.xy / (size - 1.));
289  auto out = GaussianDistribution {mean_output, out_covariance};
290  return std::tuple {std::move(out), std::move(cross_covariance)};
291  }
292 
293  private:
294 
295  const std::size_t size;
296 
297  };
298 
299 
300 }
301 
302 
303 #endif //OPENKALMAN_MONTECARLOTRANSFORM_HPP
A Monte Carlo transform from one Gaussian distribution to another.
Definition: MonteCarloTransform.hpp:35
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
auto transform_with_cross_covariance(const InputDist &x, const Trans &transformation, const NoiseDists &...n) const
Perform a Monte Carlo transform, also returning the cross-covariance.
Definition: MonteCarloTransform.hpp:278
No storage layout (e.g., if the elements are calculated rather than stored).
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
Definition: TransformBase.hpp:30
decltype(auto) constexpr reduce(BinaryFunction &&b, Arg &&arg)
Perform a partial reduction based on an associative binary function, across one or more indices...
Definition: reduce.hpp:143
The size of a coordinates::pattern.
Definition: dimension_of.hpp:37
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
constexpr bool zero
Specifies that a type is known at compile time to be a constant matrix of value zero.
Definition: zero.hpp:43
auto operator()(const InputDist &x, const Trans &transformation, const NoiseDists &...n) const
Perform a Monte Carlo transform from one statistical distribution to another.
Definition: MonteCarloTransform.hpp:245
Definition: MonteCarloTransform.hpp:167
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
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
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
MonteCarloTransform(const std::size_t samples=100000)
Constructor.
Definition: MonteCarloTransform.hpp:223
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
auto transform_with_cross_covariance(const InputDist &x, const T &t, const Ts &...ts) const
Perform one or more consecutive transforms, also returning the cross-covariance.
Definition: TransformBase.hpp:80