OpenKalman
Covariance.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_COVARIANCE_HPP
12 #define OPENKALMAN_COVARIANCE_HPP
13 
14 
15 namespace OpenKalman
16 {
17  namespace oin = OpenKalman::internal;
18 
19  // ------------ //
20  // Covariance //
21  // ------------ //
22 
23 #ifdef __cpp_concepts
24  template<fixed_pattern StaticDescriptor, covariance_nestable NestedMatrix> requires
25  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<NestedMatrix, 0>) and
26  (not std::is_rvalue_reference_v<NestedMatrix>) and values::number<scalar_type_of_t<NestedMatrix>>
27 #else
28  template<typename StaticDescriptor, typename NestedMatrix>
29 #endif
30  struct Covariance : oin::CovarianceImpl<Covariance<StaticDescriptor, NestedMatrix>, NestedMatrix>
31  {
32 
33 #ifndef __cpp_concepts
34  static_assert(fixed_pattern<StaticDescriptor>);
35  static_assert(covariance_nestable<NestedMatrix>);
36  static_assert(coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<NestedMatrix, 0>);
37  static_assert(not std::is_rvalue_reference_v<NestedMatrix>);
39 #endif
40 
42 
43  private:
44 
46  using typename Base::CholeskyNestedMatrix;
47  using Base::nested_object;
48  using Base::cholesky_nested_matrix;
49  using Base::synchronization_direction;
50  using Base::synchronize_forward;
51  using Base::synchronize_reverse;
52  using Base::mark_nested_matrix_changed;
53  using Base::mark_cholesky_nested_matrix_changed;
54  using Base::mark_synchronized;
55 
56 
57  // May be accessed externally through MatrixTraits:
58  static constexpr auto dim = index_dimension_of_v<NestedMatrix, 0>;
59 
60  // May be accessed externally through MatrixTraits:
61  static constexpr TriangleType storage_triangle =
62  triangle_type_of_v<typename MatrixTraits<std::decay_t<NestedMatrix>>::template TriangularAdapterFrom<>>;
63 
64  // A self-adjoint nested matrix type.
65  using NestedSelfAdjoint = std::conditional_t<hermitian_matrix<NestedMatrix>, NestedMatrix,
66  typename MatrixTraits<std::decay_t<NestedMatrix>>::template SelfAdjointMatrixFrom<storage_triangle>>;
67 
68 
69  // A function that makes a covariance from a nested matrix.
70  template<typename C = StaticDescriptor, typename Arg>
71  static auto make(Arg&& arg)
72  {
73  return Covariance<C, std::decay_t<Arg>>(std::forward<Arg>(arg));
74  }
75 
76 
80 #ifdef __cpp_concepts
81  template<triangular_covariance M> requires (not diagonal_matrix<M> or identity_matrix<M> or zero<M>) and
82  (hermitian_matrix<nested_object_of_t<M>> == hermitian_matrix<NestedMatrix>)
83 #else
84  template<typename M, std::enable_if_t<triangular_covariance<M> and
85  (not diagonal_matrix<M> or identity_matrix<M> or zero<M>) and
86  (hermitian_matrix<nested_object_of_t<M>> == hermitian_matrix<NestedMatrix>), int> = 0>
87 #endif
88  Covariance(M&& m) : Base {std::forward<M>(m)} {}
89 
90  public:
91 
92  // -------------- //
93  // Constructors //
94  // -------------- //
95 
97 #ifdef __cpp_concepts
98  Covariance() requires std::default_initializable<Base>
99 #else
100  template<typename T = Base, std::enable_if_t<std::is_default_constructible_v<T>, int> = 0>
102 #endif
103  : Base {} {}
104 
105 
109 #ifdef __cpp_concepts
110  template<self_adjoint_covariance M> requires (not std::derived_from<std::decay_t<M>, Covariance>) and
111  requires(M&& m) { Base {std::forward<M>(m)}; }
112 #else
113  template<typename M, std::enable_if_t<self_adjoint_covariance<M> and
114  (not std::is_base_of_v<Covariance, std::decay_t<M>>) and std::is_constructible_v<Base, M&&>, int> = 0>
115 #endif
116  Covariance(M&& m) : Base {std::forward<M>(m)} {}
117 
118 
122 #ifdef __cpp_concepts
123  template<covariance_nestable M> requires requires(M&& m) { Base {std::forward<M>(m)}; }
124 #else
125  template<typename M, std::enable_if_t<covariance_nestable<M> and std::is_constructible_v<Base, M&&>, int> = 0>
126 #endif
127  explicit Covariance(M&& m) : Base {std::forward<M>(m)} {}
128 
129 
136 #ifdef __cpp_concepts
137  template<typed_matrix M> requires (square_shaped<M> or (diagonal_matrix<NestedMatrix> and vector<M>)) and
138  compares_with<vector_space_descriptor_of_t<M, 0>, StaticDescriptor> and
139  requires(M&& m) { Base {oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<M>(m))}; }
140 #else
141  template<typename M, std::enable_if_t<typed_matrix<M> and
142  (square_shaped<M> or (diagonal_matrix<NestedMatrix> and vector<M>)) and
143  compares_with<vector_space_descriptor_of_t<M, 0>, StaticDescriptor> and
144  std::is_constructible_v<Base,
145  decltype(oin::to_covariance_nestable<NestedSelfAdjoint>(std::declval<M&&>()))>, int> = 0>
146 #endif
147  explicit Covariance(M&& m)
148  : Base {oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<M>(m))} {}
149 
150 
157 #ifdef __cpp_concepts
158  template<typed_matrix_nestable M> requires (not covariance_nestable<M>) and
159  (square_shaped<M> or (diagonal_matrix<NestedMatrix> and vector<M>)) and
160  requires(M&& m) { Base {oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<M>(m))};
161  }
162 #else
163  template<typename M, std::enable_if_t<typed_matrix_nestable<M> and (not covariance_nestable<M>) and
164  (square_shaped<M> or (diagonal_matrix<NestedMatrix> and vector<M>)) and
165  std::is_constructible_v<Base,
166  decltype(oin::to_covariance_nestable<NestedSelfAdjoint>(std::declval<M&&>()))>, int> = 0>
167 #endif
168  explicit Covariance(M&& m)
169  : Base {oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<M>(m))} {}
170 
171 
179 #ifdef __cpp_concepts
180  template<std::convertible_to<const Scalar> ... Args> requires (sizeof...(Args) > 0) and
181  requires(Args ... args) { Base {make_dense_object_from<NestedSelfAdjoint>(static_cast<const Scalar>(args)...)};
182  }
183 #else
184  template<typename ... Args, std::enable_if_t<(std::is_convertible_v<Args, const Scalar> and ...) and
185  ((diagonal_matrix<NestedMatrix> and sizeof...(Args) == dim) or
186  (sizeof...(Args) == dim * dim)) and std::is_constructible_v<Base, NestedSelfAdjoint&&>, int> = 0>
187 #endif
188  Covariance(Args ... args)
189  : Base {make_dense_object_from<NestedSelfAdjoint>(static_cast<const Scalar>(args)...)} {}
190 
191 
192  // ---------------------- //
193  // Assignment Operators //
194  // ---------------------- //
195 
197 #ifdef __cpp_concepts
198  template<self_adjoint_covariance Arg>
199  requires (not std::derived_from<std::decay_t<Arg>, Covariance>) and
200  compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor> and
201  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, nested_object_of_t<Arg&&>>
202 #else
203  template<typename Arg, std::enable_if_t<(not std::is_base_of_v<Covariance, std::decay_t<Arg>>) and
204  (self_adjoint_covariance<Arg> and compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>and
205  std::is_assignable_v<std::add_lvalue_reference_t<NestedMatrix>, nested_object_of_t<Arg&&>>, int> = 0>
206 #endif
207  auto& operator=(Arg&& arg)
208  {
209  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
210  {
211  Base::operator=(std::forward<Arg>(arg));
212  }
213  return *this;
214  }
215 
216 
221 #ifdef __cpp_concepts
222  template<typed_matrix Arg> requires square_shaped<Arg> and
223  compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>and
224  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, NestedSelfAdjoint>
225 #else
226  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and square_shaped<Arg> and
227  compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>and
228  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, NestedSelfAdjoint>, int> = 0>
229 #endif
230  auto& operator=(Arg&& other)
231  {
232  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
233  {
234  Base::operator=(oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<Arg>(other)));
235  }
236  return *this;
237  }
238 
239 
241 #ifdef __cpp_concepts
242  template<covariance_nestable Arg> requires std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, Arg&&>
243 #else
244  template<typename Arg, std::enable_if_t<(covariance_nestable<Arg>) and
245  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, Arg&&>, int> = 0>
246 #endif
247  auto& operator=(Arg&& other)
248  {
249  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
250  {
251  Base::operator=(std::forward<Arg>(other));
252  }
253  return *this;
254  }
255 
256 
261 #ifdef __cpp_concepts
262  template<typed_matrix_nestable Arg> requires (not covariance_nestable<Arg>) and square_shaped<Arg> and
263  std::assignable_from<std::add_lvalue_reference_t<NestedMatrix>, NestedSelfAdjoint>
264 #else
265  template<typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and (not covariance_nestable<Arg>) and
266  square_shaped<Arg> and std::is_assignable_v<std::add_lvalue_reference_t<NestedMatrix>, NestedSelfAdjoint>, int> = 0>
267 #endif
268  auto& operator=(Arg&& other)
269  {
270  if constexpr (not zero<NestedMatrix> and not identity_matrix<NestedMatrix>)
271  {
272  Base::operator=(oin::to_covariance_nestable<NestedSelfAdjoint>(std::forward<Arg>(other)));
273  }
274  return *this;
275  }
276 
277 
282 #ifdef __cpp_concepts
283  template<typename Arg> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>) and
284  (self_adjoint_covariance<Arg> or (typed_matrix<Arg> and square_shaped<Arg>)) and
285  coordinates::compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>
286 #else
287  template<typename Arg, std::enable_if_t<(not std::is_const_v<std::remove_reference_t<NestedMatrix>>) and
288  (self_adjoint_covariance<Arg> or (typed_matrix<Arg> and square_shaped<Arg>)) and
289  compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>, int> = 0>
290 #endif
291  auto& operator+=(Arg&& arg)
292  {
293  if constexpr(hermitian_matrix<NestedMatrix>)
294  {
295  nested_object() += oin::to_covariance_nestable<NestedMatrix>(std::forward<Arg>(arg));
296  mark_nested_matrix_changed();
297  }
298  else
299  {
300  if (synchronization_direction() >= 0)
301  {
302  if constexpr(triangular_matrix<NestedMatrix, TriangleType::upper>)
303  {
304 
306  nested_object(), oin::to_covariance_nestable<NestedMatrix>(std::forward<Arg>(arg))));
307  }
308  else
309  {
311  nested_object(), oin::to_covariance_nestable<NestedMatrix>(std::forward<Arg>(arg))));
312  }
313  }
314  if (synchronization_direction() <= 0)
315  {
316  cholesky_nested_matrix() += oin::to_covariance_nestable<NestedSelfAdjoint>(
317  std::forward<Arg>(arg));
318  }
319  }
320  return *this;
321  }
322 
323 
327 #ifdef __cpp_concepts
328  auto& operator+=(const Covariance& arg) requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
329 #else
330  template<typename T = NestedMatrix, std::enable_if_t<(not std::is_const_v<std::remove_reference_t<T>>), int> = 0>
331  auto& operator+=(const Covariance& arg)
332 #endif
333  {
334  return operator+=<const Covariance&>(arg);
335  }
336 
337 
338 #ifdef __cpp_concepts
339  template<typename Arg> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>) and
340  (self_adjoint_covariance<Arg> or (typed_matrix<Arg> and square_shaped<Arg>)) and
341  coordinates::compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>
342 #else
343  template<typename Arg, std::enable_if_t<(not std::is_const_v<std::remove_reference_t<NestedMatrix>>) and
344  (self_adjoint_covariance<Arg> or (typed_matrix<Arg> and square_shaped<Arg>)) and
345  compares_with<vector_space_descriptor_of_t<Arg, 0>, StaticDescriptor>, int> = 0>
346 #endif
347  auto& operator-=(const Arg& arg)
348  {
349  if constexpr(hermitian_matrix<NestedMatrix>)
350  {
351  nested_object() -= oin::to_covariance_nestable<NestedMatrix>(arg);
352  mark_nested_matrix_changed();
353  }
354  else
355  {
356  if (synchronization_direction() >= 0)
357  {
358  using TLowerType = typename MatrixTraits<std::decay_t<NestedMatrix>>::template TriangularAdapterFrom<TriangleType::lower>;
359  const auto U = oin::to_covariance_nestable<TLowerType>(arg);
360  OpenKalman::rank_update(nested_object(), U, Scalar(-1));
361  }
362  if (synchronization_direction() <= 0)
363  {
364  cholesky_nested_matrix() -= oin::to_covariance_nestable<NestedSelfAdjoint>(arg);
365  }
366  }
367  return *this;
368  }
369 
370 
374 #ifdef __cpp_concepts
375  auto& operator-=(const Covariance& arg) requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
376 #else
377  template<typename T = NestedMatrix, std::enable_if_t<(not std::is_const_v<std::remove_reference_t<T>>), int> = 0>
378  auto& operator-=(const Covariance& arg)
379 #endif
380  {
381  return operator-=<const Covariance&>(arg);
382  }
383 
384 
385 #ifdef __cpp_concepts
386  template<std::convertible_to<Scalar> S> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
387 #else
388  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar> and
389  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>), int> = 0>
390 #endif
391  auto& operator*=(const S s)
392  {
393  if constexpr(hermitian_matrix<NestedMatrix>)
394  {
395  nested_object() *= static_cast<const Scalar>(s);
396  mark_nested_matrix_changed();
397  }
398  else if (s > 0)
399  {
400  if (synchronization_direction() >= 0) nested_object() *= square_root(static_cast<const Scalar>(s));
401  if (synchronization_direction() <= 0) cholesky_nested_matrix() *= static_cast<const Scalar>(s);
402  }
403  else if (s < 0)
404  {
405  if (synchronization_direction() >= 0)
406  {
407  using TLowerType = typename MatrixTraits<std::decay_t<NestedMatrix>>::template TriangularAdapterFrom<TriangleType::lower>;
408  const auto U = oin::to_covariance_nestable<TLowerType>(*this);
410  OpenKalman::rank_update(nested_object(), U, s);
411  }
412  if (synchronization_direction() <= 0)
413  {
414  cholesky_nested_matrix() *= static_cast<const Scalar>(s);
415  }
416  }
417  else
418  {
420  if (synchronization_direction() <= 0)
421  {
422  cholesky_nested_matrix() = make_zero(cholesky_nested_matrix());
423  mark_synchronized();
424  }
425  }
426  return *this;
427  }
428 
429 
430 #ifdef __cpp_concepts
431  template<std::convertible_to<Scalar> S> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
432 #else
433  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar> and
434  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>), int> = 0>
435 #endif
436  auto& operator/=(const S s)
437  {
438  if constexpr(hermitian_matrix<NestedMatrix>)
439  {
440  nested_object() /= static_cast<const Scalar>(s);
441  mark_nested_matrix_changed();
442  }
443  else if (s > 0)
444  {
445  if (synchronization_direction() >= 0) nested_object() /= square_root(static_cast<const Scalar>(s));
446  if (synchronization_direction() <= 0) cholesky_nested_matrix() /= static_cast<const Scalar>(s);
447  }
448  else if (s < 0)
449  {
450  if (synchronization_direction() >= 0)
451  {
452  using TLowerType = typename MatrixTraits<std::decay_t<NestedMatrix>>::template TriangularAdapterFrom<TriangleType::lower>;
453  const auto u = oin::to_covariance_nestable<TLowerType>(*this);
455  OpenKalman::rank_update(nested_object(), u, 1 / static_cast<const Scalar>(s));
456  }
457  if (synchronization_direction() <= 0)
458  {
459  cholesky_nested_matrix() /= static_cast<const Scalar>(s);
460  }
461  }
462  else
463  {
464  throw std::runtime_error("Covariance operator/=: divide by zero");
465  }
466  return *this;
467  }
468 
469 
471 #ifdef __cpp_concepts
472  template<std::convertible_to<Scalar> S> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
473 #else
474  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar> and
475  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>), int> = 0>
476 #endif
477  auto& scale(const S s)
478  {
479  if constexpr(hermitian_matrix<NestedMatrix>)
480  {
481  nested_object() *= static_cast<const Scalar>(s) * s;
482  mark_nested_matrix_changed();
483  }
484  else
485  {
486  if (synchronization_direction() >= 0) nested_object() *= static_cast<const Scalar>(s);
487  if (synchronization_direction() <= 0) cholesky_nested_matrix() *= static_cast<const Scalar>(s) * s;
488  }
489  return *this;
490  }
491 
492 
494 #ifdef __cpp_concepts
495  template<std::convertible_to<Scalar> S> requires (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
496 #else
497  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar> and
498  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>), int> = 0>
499 #endif
500  auto& inverse_scale(const S s)
501  {
502  if constexpr(hermitian_matrix<NestedMatrix>)
503  {
504  nested_object() /= static_cast<const Scalar>(s) * s;
505  mark_nested_matrix_changed();
506  }
507  else
508  {
509  if (synchronization_direction() >= 0) nested_object() /= static_cast<const Scalar>(s);
510  if (synchronization_direction() <= 0) cholesky_nested_matrix() /= static_cast<const Scalar>(s) * s;
511  }
512  return *this;
513  }
514 
515 
516  // ------- //
517  // Other //
518  // ------- //
519 
526  auto square_root() &
527  {
528  if constexpr ((not diagonal_matrix<NestedMatrix>) or identity_matrix<NestedMatrix> or zero<NestedMatrix>)
529  {
531  }
532  else
533  {
534  static_assert(diagonal_matrix<NestedMatrix>);
535  auto n = cholesky_factor<storage_triangle>(nested_object());
537  }
538  }
539 
540 
542  auto square_root() const &
543  {
544  if constexpr ((not diagonal_matrix<NestedMatrix>) or identity_matrix<NestedMatrix> or zero<NestedMatrix>)
545  {
547  }
548  else
549  {
550  static_assert(diagonal_matrix<NestedMatrix>);
551  auto n = cholesky_factor<storage_triangle>(nested_object());
553  }
554  }
555 
556 
558  auto square_root() &&
559  {
560  if constexpr ((not diagonal_matrix<NestedMatrix>) or identity_matrix<NestedMatrix> or zero<NestedMatrix>)
561  {
563  }
564  else
565  {
566  static_assert(diagonal_matrix<NestedMatrix>);
567  auto n = cholesky_factor<storage_triangle>(std::move(*this).nested_object());
569  }
570  }
571 
572 
574  auto square_root() const &&
575  {
576  if constexpr ((not diagonal_matrix<NestedMatrix>) or identity_matrix<NestedMatrix> or zero<NestedMatrix>)
577  {
579  }
580  else
581  {
582  static_assert(diagonal_matrix<NestedMatrix>);
583  auto n = cholesky_factor<storage_triangle>(std::move(*this).nested_object());
585  }
586  }
587 
588 
592 #ifdef __cpp_concepts
593  template<typed_matrix U> requires compares_with<vector_space_descriptor_of_t<U, 0>, StaticDescriptor>and
594  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>)
595 #else
596  template<typename U, std::enable_if_t<typed_matrix<U> and
597  compares_with<vector_space_descriptor_of_t<U, 0>, StaticDescriptor>and
598  (not std::is_const_v<std::remove_reference_t<NestedMatrix>>), int> = 0>
599 #endif
600  auto& rank_update(const U& u, const Scalar alpha = 1) &
601  {
602  if (synchronization_direction() < 0) Base::synchronize_reverse();
603  if constexpr (one_dimensional<NestedMatrix>)
604  {
605  Base::operator()(0, 0) = trace(nested_object()) + alpha * trace(u * adjoint(u));
606  }
607  else
608  {
609  OpenKalman::rank_update(nested_object(), OpenKalman::nested_object(u), alpha);
610  }
611  mark_nested_matrix_changed();
612  return *this;
613  }
614 
615 
619 #ifdef __cpp_concepts
620  template<typed_matrix U> requires coordinates::compares_with<vector_space_descriptor_of_t<U, 0>, StaticDescriptor>
621 #else
622  template<typename U, std::enable_if_t<typed_matrix<U> and
623  compares_with<vector_space_descriptor_of_t<U, 0>, StaticDescriptor>, int> = 0>
624 #endif
625  auto rank_update(const U& u, const Scalar alpha = 1) &&
626  {
627  if (synchronization_direction() < 0) synchronize_reverse();
628  if constexpr (one_dimensional<NestedMatrix>)
629  {
630  std::tuple d_tup {Dimensions<1>{}, Dimensions<1>{}};
631  return make_dense_object_from<NestedMatrix>(d_tup, trace(nested_object()) + alpha * trace(u * adjoint(u)));
632  }
633  else
634  {
635  return make(OpenKalman::rank_update(nested_object(), OpenKalman::nested_object(u), alpha));
636  }
637  }
638 
639  private:
640 
641 #ifdef __cpp_concepts
642  template<typename, typename>
643 #else
644  template<typename, typename, typename>
645 #endif
646  friend struct oin::CovarianceBase;
647 
648 
649  template<typename, typename>
650  friend struct oin::CovarianceImpl;
651 
652 
653  template<typename, typename>
654  friend struct oin::CovarianceBase3Impl;
655 
656 
657 #ifdef __cpp_concepts
658  template<fixed_pattern C, covariance_nestable N> requires
659  (coordinates::dimension_of_v<C> == index_dimension_of_v<N, 0>) and (not std::is_rvalue_reference_v<N>)
660 #else
661  template<typename, typename>
662 #endif
663  friend struct Covariance;
664 
665 
666 #ifdef __cpp_concepts
667  template<fixed_pattern C, covariance_nestable N> requires
668  (coordinates::dimension_of_v<C> == index_dimension_of_v<N, 0>) and (not std::is_rvalue_reference_v<N>)
669 #else
670  template<typename, typename>
671 #endif
672  friend struct SquareRootCovariance;
673 
674  };
675 
676 
677  // ------------------------------- //
678  // Deduction guides //
679  // ------------------------------- //
680 
684 #ifdef __cpp_concepts
685  template<covariance_nestable M>
686 #else
687  template<typename M, std::enable_if_t<covariance_nestable<M>, int> = 0>
688 #endif
689  explicit Covariance(M&&) -> Covariance<Dimensions<index_dimension_of_v<M, 0>>, passable_t<M>>;
690 
691 
695 #ifdef __cpp_concepts
696  template<typed_matrix M> requires square_shaped<M>
697 #else
698  template<typename M, std::enable_if_t<typed_matrix<M> and square_shaped<M>, int> = 0>
699 #endif
700  explicit Covariance(M&&) -> Covariance<
702  typename MatrixTraits<std::decay_t<nested_object_of_t<M>>>::template SelfAdjointMatrixFrom<>>;
703 
704 
708 #ifdef __cpp_concepts
709  template<typed_matrix_nestable M> requires (not covariance_nestable<M>) and square_shaped<M>
710 #else
711  template<typename M, std::enable_if_t<
712  typed_matrix_nestable<M> and (not covariance_nestable<M>) and square_shaped<M>, int> = 0>
713 #endif
714  explicit Covariance(M&&) -> Covariance<
715  Dimensions<index_dimension_of_v<M, 0>>, typename MatrixTraits<std::decay_t<M>>::template SelfAdjointMatrixFrom<>>;
716 
717 
718  // ---------------- //
719  // Make Functions //
720  // ---------------- //
721 
727 #ifdef __cpp_concepts
728  template<fixed_pattern StaticDescriptor, covariance_nestable Arg> requires
729  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 0>)
730 #else
731  template<typename StaticDescriptor, typename Arg, std::enable_if_t<fixed_pattern<StaticDescriptor> and
732  covariance_nestable<Arg> and (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 0>::value), int> = 0>
733 #endif
734  inline auto
735  make_covariance(Arg&& arg)
736  {
737  return Covariance<StaticDescriptor, passable_t<Arg>> {std::forward<Arg>(arg)};
738  }
739 
740 
747 #ifdef __cpp_concepts
748  template<fixed_pattern StaticDescriptor, TriangleType triangle_type, covariance_nestable Arg> requires
749  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 0>) and
750  (triangle_type != TriangleType::lower or triangular_matrix<Arg, TriangleType::lower>) and
751  (triangle_type != TriangleType::upper or triangular_matrix<Arg, TriangleType::upper>) and
752  (triangle_type != TriangleType::diagonal or diagonal_matrix<Arg>)
753 #else
754  template<typename StaticDescriptor, TriangleType triangle_type, typename Arg, std::enable_if_t<
755  fixed_pattern<StaticDescriptor> and covariance_nestable<Arg> and
756  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 0>::value) and
757  (triangle_type != TriangleType::lower or triangular_matrix<Arg, TriangleType::lower>) and
758  (triangle_type != TriangleType::upper or triangular_matrix<Arg, TriangleType::upper>) and
759  (triangle_type != TriangleType::diagonal or diagonal_matrix<Arg>), int> = 0>
760 #endif
761  inline auto
762  make_covariance(Arg&& arg)
763  {
764  return Covariance<StaticDescriptor, passable_t<Arg>> {std::forward<Arg>(arg)};
765  }
766 
767 
774 #ifdef __cpp_concepts
775  template<covariance_nestable Arg>
776 #else
777  template<typename Arg, std::enable_if_t<covariance_nestable<Arg>, int> = 0>
778 #endif
779  inline auto
780  make_covariance(Arg&& arg)
781  {
782  using C = Dimensions<index_dimension_of_v<Arg, 0>>;
783  return make_covariance<C>(std::forward<Arg>(arg));
784  }
785 
786 
794 #ifdef __cpp_concepts
795  template<fixed_pattern StaticDescriptor, TriangleType triangle_type, typed_matrix_nestable Arg> requires
796  (not covariance_nestable<Arg>) and
797  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and
798  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 0>) and (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 1>)
799 #else
800  template<typename StaticDescriptor, TriangleType triangle_type, typename Arg, std::enable_if_t<
801  fixed_pattern<StaticDescriptor> and typed_matrix_nestable<Arg> and (not covariance_nestable<Arg>) and
802  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and
803  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 0>::value) and
804  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 1>::value), int> = 0>
805 #endif
806  inline auto
807  make_covariance(Arg&& arg)
808  {
809  using T = typename MatrixTraits<std::decay_t<Arg>>::template TriangularAdapterFrom<triangle_type>;
810  return Covariance<StaticDescriptor, T> {std::forward<Arg>(arg)};
811  }
812 
813 
820 #ifdef __cpp_concepts
821  template<fixed_pattern StaticDescriptor, typed_matrix_nestable Arg> requires (not covariance_nestable<Arg>) and
822  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 0>) and (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 1>)
823 #else
824  template<typename StaticDescriptor, typename Arg, std::enable_if_t<
825  fixed_pattern<StaticDescriptor> and typed_matrix_nestable<Arg> and (not covariance_nestable<Arg>) and
826  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 0>::value) and
827  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of<Arg, 1>::value), int> = 0>
828 #endif
829  inline auto
830  make_covariance(Arg&& arg)
831  {
832  using SA = typename MatrixTraits<std::decay_t<Arg>>::template SelfAdjointMatrixFrom<>;
833  return make_covariance<StaticDescriptor, SA>(oin::to_covariance_nestable<SA>(std::forward<Arg>(arg)));
834  }
835 
836 
843 #ifdef __cpp_concepts
844  template<TriangleType triangle_type, typed_matrix_nestable Arg> requires (not covariance_nestable<Arg>) and
845  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and square_shaped<Arg>
846 #else
847  template<TriangleType triangle_type, typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and
848  (not covariance_nestable<Arg>) and
849  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and square_shaped<Arg>, int> = 0>
850 #endif
851  inline auto
852  make_covariance(Arg&& arg)
853  {
854  using C = Dimensions<index_dimension_of_v<Arg, 0>>;
855  return make_covariance<C, triangle_type>(std::forward<Arg>(arg));
856  }
857 
858 
864 #ifdef __cpp_concepts
865  template<typed_matrix_nestable Arg> requires (not covariance_nestable<Arg>) and square_shaped<Arg>
866 #else
867  template<typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and (not covariance_nestable<Arg>) and
868  square_shaped<Arg>, int> = 0>
869 #endif
870  inline auto
871  make_covariance(Arg&& arg)
872  {
873  using C = Dimensions<index_dimension_of_v<Arg, 0>>;
874  return make_covariance<C>(std::forward<Arg>(arg));
875  }
876 
877 
882 #ifdef __cpp_concepts
883  template<fixed_pattern StaticDescriptor, typename Arg> requires
884  (covariance_nestable<Arg> or typed_matrix_nestable<Arg>) and square_shaped<Arg>
885 #else
886  template<typename StaticDescriptor, typename Arg, std::enable_if_t<
887  (covariance_nestable<Arg> or typed_matrix_nestable<Arg>) and square_shaped<Arg>, int> = 0>
888 #endif
889  inline auto
891  {
892  constexpr TriangleType triangle_type = triangle_type_of_v<typename MatrixTraits<std::decay_t<Arg>>::template TriangularAdapterFrom<>>;
893  using B = std::conditional_t<diagonal_matrix<Arg>,
894  typename MatrixTraits<std::decay_t<Arg>>::template DiagonalMatrixFrom<>,
895  std::conditional_t<triangular_matrix<Arg>,
896  typename MatrixTraits<std::decay_t<Arg>>::template TriangularAdapterFrom<triangle_type>,
897  typename MatrixTraits<std::decay_t<Arg>>::template SelfAdjointMatrixFrom<triangle_type>>>;
899  }
900 
901 
906 #ifdef __cpp_concepts
907  template<fixed_pattern StaticDescriptor, TriangleType triangle_type, typed_matrix_nestable Arg> requires
908  square_shaped<Arg>
909 #else
910  template<typename StaticDescriptor, TriangleType triangle_type, typename Arg,
911  std::enable_if_t<fixed_pattern<StaticDescriptor> and typed_matrix_nestable<Arg> and square_shaped<Arg>, int> = 0>
912 #endif
913  inline auto
915  {
916  using B = std::conditional_t<triangle_type == TriangleType::diagonal,
917  typename MatrixTraits<std::decay_t<Arg>>::template DiagonalMatrixFrom<>,
918  typename MatrixTraits<std::decay_t<Arg>>::template TriangularAdapterFrom<triangle_type>>;
920  }
921 
922 
928 #ifdef __cpp_concepts
929  template<typename Arg> requires (covariance_nestable<Arg> or typed_matrix_nestable<Arg>) and square_shaped<Arg>
930 #else
931  template<typename Arg, std::enable_if_t<(covariance_nestable<Arg> or typed_matrix_nestable<Arg>) and
932  square_shaped<Arg>, int> = 0>
933 #endif
934  inline auto
936  {
937  using C = Dimensions<index_dimension_of_v<Arg, 0>>;
938  return make_covariance<C, Arg>();
939  }
940 
941 
946 #ifdef __cpp_concepts
947  template<TriangleType triangle_type, typed_matrix_nestable Arg> requires square_shaped<Arg>
948 #else
949  template<TriangleType triangle_type, typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and
950  square_shaped<Arg>, int> = 0>
951 #endif
952  inline auto
954  {
955  using C = Dimensions<index_dimension_of_v<Arg, 0>>;
956  return make_covariance<C, triangle_type, Arg>();
957  }
958 
959 
964 #ifdef __cpp_concepts
965  template<self_adjoint_covariance Arg>
966 #else
967  template<typename Arg, std::enable_if_t<self_adjoint_covariance<Arg>, int> = 0>
968 #endif
969  inline auto
970  make_covariance(Arg&& arg)
971  {
973  return Covariance<C, nested_object_of_t<Arg>>(std::forward<Arg>(arg));
974  }
975 
976 
981 #ifdef __cpp_concepts
982  template<self_adjoint_covariance T>
983 #else
984  template<typename T, std::enable_if_t<self_adjoint_covariance<T>, int> = 0>
985 #endif
986  inline auto
988  {
990  using B = nested_object_of_t<T>;
991  return make_covariance<C, B>();
992  }
993 
994 
999 #ifdef __cpp_concepts
1000  template<typed_matrix Arg> requires square_shaped<Arg>
1001 #else
1002  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and square_shaped<Arg>, int> = 0>
1003 #endif
1004  inline auto
1005  make_covariance(Arg&& arg)
1006  {
1008  return make_covariance<C>(nested_object(std::forward<Arg>(arg)));
1009  }
1010 
1011 
1016 #ifdef __cpp_concepts
1017  template<TriangleType triangle_type, typed_matrix Arg> requires
1018  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and square_shaped<Arg>
1019 #else
1020  template<TriangleType triangle_type, typename Arg, std::enable_if_t<typed_matrix<Arg> and
1021  (triangle_type == TriangleType::lower or triangle_type == TriangleType::upper) and square_shaped<Arg>, int> = 0>
1022 #endif
1023  inline auto
1024  make_covariance(Arg&& arg)
1025  {
1027  return make_covariance<C, triangle_type>(nested_object(std::forward<Arg>(arg)));
1028  }
1029 
1030 
1035 #ifdef __cpp_concepts
1036  template<TriangleType triangle_type, typed_matrix Arg> requires square_shaped<Arg>
1037 #else
1038  template<TriangleType triangle_type, typename Arg, std::enable_if_t<typed_matrix<Arg> and
1039  square_shaped<Arg>, int> = 0>
1040 #endif
1041  inline auto
1042  make_covariance()
1043  {
1045  using B = nested_object_of_t<Arg>;
1046  return make_covariance<C, triangle_type, B>();
1047  }
1048 
1049 
1054 #ifdef __cpp_concepts
1055  template<typed_matrix Arg> requires square_shaped<Arg>
1056 #else
1057  template<typename Arg, std::enable_if_t<typed_matrix<Arg> and square_shaped<Arg>, int> = 0>
1058 #endif
1059  inline auto
1060  make_covariance()
1061  {
1063  using B = nested_object_of_t<Arg>;
1064  return make_covariance<C, B>();
1065  }
1066 
1067 
1068  // ------------------------- //
1069  // Interfaces //
1070  // ------------------------- //
1071 
1072  namespace interface
1073  {
1074  template<typename Coeffs, typename NestedMatrix>
1075  struct indexible_object_traits<Covariance<Coeffs, NestedMatrix>>
1076  {
1077  using scalar_type = scalar_type_of_t<NestedMatrix>;
1078 
1079  template<typename Arg>
1080  static constexpr auto count_indices(const Arg& arg) { return std::integral_constant<std::size_t, 2>{}; }
1081 
1082  template<typename Arg, typename N>
1083  static constexpr auto get_vector_space_descriptor(Arg&& arg, N)
1084  {
1085  return std::forward<Arg>(arg).my_dimension;
1086  }
1087 
1088 
1089  template<typename Arg>
1090  static decltype(auto) nested_object(Arg&& arg)
1091  {
1092  if constexpr (hermitian_matrix<NestedMatrix>)
1093  return std::forward<Arg>(arg).get_self_adjoint_nested_matrix();
1094  else
1095  return std::forward<Arg>(arg).get_triangular_nested_matrix();
1096  }
1097 
1098 
1099  template<typename Arg>
1100  static constexpr auto get_constant(const Arg& arg)
1101  {
1102  if constexpr (hermitian_matrix<NestedMatrix>)
1103  return constant_coefficient{arg.nestedExpression()};
1104  else
1105  return std::monostate {};
1106  }
1107 
1108 
1109  template<typename Arg>
1110  static constexpr auto get_constant_diagonal(const Arg& arg)
1111  {
1112  return constant_diagonal_coefficient {arg.nestedExpression()};
1113  }
1114 
1115 
1116  template<Applicability b>
1117  static constexpr bool one_dimensional = OpenKalman::one_dimensional<NestedMatrix, b>;
1118 
1119 
1120  template<Applicability b>
1121  static constexpr bool is_square = true;
1122 
1123 
1124  template<TriangleType t>
1125  static constexpr bool is_triangular = triangular_matrix<NestedMatrix, TriangleType::diagonal>;
1126 
1127 
1128  static constexpr bool is_triangular_adapter = false;
1129 
1130 
1131  static constexpr bool is_hermitian = true;
1132 
1133 
1134  #ifdef __cpp_lib_concepts
1135  template<typename Arg, typename...I> requires
1136  element_gettable<decltype(std::declval<Arg&&>().get_self_adjoint_nested_matrix()), sizeof...(I)>
1137  #else
1138  template<typename Arg, typename...I, std::enable_if_t<
1139  element_gettable<decltype(std::declval<Arg&&>().get_self_adjoint_nested_matrix()), sizeof...(I)>, int> = 0>
1140  #endif
1141  static constexpr auto get(Arg&& arg, I...i)
1142  {
1143  return std::forward<Arg>(arg)(i...);
1144  }
1145 
1146 
1147  #ifdef __cpp_lib_concepts
1148  template<typename Arg, typename...I> requires
1149  writable_by_component<decltype(std::declval<Arg&>().get_self_adjoint_nested_matrix()), sizeof...(I)>
1150  #else
1151  template<typename Arg, typename...I, std::enable_if_t<
1152  writable_by_component<decltype(std::declval<Arg&>().get_self_adjoint_nested_matrix()), sizeof...(I)>, int> = 0>
1153  #endif
1154  static constexpr void set(Arg& arg, const scalar_type_of_t<Arg>& s, I...i)
1155  {
1156  arg.set_component(s, i...);
1157  }
1158 
1159  static constexpr bool is_writable = library_interface<std::decay_t<NestedMatrix>>::is_writable;
1160 
1161 
1162 #ifdef __cpp_lib_concepts
1163  template<typename Arg> requires raw_data_defined_for<NestedMatrix>
1164 #else
1165  template<typename Arg, std::enable_if_t<raw_data_defined_for<NestedMatrix>, int> = 0>
1166 #endif
1167  static constexpr auto * const
1168  raw_data(Arg& arg) { return internal::raw_data(nested_object(arg)); }
1169 
1170 
1171  static constexpr Layout layout = layout_of_v<NestedMatrix>;
1172 
1173  };
1174 
1175  } // namespace interface
1176 
1177 
1178 } // OpenKalman
1179 
1180 #endif //OPENKALMAN_COVARIANCE_HPP
decltype(auto) constexpr concatenate_vertical(V &&v, Vs &&... vs)
Concatenate one or more typed matrices objects vertically.
Definition: typed-matrix-overloads.hpp:270
constexpr auto count_indices(const T &t)
Get the number of indices available to address the components of an indexible object.
Definition: count_indices.hpp:33
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:66
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:83
TriangleType
The type of a triangular matrix.
Definition: global-definitions.hpp:60
Definition: indexible_object_traits.hpp:36
Definition: CovarianceImpl.hpp:39
Covariance(Args ... args)
Construct from a row-major list of Scalar coefficients forming a self-adjoint matrix.
Definition: Covariance.hpp:188
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
Covariance(M &&m)
Construct from another non-square-root covariance.
Definition: Covariance.hpp:116
decltype(auto) constexpr QR_decomposition(A &&a)
Perform a QR decomposition of matrix A=Q[U,0], U is a upper-triangular matrix, and Q is orthogonal...
Definition: QR_decomposition.hpp:33
constexpr bool number
T is a numerical type.
Definition: number.hpp:33
decltype(auto) constexpr concatenate_horizontal(V &&v, Vs &&... vs)
Concatenate one or more matrix objects vertically.
Definition: typed-matrix-overloads.hpp:308
An upper-right triangular matrix.
auto operator()(std::size_t i, std::size_t j)
Get or set element (i, j) of the covariance matrix.
Definition: CovarianceImpl.hpp:354
Definition: CovarianceBase3Impl.hpp:35
The constant associated with T, assuming T is a constant_matrix.
Definition: constant_coefficient.hpp:36
The upper or lower triangle Cholesky factor (square root) of a covariance matrix. ...
Definition: forward-class-declarations.hpp:559
The root namespace for OpenKalman.
Definition: basics.hpp:34
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:37
The constant associated with T, assuming T is a constant_diagonal_matrix.
Definition: constant_diagonal_coefficient.hpp:32
scalar_type_of_t< NestedMatrix > Scalar
Scalar type for this matrix.
Definition: Covariance.hpp:41
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
Layout
The layout format of a multidimensional array.
Definition: global-definitions.hpp:47
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
A self-adjoint Covariance matrix.
Definition: Covariance.hpp:30
A diagonal matrix (both a lower-left and an upper-right triangular matrix).
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
scalar_type_of_t< NestedMatrix > Scalar
Scalar type for this matrix.
Definition: CovarianceImpl.hpp:47
Covariance()
Default constructor.
Definition: Covariance.hpp:101
constexpr auto make_zero(Descriptors &&descriptors)
Make a zero associated with a particular library.
Definition: make_zero.hpp:36
Covariance(M &&m)
Construct from a covariance_nestable.
Definition: Covariance.hpp:127
auto inverse_scale(M &&m, const S s)
Scale a covariance by the inverse of a scalar factor.
Definition: covariance-arithmetic.hpp:543
auto scale(M &&m, const S s)
Scale a covariance by a factor.
Definition: covariance-arithmetic.hpp:518
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:34
decltype(auto) constexpr trace(Arg &&arg)
Take the trace of a matrix.
Definition: trace.hpp:35
A lower-left triangular matrix.
Definition: basics.hpp:48
constexpr bool hermitian_matrix
Specifies that a type is a hermitian matrix (assuming it is square_shaped).
Definition: hermitian_matrix.hpp:50
constexpr auto get_vector_space_descriptor(const T &t, const N &n)
Get the coordinates::pattern object for index N of indexible object T.
Definition: get_vector_space_descriptor.hpp:56