OpenKalman
LinearTransformation.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) 2018-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 
11 #ifndef OPENKALMAN_LINEARTRANSFORMATION_HPP
12 #define OPENKALMAN_LINEARTRANSFORMATION_HPP
13 
14 
15 namespace OpenKalman
16 {
17  namespace oin = OpenKalman::internal;
18 
19 
30 #ifdef __cpp_concepts
31  template<fixed_pattern InputCoefficients, fixed_pattern OutputCoefficients, typed_matrix_nestable TransformationMatrix,
32  typed_matrix_nestable ... PerturbationTransformationMatrices> requires
33  (index_dimension_of_v<TransformationMatrix, 0> == coordinates::dimension_of_v<OutputCoefficients>) and
34  (index_dimension_of_v<TransformationMatrix, 1> == coordinates::dimension_of_v<InputCoefficients>) and
35  ((index_dimension_of_v<PerturbationTransformationMatrices, 0> == coordinates::dimension_of_v<OutputCoefficients>) and ...) and
36  (square_shaped<PerturbationTransformationMatrices> and ...)
37 #else
38  template<typename InputCoefficients, typename OutputCoefficients, typename TransformationMatrix,
39  typename ... PerturbationTransformationMatrices>
40 #endif
42 
43 
44  namespace internal
45  {
46 #ifdef __cpp_concepts
47  template<typename In, typename Out, typename Tm, typename...Pm, std::size_t order> requires (order <= 1)
48  struct is_linearized_function<LinearTransformation<In, Out, Tm, Pm...>, order> : std::true_type {};
49 #else
50  template<typename In, typename Out, typename Tm, typename...Pm, std::size_t order>
51  struct is_linearized_function<LinearTransformation<In, Out, Tm, Pm...>, order, std::enable_if_t<order <= 1>>
52  : std::true_type {};
53 #endif
54 
55 
56 #ifndef __cpp_concepts
57  namespace detail
58  {
59  template<typename T, typename R, typename C, typename = void>
60  struct is_linear_transformation_input : std::false_type {};
61 
62  template<typename T, typename R, typename C>
63  struct is_linear_transformation_input<T, R, C, std::enable_if_t<
64  typed_matrix<T> and compares_with<vector_space_descriptor_of_t<T, 0>, R>and
65  compares_with<vector_space_descriptor_of_t<T, 1>, C>>>: std::true_type {};
66 
67  template<typename T, typename R, typename C>
68  struct is_linear_transformation_input<T, R, C, std::enable_if_t<
69  typed_matrix_nestable<T> and (index_dimension_of<T, 0>::value == coordinates::dimension_of_v<R>) and
70  (index_dimension_of<T, 1>::value == coordinates::dimension_of_v<C>)>> : std::true_type {};
71  }
72 #endif
73 
74 
82 #ifdef __cpp_concepts
83  template<typename T, typename RowCoefficients, typename ColumnCoefficients = RowCoefficients>
84  concept linear_transformation_input =
85  (typed_matrix<T> or typed_matrix_nestable<T>) and
86  fixed_pattern<RowCoefficients> and fixed_pattern<ColumnCoefficients> and
87  (not typed_matrix<T> or (compares_with<vector_space_descriptor_of_t<T, 0>, RowCoefficients>and
88  compares_with<vector_space_descriptor_of_t<T, 1>, ColumnCoefficients>)) and
89  (not typed_matrix_nestable<T> or (index_dimension_of_v<T, 0> == coordinates::dimension_of_v<RowCoefficients> and
90  index_dimension_of_v<T, 1> == coordinates::dimension_of_v<ColumnCoefficients>));
91 #else
92  template<typename T, typename RowCoefficients, typename ColumnCoefficients = RowCoefficients>
93  constexpr bool linear_transformation_input =
94  fixed_pattern<RowCoefficients> and fixed_pattern<ColumnCoefficients> and
96 #endif
97 
98  } // namespace internal
99 
100 
101 #ifdef __cpp_concepts
102  template<fixed_pattern InputCoefficients, fixed_pattern OutputCoefficients, typed_matrix_nestable TransformationMatrix,
103  typed_matrix_nestable ... PerturbationTransformationMatrices> requires
104  (index_dimension_of_v<TransformationMatrix, 0> == coordinates::dimension_of_v<OutputCoefficients>) and
105  (index_dimension_of_v<TransformationMatrix, 1> == coordinates::dimension_of_v<InputCoefficients>) and
106  ((index_dimension_of_v<PerturbationTransformationMatrices, 0> == coordinates::dimension_of_v<OutputCoefficients>) and ...) and
107  (square_shaped<PerturbationTransformationMatrices> and ...)
108 #else
109  template<typename InputCoefficients, typename OutputCoefficients, typename TransformationMatrix,
110  typename ... PerturbationTransformationMatrices>
111 #endif
112  struct LinearTransformation
113  {
114 
115 #ifndef __cpp_concepts
116  static_assert(fixed_pattern<InputCoefficients>);
117  static_assert(fixed_pattern<OutputCoefficients>);
118  static_assert(typed_matrix_nestable<TransformationMatrix>);
119  static_assert((typed_matrix_nestable<PerturbationTransformationMatrices> and ...));
120  static_assert(index_dimension_of_v<TransformationMatrix, 0> == coordinates::dimension_of_v<OutputCoefficients>);
121  static_assert(index_dimension_of_v<TransformationMatrix, 1> == coordinates::dimension_of_v<InputCoefficients>);
122  static_assert(((index_dimension_of_v<PerturbationTransformationMatrices, 0> == coordinates::dimension_of_v<OutputCoefficients>) and ...));
123  static_assert((square_shaped<PerturbationTransformationMatrices> and ...));
124 #endif
125 
126  private:
127 
128  template<typename Jacobians, typename InputTuple, std::size_t...ints>
129  constexpr auto sumprod(Jacobians&& js, InputTuple&& inputs, std::index_sequence<ints...>) const
130  {
131  return make_self_contained(
132  ((std::get<ints>(std::forward<Jacobians>(js)) * std::get<ints>(std::forward<InputTuple>(inputs))) + ...));
133  }
134 
135 
136  using TransformationMatricesTuple = std::tuple<
139 
140  const TransformationMatricesTuple transformation_matrices;
141 
142  public:
143 
147  LinearTransformation(const TransformationMatrix& mat, const PerturbationTransformationMatrices& ... p_mats)
148  : transformation_matrices {mat, p_mats...} {}
149 
150 
156 #ifdef __cpp_concepts
157  template<oin::linear_transformation_input<OutputCoefficients, InputCoefficients> T,
158  oin::linear_transformation_input<OutputCoefficients> ... Ps>
159 #else
160  template<typename T, typename ... Ps, std::enable_if_t<
161  oin::linear_transformation_input<T, OutputCoefficients, InputCoefficients> and
162  (oin::linear_transformation_input<Ps, OutputCoefficients> and ...), int> = 0>
163 #endif
164  LinearTransformation(T&& mat, Ps&& ... p_mats)
165  : transformation_matrices {std::forward<T>(mat), std::forward<Ps>(p_mats)...} {}
166 
167 
169 #ifdef __cpp_concepts
170  template<transformation_input<InputCoefficients> In, perturbation<OutputCoefficients> ... Perturbations>
171 #else
172  template<typename In, typename ... Perturbations, std::enable_if_t<transformation_input<In, InputCoefficients> and
173  (perturbation<Perturbations, OutputCoefficients> and ...), int> = 0>
174 #endif
175  auto operator()(In&& in, Perturbations&& ... ps) const
176  {
177  return sumprod(
178  jacobian(in, ps...),
179  std::forward_as_tuple(std::forward<In>(in), oin::get_perturbation(std::forward<Perturbations>(ps))...),
180  std::make_index_sequence<sizeof...(Perturbations) + 1> {});
181  }
182 
183 
185 #ifdef __cpp_concepts
186  template<transformation_input<InputCoefficients> In, perturbation<OutputCoefficients> ... Perturbations>
187 #else
188  template<typename In, typename ... Perturbations, std::enable_if_t<transformation_input<In, InputCoefficients> and
189  (perturbation<Perturbations, OutputCoefficients> and ...), int> = 0>
190 #endif
191  auto jacobian(const In&, const Perturbations& ...) const
192  {
193  constexpr auto mat_count = std::tuple_size_v<TransformationMatricesTuple>;
194 
195  // If there are more inputs than tests matrices, pad the tuple with extra identity matrices.
196  if constexpr(sizeof...(Perturbations) + 1 > mat_count)
197  {
198  constexpr auto pad_size = sizeof...(Perturbations) + 1 - mat_count;
199  auto id = make_vector_space_adapter(make_identity_matrix_like<TransformationMatrix>(), OutputCoefficients{}, OutputCoefficients{});
200  return std::tuple_cat(transformation_matrices, OpenKalman::collections::internal::repeat_tuple_view<pad_size, decltype(id)&&>(std::move(id)));
201  }
202  else
203  {
204  return oin::tuple_slice<0, sizeof...(Perturbations) + 1>(transformation_matrices);
205  }
206  }
207 
208  };
209 
210 
211  /*
212  * Deduction guides
213  */
214 
215 #ifdef __cpp_concepts
216  template<typed_matrix T, oin::linear_transformation_input<vector_space_descriptor_of_t<T, 0>> ... Ps>
217 #else
218  template<typename T, typename ... Ps, std::enable_if_t<
219  (typed_matrix<T> and ... and oin::linear_transformation_input<Ps, vector_space_descriptor_of_t<T, 0>>),
220  int> = 0>
221 #endif
222  LinearTransformation(T&&, Ps&& ...)
226  equivalent_self_contained_t<nested_object_of_t<T>>,
227  equivalent_self_contained_t<std::conditional_t<typed_matrix<Ps>, nested_object_of_t<Ps>, Ps>>...>;
228 
229 
230 #ifdef __cpp_concepts
231  template<typed_matrix_nestable T, oin::linear_transformation_input<Dimensions<index_dimension_of_v<T, 0>>> ... Ps>
232 #else
233  template<typename T, typename ... Ps, std::enable_if_t<
234  (typed_matrix_nestable<T> and ... and oin::linear_transformation_input<Ps, Dimensions<index_dimension_of<T, 0>::value>>), int> = 0>
235 #endif
236  LinearTransformation(T&&, Ps&& ...)
238  Dimensions<index_dimension_of_v<T, 1>>,
239  Dimensions<index_dimension_of_v<T, 0>>,
240  equivalent_self_contained_t<T>,
241  equivalent_self_contained_t<Ps>...>;
242 
243 
244  /*
245  * Traits
246  */
247 
248  namespace internal
249  {
250  template<typename InC, typename OutC, typename T, typename ... Ps>
251  struct is_linearized_function<LinearTransformation<InC, OutC, T, Ps...>, 0> : std::true_type {};
252 
253  template<typename InC, typename OutC, typename T, typename ... Ps>
254  struct is_linearized_function<LinearTransformation<InC, OutC, T, Ps...>, 1> : std::true_type {};
255 
256  } // namespace internal
257 
258 }
259 
260 
261 #endif //OPENKALMAN_LINEARTRANSFORMATION_HPP
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:66
auto jacobian(const In &, const Perturbations &...) const
Returns a tuple of the Jacobians for the input and each perturbation term.
Definition: LinearTransformation.hpp:191
auto make_vector_space_adapter(Arg &&arg, Descriptors &&descriptors)
If necessary, wrap an object in a wrapper that adds vector space descriptors for each index...
Definition: make_vector_space_adapter.hpp:37
A linear tests from one single-column vector to another.
Definition: LinearTransformation.hpp:41
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
LinearTransformation(const TransformationMatrix &mat, const PerturbationTransformationMatrices &... p_mats)
Constructor.
Definition: LinearTransformation.hpp:147
constexpr bool typed_matrix_nestable
Specifies a type that is nestable in a general typed matrix (e.g., matrix, mean, or euclidean_mean) ...
Definition: object-types.hpp:253
The root namespace for OpenKalman.
Definition: basics.hpp:34
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 auto tuple_slice(Arg &&arg)
Takes a slice of a tuple, given an index range.
Definition: tuple_slice.hpp:132
auto operator()(In &&in, Perturbations &&... ps) const
Applies the tests.
Definition: LinearTransformation.hpp:175
constexpr bool fixed_pattern
A coordinates::pattern for which the size is fixed at compile time.
Definition: fixed_pattern.hpp:47
Definition: TransformationTraits.hpp:39
Definition: basics.hpp:48
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:448
LinearTransformation(T &&mat, Ps &&... p_mats)
General constructor.
Definition: LinearTransformation.hpp:164