OpenKalman
Matrix.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_MATRIX_HPP
12 #define OPENKALMAN_MATRIX_HPP
13 
14 #include "basics/basics.hpp"
15 
16 namespace OpenKalman
17 {
18  namespace oin = OpenKalman::internal;
19 
20  // --------------------- //
21  // Matrix //
22  // --------------------- //
23 
24 #ifdef __cpp_concepts
25  template<fixed_pattern RowCoefficients, fixed_pattern ColumnCoefficients, typed_matrix_nestable NestedMatrix>
26  requires (coordinates::dimension_of_v<RowCoefficients> == index_dimension_of_v<NestedMatrix, 0>) and
27  (coordinates::dimension_of_v<ColumnCoefficients> == index_dimension_of_v<NestedMatrix, 1>) and
28  (not std::is_rvalue_reference_v<NestedMatrix>) and
29  (dynamic_pattern<RowCoefficients> == dynamic_dimension<NestedMatrix, 0>) and
30  (dynamic_pattern<ColumnCoefficients> == dynamic_dimension<NestedMatrix, 1>)
31 #else
32  template<typename RowCoefficients, typename ColumnCoefficients, typename NestedMatrix>
33 #endif
34  struct Matrix : oin::TypedMatrixBase<Matrix<RowCoefficients, ColumnCoefficients, NestedMatrix>, NestedMatrix,
35  RowCoefficients, ColumnCoefficients>
36  {
37 
38 #ifndef __cpp_concepts
39  static_assert(fixed_pattern<RowCoefficients>);
40  static_assert(fixed_pattern<ColumnCoefficients>);
41  static_assert(typed_matrix_nestable<NestedMatrix>);
42  static_assert(coordinates::dimension_of_v<RowCoefficients> == index_dimension_of_v<NestedMatrix, 0>);
43  static_assert(coordinates::dimension_of_v<ColumnCoefficients> == index_dimension_of_v<NestedMatrix, 1>);
44  static_assert(not std::is_rvalue_reference_v<NestedMatrix>);
45  static_assert(dynamic_pattern<RowCoefficients> == dynamic_dimension<NestedMatrix, 0>);
46  static_assert(dynamic_pattern<ColumnCoefficients> == dynamic_dimension<NestedMatrix, 1>);
47 #endif
48 
49  using Scalar = scalar_type_of_t<NestedMatrix>;
50 
51  private:
52 
53  using Base = oin::TypedMatrixBase<Matrix, NestedMatrix, RowCoefficients, ColumnCoefficients>;
54 
55  public:
56 
57  using Base::Base;
58 
59 
61 #ifdef __cpp_concepts
62  template<typed_matrix Arg> requires (not std::derived_from<std::decay_t<Arg>, Matrix>) and
63  (not euclidean_transformed<Arg>) and
64  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
65  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
66  //requires(Arg&& arg) { NestedMatrix {nested_object(std::forward<Arg>(arg))}; } // \todo 't work in GCC 10
67  std::constructible_from<NestedMatrix, decltype(nested_object(std::declval<Arg&&>()))>
68 #else
69  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and not std::is_base_of_v<Matrix, std::decay_t<Arg>> and
70  not euclidean_transformed<Arg> and
71  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
72  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
73  stdex::constructible_from<NestedMatrix, decltype(nested_object(std::declval<Arg&&>()))>, int> = 0>
74 #endif
75  Matrix(Arg&& arg) : Base {nested_object(std::forward<Arg>(arg))} {}
76 
77 
79 #ifdef __cpp_concepts
80  template<euclidean_transformed Arg> requires
81  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
82  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
83  requires(Arg&& arg) { NestedMatrix {from_euclidean<RowCoefficients>(nested_object(std::forward<Arg>(arg)))}; }
84 #else
85  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and euclidean_transformed<Arg> and
86  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
87  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
88  stdex::constructible_from<NestedMatrix,
89  decltype(from_euclidean<RowCoefficients>(nested_object(std::declval<Arg&&>())))>, int> = 0>
90 #endif
91  Matrix(Arg&& arg)
92  : Base {from_euclidean<RowCoefficients>(nested_object(std::forward<Arg>(arg)))} {}
93 
94 
96 #ifdef __cpp_concepts
97  template<typed_matrix_nestable Arg> requires (index_dimension_of_v<Arg, 0> == index_dimension_of_v<NestedMatrix, 0>) and
98  (index_dimension_of_v<Arg, 1> == index_dimension_of_v<NestedMatrix, 1>) and
99  std::constructible_from<NestedMatrix, Arg&&>
100 #else
101  template<typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and
102  (index_dimension_of<Arg, 0>::value == index_dimension_of<NestedMatrix, 0>::value) and
103  (index_dimension_of<Arg, 1>::value == index_dimension_of<NestedMatrix, 1>::value) and
104  stdex::constructible_from<NestedMatrix, Arg&&>, int> = 0>
105 #endif
106  explicit Matrix(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
107 
108 
110 #ifdef __cpp_concepts
111  template<covariance Arg> requires
112  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
113  compares_with<vector_space_descriptor_of_t<Arg, 0>, ColumnCoefficients> and
114  requires(Arg&& arg) { NestedMatrix {to_dense_object(std::forward<Arg>(arg))}; }
115 #else
116  template<typename Arg, std::enable_if_t<covariance<Arg> and
117  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
118  compares_with<vector_space_descriptor_of_t<Arg, 0>, ColumnCoefficients> and
119  stdex::constructible_from<NestedMatrix, dense_writable_matrix_t<Arg>>, int> = 0>
120 #endif
121  Matrix(Arg&& arg) : Base {to_dense_object(std::forward<Arg>(arg))} {}
122 
123 
125 #ifdef __cpp_concepts
126  template<typed_matrix Arg> requires (not euclidean_transformed<Arg>) and
127  (not std::derived_from<std::decay_t<Arg>, Matrix>) and
128  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
129  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
130  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, nested_object_of_t<Arg&&>>
131 #else
132  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and (not euclidean_transformed<Arg>) and
133  (not std::is_base_of_v<Matrix, std::decay_t<Arg>>) and
134  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
135  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
136  std::is_assignable_v<std::add_lvalue_reference_t<NestedMatrix>, nested_object_of_t<Arg&&>>, int> = 0>
137 #endif
138  auto& operator=(Arg&& other)
139  {
140  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
141  {
142  Base::operator=(nested_object(std::forward<Arg>(other)));
143  }
144  return *this;
145  }
146 
147 
149 #ifdef __cpp_concepts
150  template<euclidean_transformed Arg> requires
151  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
152  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
153  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, decltype(from_euclidean<RowCoefficients>(std::declval<nested_object_of_t<Arg>>()))>
154 #else
155  template<typename Arg, std::enable_if_t<euclidean_transformed<Arg> and
156  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
157  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients> and
158  std::is_assignable_v<std::add_lvalue_reference_t<NestedMatrix>, decltype(from_euclidean<RowCoefficients>(std::declval<nested_object_of_t<Arg>>()))>,
159  int> = 0>
160 #endif
161  auto& operator=(Arg&& other)
162  {
163  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
164  {
165  Base::operator=(from_euclidean<RowCoefficients>(nested_object(std::forward<Arg>(other))));
166  }
167  return *this;
168  }
169 
170 
172 #ifdef __cpp_concepts
173  template<typed_matrix_nestable Arg> requires std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, Arg&&>
174 #else
175  template<typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and
176  std::is_assignable_v<std::add_lvalue_reference_t<NestedMatrix>, Arg&&>, int> = 0>
177 #endif
178  auto& operator=(Arg&& arg)
179  {
180  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
181  {
182  Base::operator=(std::forward<Arg>(arg));
183  }
184  return *this;
185  }
186 
187 
189  auto& operator+=(const Matrix& other)
190  {
191  this->nested_object() += other.nested_object();
192  return *this;
193  }
194 
196 #ifdef __cpp_concepts
197  template<typed_matrix Arg> requires
198  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients> and
199  coordinates::compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients>
200 #else
201  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and
202  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients>and
203  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients>, int> = 0>
204 #endif
205  auto& operator+=(Arg&& other)
206  {
207  this->nested_object() += nested_object(std::forward<Arg>(other));
208  return *this;
209  }
210 
211 
213 #ifdef __cpp_concepts
214  template<distribution Arg> requires (coordinates::euclidean_pattern<ColumnCoefficients>) and
215  (compares_with<typename DistributionTraits<Arg>::StaticDescriptor, RowCoefficients>)
216 #else
217  template<typename Arg, std::enable_if_t<distribution<Arg> and (coordinates::euclidean_pattern<ColumnCoefficients>) and
218  (compares_with<typename DistributionTraits<Arg>::StaticDescriptor, RowCoefficients>), int> = 0>
219 #endif
220  auto& operator+=(const Arg& arg)
221  {
222  apply_columnwise([&arg](auto& col) { col += arg().nested_object(); }, this->nested_object());
223  return *this;
224  }
225 
226 
228  auto& operator-=(const Matrix& other)
229  {
230  this->nested_object() -= other.nested_object();
231  return *this;
232  }
233 
234 
236 #ifdef __cpp_concepts
237  template<typed_matrix Arg> requires
238  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients>and
239  coordinates::compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients>
240 #else
241  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and
242  compares_with<vector_space_descriptor_of_t<Arg, 0>, RowCoefficients>and
243  compares_with<vector_space_descriptor_of_t<Arg, 1>, ColumnCoefficients>, int> = 0>
244 #endif
245  auto& operator-=(Arg&& other)
246  {
247  this->nested_object() -= nested_object(std::forward<Arg>(other));
248  return *this;
249  }
250 
251 
253 #ifdef __cpp_concepts
254  template<distribution Arg> requires (coordinates::euclidean_pattern<ColumnCoefficients>) and
255  (compares_with<typename DistributionTraits<Arg>::StaticDescriptor, RowCoefficients>)
256 #else
257  template<typename Arg, std::enable_if_t<distribution<Arg> and (coordinates::euclidean_pattern<ColumnCoefficients>) and
258  (compares_with<typename DistributionTraits<Arg>::StaticDescriptor, RowCoefficients>), int> = 0>
259 #endif
260  auto& operator-=(const Arg& arg)
261  {
262  apply_columnwise([&arg](auto& col){ col -= arg().nested_object(); }, this->nested_object());
263  return *this;
264  }
265 
266  private:
267 
268  template<typename CR = RowCoefficients, typename CC = ColumnCoefficients, typename Arg>
269  static auto make(Arg&& arg)
270  {
271  return Matrix<CR, CC, std::decay_t<Arg>>(std::forward<Arg>(arg));
272  }
273 
274  };
275 
276 
277  // ------------------------------- //
278  // Deduction Guides //
279  // ------------------------------- //
280 
282 #ifdef __cpp_concepts
283  template<typed_matrix_nestable M>
284 #else
285  template<typename M, std::enable_if_t<typed_matrix_nestable<M>, int> = 0>
286 #endif
287  explicit Matrix(M&&) -> Matrix<Dimensions<index_dimension_of_v<M, 0>>, Dimensions<index_dimension_of_v<M, 1>>, passable_t<M>>;
288 
289 
290 #ifdef __cpp_concepts
292 #else
293  template<typename M, std::enable_if_t<typed_matrix_nestable<M>, int> = 0>
294 #endif
295  explicit Matrix(M&&, const Cs&...) -> Matrix<Cs..., passable_t<M>>;
296 
297 
299 #ifdef __cpp_concepts
300  template<typed_matrix V> requires (not euclidean_transformed<V>)
301 #else
302  template<typename V, std::enable_if_t<typed_matrix<V> and not euclidean_transformed<V>, int> = 0>
303 #endif
304  Matrix(V&&) -> Matrix<
305  vector_space_descriptor_of_t<V, 0>,
306  vector_space_descriptor_of_t<V, 1>,
307  passable_t<nested_object_of_t<V>>>;
308 
309 
311 #if defined(__cpp_concepts) and OPENKALMAN_CPP_FEATURE_CONCEPTS_2
312  // \todo Unlike SFINAE version, this incorrectly matches V==Mean and V==Matrix in both GCC 10.1.0 and clang 10.0.0:
313  template<euclidean_transformed V> requires has_untyped_index<V, 1>
314 #else
315  template<typename V, std::enable_if_t<euclidean_transformed<V> and has_untyped_index<V, 1>, int> = 0>
316 #endif
317  Matrix(V&&) -> Matrix<
318  vector_space_descriptor_of_t<V, 0>,
319  vector_space_descriptor_of_t<V, 1>,
320  decltype(from_euclidean<vector_space_descriptor_of_t<V, 0>>(
321  nested_object(std::forward<V>(std::declval<V>()))))>;
322 
323 
325 #ifdef __cpp_concepts
326  template<covariance V>
327 #else
328  template<typename V, std::enable_if_t<covariance<V>, int> = 0>
329 #endif
330  Matrix(V&&) -> Matrix<
331  vector_space_descriptor_of_t<V, 0>,
332  vector_space_descriptor_of_t<V, 0>,
334 
335 
336  // ------------------------- //
337  // Interfaces //
338  // ------------------------- //
339 
340  namespace interface
341  {
342  template<typename RowCoeffs, typename ColCoeffs, typename NestedMatrix>
343  struct object_traits<Matrix<RowCoeffs, ColCoeffs, NestedMatrix>>
344  {
345  using scalar_type = scalar_type_of_t<NestedMatrix>;
346 
347  template<typename Arg>
348  static constexpr auto count_indices(const Arg& arg) { return collections::size_of_v<decltype(arg.my_dimensions)>; }
349 
350  template<typename Arg, typename N>
351  static constexpr auto get_pattern_collection(Arg&& arg, N n)
352  {
353  if constexpr (values::fixed<N>)
354  return std::get<N>(std::forward<Arg>(arg).my_dimensions);
355  else if constexpr (compares_with<RowCoeffs, ColCoeffs>)
356  return std::get<0>(std::forward<Arg>(arg).my_dimensions);
357  else
358  return std::apply(
359  [](const auto&...ds, N n){ return std::array {DynamicDescriptor<scalar_type>{ds}...}[n]; },
360  arg.my_dimensions, n);
361  }
362 
363 
364  template<typename Arg>
365  static decltype(auto) nested_object(Arg&& arg)
366  {
367  return std::forward<Arg>(arg).nested_object();
368  }
369 
370 
371  template<typename Arg>
372  static constexpr auto get_constant(const Arg& arg)
373  {
374  return constant_value{arg.nestedExpression()};
375  }
376 
377 
378  template<typename Arg>
379  static constexpr auto get_constant_diagonal(const Arg& arg)
380  {
381  if constexpr (coordinates::euclidean_pattern<RowCoeffs> and coordinates::euclidean_pattern<ColCoeffs>)
382  return constant_diagonal_value {arg.nestedExpression()};
383  else
384  return std::monostate {};
385  }
386 
387 
388  template<applicability b>
389  static constexpr bool one_dimensional = OpenKalman::one_dimensional<NestedMatrix, b>;
390 
391 
392  template<applicability b>
393  static constexpr bool is_square = OpenKalman::square_shaped<NestedMatrix, b>;
394 
395 
396  template<triangle_type t>
397  static constexpr bool triangle_type_value = compares_with<RowCoeffs, ColCoeffs>and triangular_matrix<NestedMatrix, t>;
398 
399 
400  static constexpr bool is_triangular_adapter = false;
401 
402 
403  static constexpr bool is_hermitian = compares_with<RowCoeffs, ColCoeffs>and hermitian_matrix<NestedMatrix>;
404 
405 
406  #ifdef __cpp_lib_concepts
407  template<typename Arg, typename...I> requires element_gettable<nested_object_of_t<Arg&&>, sizeof...(I)>
408  #else
409  template<typename Arg, typename...I, std::enable_if_t<element_gettable<typename nested_object_of<Arg&&>::type, sizeof...(I)>, int> = 0>
410  #endif
411  static constexpr decltype(auto) get(Arg&& arg, I...i)
412  {
413  return get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), i...);
414  }
415 
416 
417  #ifdef __cpp_lib_concepts
418  template<typename Arg, typename I, typename...Is> requires writable_by_component<nested_object_of_t<Arg&>, 1 + sizeof...(Is)>
419  #else
420  template<typename Arg, typename I, typename...Is, std::enable_if_t<writable_by_component<typename nested_object_of<Arg&>::type, 1 + sizeof...(Is)>, int> = 0>
421  #endif
422  static constexpr void set(Arg& arg, const scalar_type_of_t<Arg>& s, I i, Is...is)
423  {
424  set_component(OpenKalman::nested_object(arg), s, i, is...);
425  }
426 
427 
428  static constexpr bool is_writable = library_interface<std::decay_t<NestedMatrix>>::is_writable;
429 
430 
431 #ifdef __cpp_lib_concepts
432  template<typename Arg> requires raw_data_defined_for<NestedMatrix>
433 #else
434  template<typename Arg, std::enable_if_t<raw_data_defined_for<NestedMatrix>, int> = 0>
435 #endif
436  static constexpr auto * const
437  raw_data(Arg& arg) { return internal::raw_data(nested_object(arg)); }
438 
439 
440  static constexpr data_layout layout = layout_of_v<NestedMatrix>;
441 
442  };
443 
444  }
445 
446 }
447 
448 
449 #endif
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
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:58
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:56
auto & operator+=(Arg &&other)
Increment from another typed matrix.
Definition: Matrix.hpp:205
Matrix(Arg &&arg)
Construct from a compatible typed_matrix.
Definition: Matrix.hpp:75
auto & operator-=(Arg &&other)
Decrement from another typed matrix.
Definition: Matrix.hpp:245
constexpr bool euclidean_transformed
Specifies that T is a Euclidean mean that actually has coefficients that are transformed to Euclidean...
Definition: object-types.hpp:87
auto & operator+=(const Arg &arg)
Add a stochastic value to each column of the matrix, based on a distribution.
Definition: Matrix.hpp:220
auto & operator+=(const Matrix &other)
Increment from another Matrix.
Definition: Matrix.hpp:189
constexpr auto count_indices(const T &)
Get the number of indices necessary to address all the components of an indexible object...
Definition: count_indices.hpp:51
constexpr bool pattern
An object describing the set of coordinates associated with a tensor index.
Definition: pattern.hpp:31
auto & operator=(Arg &&arg)
Assign from a compatible typed_matrix_nestable.
Definition: Matrix.hpp:178
decltype(auto) constexpr get_pattern_collection(T &&t)
Get the coordinates::pattern_collection associated with indexible object T.
Definition: get_pattern_collection.hpp:59
auto & operator-=(const Matrix &other)
Decrement from another Matrix.
Definition: Matrix.hpp:228
decltype(auto) constexpr to_dense_object(Arg &&arg)
Convert the argument to a dense, writable matrix of a particular scalar type.
Definition: to_dense_object.hpp:37
decltype(auto) constexpr apply(F &&f, T &&t)
A generalization of std::apply.
Definition: apply.hpp:49
A wrapper type&#39;s nested object type, if it exists.
Definition: nested_object_of.hpp:34
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.
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
scalar_type_of_t< CrossCovarianceMatrix > Scalar
Scalar type for this matrix.
Definition: Matrix.hpp:49
auto & operator-=(const Arg &arg)
Subtract a stochastic value to each column of the matrix, based on a distribution.
Definition: Matrix.hpp:260
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:42
Definition: object_traits.hpp:38
constexpr auto constant_value(T &&t)
The constant value associated with a constant_object or constant_diagonal_object. ...
Definition: constant_value.hpp:37
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
Basic definitions for OpenKalman as a whole.
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:35
constexpr bool compares_with
Compares two coordinates::pattern objects.
Definition: compares_with.hpp:475
Definition: basics.hpp:48
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:292
constexpr std::size_t size_of_v
Helper for collections::size_of.
Definition: size_of.hpp:60
auto & operator=(Arg &&other)
Assign from a compatible typed_matrix.
Definition: Matrix.hpp:138