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>);
38  static_assert(values::number<scalar_type_of_t<NestedMatrix>>);
39 #endif
40 
41  using Scalar = scalar_type_of_t<NestedMatrix>;
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 triangle_type 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<bool Enable = true, std::enable_if_t<Enable and stdex::default_initializable<Base>, 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 stdex::constructible_from<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 stdex::constructible_from<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  stdex::constructible_from<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  stdex::constructible_from<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<(stdex::convertible_to<Args, const Scalar> and ...) and
185  ((diagonal_matrix<NestedMatrix> and sizeof...(Args) == dim) or
186  (sizeof...(Args) == dim * dim)) and stdex::constructible_from<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, triangle_type::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<triangle_type::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<stdex::convertible_to<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<triangle_type::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<stdex::convertible_to<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<triangle_type::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<stdex::convertible_to<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<stdex::convertible_to<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<
701  vector_space_descriptor_of_t<M, 0>,
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, triangle_type tri, covariance_nestable Arg> requires
749  (coordinates::dimension_of_v<StaticDescriptor> == index_dimension_of_v<Arg, 0>) and
750  (tri != triangle_type::lower or triangular_matrix<Arg, triangle_type::lower>) and
751  (tri != triangle_type::upper or triangular_matrix<Arg, triangle_type::upper>) and
752  (tri != triangle_type::diagonal or diagonal_matrix<Arg>)
753 #else
754  template<typename StaticDescriptor, triangle_type tri, 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  (tri != triangle_type::lower or triangular_matrix<Arg, triangle_type::lower>) and
758  (tri != triangle_type::upper or triangular_matrix<Arg, triangle_type::upper>) and
759  (tri != triangle_type::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, triangle_type tri, typed_matrix_nestable Arg> requires
796  (not covariance_nestable<Arg>) and
797  (tri == triangle_type::lower or tri == triangle_type::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, triangle_type tri, typename Arg, std::enable_if_t<
801  fixed_pattern<StaticDescriptor> and typed_matrix_nestable<Arg> and (not covariance_nestable<Arg>) and
802  (tri == triangle_type::lower or tri == triangle_type::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<tri>;
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<triangle_type tri, typed_matrix_nestable Arg> requires (not covariance_nestable<Arg>) and
845  (tri == triangle_type::lower or tri == triangle_type::upper) and square_shaped<Arg>
846 #else
847  template<triangle_type tri, typename Arg, std::enable_if_t<typed_matrix_nestable<Arg> and
848  (not covariance_nestable<Arg>) and
849  (tri == triangle_type::lower or tri == triangle_type::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, tri>(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 triangle_type tri = 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<tri>,
897  typename MatrixTraits<std::decay_t<Arg>>::template SelfAdjointMatrixFrom<tri>>>;
899  }
900 
901 
906 #ifdef __cpp_concepts
907  template<fixed_pattern StaticDescriptor, triangle_type tri, typed_matrix_nestable Arg> requires
908  square_shaped<Arg>
909 #else
910  template<typename StaticDescriptor, triangle_type tri, 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<tri == triangle_type::diagonal,
917  typename MatrixTraits<std::decay_t<Arg>>::template DiagonalMatrixFrom<>,
918  typename MatrixTraits<std::decay_t<Arg>>::template TriangularAdapterFrom<tri>>;
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<triangle_type tri, typed_matrix_nestable Arg> requires square_shaped<Arg>
948 #else
949  template<triangle_type tri, 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, tri, 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  {
972  using C = vector_space_descriptor_of_t<Arg, 0>;
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  {
989  using C = vector_space_descriptor_of_t<T, 0>;
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  {
1007  using C = vector_space_descriptor_of_t<Arg, 0>;
1008  return make_covariance<C>(nested_object(std::forward<Arg>(arg)));
1009  }
1010 
1011 
1016 #ifdef __cpp_concepts
1017  template<triangle_type tri, typed_matrix Arg> requires
1018  (tri == triangle_type::lower or tri == triangle_type::upper) and square_shaped<Arg>
1019 #else
1020  template<triangle_type tri, typename Arg, std::enable_if_t<typed_matrix<Arg> and
1021  (tri == triangle_type::lower or tri == triangle_type::upper) and square_shaped<Arg>, int> = 0>
1022 #endif
1023  inline auto
1024  make_covariance(Arg&& arg)
1025  {
1026  using C = vector_space_descriptor_of_t<Arg, 0>;
1027  return make_covariance<C, tri>(nested_object(std::forward<Arg>(arg)));
1028  }
1029 
1030 
1035 #ifdef __cpp_concepts
1036  template<triangle_type tri, typed_matrix Arg> requires square_shaped<Arg>
1037 #else
1038  template<triangle_type tri, 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  {
1044  using C = vector_space_descriptor_of_t<Arg, 0>;
1045  using B = nested_object_of_t<Arg>;
1046  return make_covariance<C, tri, 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  {
1062  using C = vector_space_descriptor_of_t<Arg, 0>;
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 object_traits<Covariance<Coeffs, NestedMatrix>>
1076  {
1077  static const bool is_specialized = true;
1078 
1079  using scalar_type = scalar_type_of_t<NestedMatrix>;
1080 
1081  template<typename Arg>
1082  static constexpr auto count_indices(const Arg& arg) { return std::integral_constant<std::size_t, 2>{}; }
1083 
1084  template<typename Arg, typename N>
1085  static constexpr auto get_pattern_collection(Arg&& arg, N)
1086  {
1087  return std::forward<Arg>(arg).my_dimension;
1088  }
1089 
1090 
1091  template<typename Arg>
1092  static decltype(auto) nested_object(Arg&& arg)
1093  {
1094  if constexpr (hermitian_matrix<NestedMatrix>)
1095  return std::forward<Arg>(arg).get_self_adjoint_nested_matrix();
1096  else
1097  return std::forward<Arg>(arg).get_triangular_nested_matrix();
1098  }
1099 
1100 
1101  template<typename Arg>
1102  static constexpr auto get_constant(const Arg& arg)
1103  {
1104  if constexpr (hermitian_matrix<NestedMatrix>)
1105  return constant_value{arg.nestedExpression()};
1106  else
1107  return std::monostate {};
1108  }
1109 
1110 
1111  template<typename Arg>
1112  static constexpr auto get_constant_diagonal(const Arg& arg)
1113  {
1114  return constant_diagonal_value {arg.nestedExpression()};
1115  }
1116 
1117 
1118  template<applicability b>
1119  static constexpr bool one_dimensional = OpenKalman::one_dimensional<NestedMatrix, b>;
1120 
1121 
1122  template<applicability b>
1123  static constexpr bool is_square = true;
1124 
1125 
1126  template<triangle_type t>
1127  static constexpr bool triangle_type_value = triangular_matrix<NestedMatrix, triangle_type::diagonal>;
1128 
1129 
1130  static constexpr bool is_triangular_adapter = false;
1131 
1132 
1133  static constexpr bool is_hermitian = true;
1134 
1135 
1136  #ifdef __cpp_lib_concepts
1137  template<typename Arg, typename...I> requires
1138  element_gettable<decltype(std::declval<Arg&&>().get_self_adjoint_nested_matrix()), sizeof...(I)>
1139  #else
1140  template<typename Arg, typename...I, std::enable_if_t<
1141  element_gettable<decltype(std::declval<Arg&&>().get_self_adjoint_nested_matrix()), sizeof...(I)>, int> = 0>
1142  #endif
1143  static constexpr auto get(Arg&& arg, I...i)
1144  {
1145  return std::forward<Arg>(arg)(i...);
1146  }
1147 
1148 
1149  #ifdef __cpp_lib_concepts
1150  template<typename Arg, typename...I> requires
1151  writable_by_component<decltype(std::declval<Arg&>().get_self_adjoint_nested_matrix()), sizeof...(I)>
1152  #else
1153  template<typename Arg, typename...I, std::enable_if_t<
1154  writable_by_component<decltype(std::declval<Arg&>().get_self_adjoint_nested_matrix()), sizeof...(I)>, int> = 0>
1155  #endif
1156  static constexpr void set(Arg& arg, const scalar_type_of_t<Arg>& s, I...i)
1157  {
1158  arg.set_component(s, i...);
1159  }
1160 
1161  static constexpr bool is_writable = library_interface<std::decay_t<NestedMatrix>>::is_writable;
1162 
1163 
1164 #ifdef __cpp_lib_concepts
1165  template<typename Arg> requires raw_data_defined_for<NestedMatrix>
1166 #else
1167  template<typename Arg, std::enable_if_t<raw_data_defined_for<NestedMatrix>, int> = 0>
1168 #endif
1169  static constexpr auto * const
1170  raw_data(Arg& arg) { return internal::raw_data(nested_object(arg)); }
1171 
1172 
1173  static constexpr data_layout layout = layout_of_v<NestedMatrix>;
1174 
1175  };
1176 
1177  }
1178 
1179 
1180 } // OpenKalman
1181 
1182 #endif
decltype(auto) constexpr concatenate_vertical(V &&v, Vs &&... vs)
Concatenate one or more typed matrices objects vertically.
Definition: typed-matrix-overloads.hpp:270
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
triangle_type
The type of a triangular matrix.
Definition: enumerations.hpp:26
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
A lower-left triangular matrix.
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
Covariance(M &&m)
Construct from another non-square-root covariance.
Definition: Covariance.hpp:116
decltype(auto) constexpr get_pattern_collection(T &&t)
Get the coordinates::pattern_collection associated with indexible object T.
Definition: get_pattern_collection.hpp:59
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 field type.
Definition: number.hpp:41
decltype(auto) constexpr concatenate_horizontal(V &&v, Vs &&... vs)
Concatenate one or more matrix objects vertically.
Definition: typed-matrix-overloads.hpp:308
A diagonal matrix (both a lower-left and 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
constexpr bool hermitian_matrix
Specifies that a type is a hermitian matrix.
Definition: hermitian_matrix.hpp:59
The upper or lower triangle Cholesky factor (square root) of a covariance matrix. ...
Definition: forward-class-declarations.hpp:403
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:42
Definition: object_traits.hpp:38
scalar_type_of_t< NestedMatrix > Scalar
Scalar type for this matrix.
Definition: Covariance.hpp:41
constexpr auto make_zero(stdex::extents< IndexType, Extents... > extents)
Make an indexible object in which every element is 0.
Definition: make_zero.hpp:35
An upper-right triangular matrix.
constexpr auto constant_value(T &&t)
The constant value associated with a constant_object or constant_diagonal_object. ...
Definition: constant_value.hpp:37
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
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:35
scalar_type_of_t< NestedMatrix > Scalar
Scalar type for this matrix.
Definition: CovarianceImpl.hpp:47
Covariance()
Default constructor.
Definition: Covariance.hpp:101
Covariance(M &&m)
Construct from a covariance_nestable.
Definition: Covariance.hpp:127
decltype(auto) constexpr adjoint(Arg &&arg)
Take the conjugate-transpose of an indexible_object.
Definition: adjoint.hpp:35
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:35
decltype(auto) constexpr trace(Arg &&arg)
Take the trace of a matrix.
Definition: trace.hpp:35
Definition: basics.hpp:48