OpenKalman
ToEuclideanExpr.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2018-2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_TOEUCLIDEANEXPR_HPP
17 #define OPENKALMAN_TOEUCLIDEANEXPR_HPP
18 
19 #include "linear-algebra/coordinates/coordinates.hpp"
24 #include "linear-algebra/adapters/internal/AdapterBase.hpp"
25 
26 namespace OpenKalman
27 {
28 
30 #ifdef __cpp_concepts
31  template<indexible NestedObject>
32 #else
33  template<typename NestedObject>
34 #endif
35  struct ToEuclideanExpr : internal::AdapterBase<ToEuclideanExpr<NestedObject>, NestedObject>
36  {
37 
38  private:
39 
40 #ifndef __cpp_concepts
41  static_assert(indexible<NestedObject>);
42 #endif
43 
44  using Scalar = scalar_type_of_t<NestedObject>;
45 
46  using Base = internal::AdapterBase<ToEuclideanExpr, NestedObject>;
47 
48  public:
49 
53 #ifdef __cpp_concepts
54  constexpr ToEuclideanExpr() requires std::default_initializable<Base>
55 #else
56  template<typename B = Base, std::enable_if_t<std::is_default_constructible_v<B>, int> = 0>
57  constexpr ToEuclideanExpr()
58 #endif
59  {}
60 
61 
65 #ifdef __cpp_concepts
66  template<indexible Arg> requires std::constructible_from<NestedObject, Arg&&>
67 #else
68  template<typename Arg, std::enable_if_t<indexible<Arg> and std::is_constructible_v<NestedObject, Arg&&>, int> = 0>
69 #endif
70  explicit ToEuclideanExpr(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
71 
72 
76 #ifdef __cpp_concepts
77  template<indexible Arg> requires (not std::is_base_of_v<ToEuclideanExpr, std::decay_t<Arg>>) and
78  std::assignable_from<std::add_lvalue_reference_t<NestedObject>,
79  decltype(from_euclidean(std::declval<Arg>(), get_vector_space_descriptor<0>(std::declval<NestedObject>())))>
80 #else
81  template<typename Arg, std::enable_if_t<indexible<Arg> and (not std::is_base_of_v<ToEuclideanExpr, std::decay_t<Arg>>) and
82  std::is_assignable_v<std::add_lvalue_reference_t<NestedObject>,
83  decltype(from_euclidean(std::declval<Arg>(), get_vector_space_descriptor<0>(std::declval<NestedObject>())))>, int> = 0>
84 #endif
85  auto& operator=(Arg&& arg)
86  {
87  using FArg = decltype(from_euclidean(std::declval<Arg>(), get_vector_space_descriptor<0>(std::declval<NestedObject>())));
88  if constexpr ((zero<NestedObject> and zero<FArg>) or (identity_matrix<NestedObject> and identity_matrix<FArg>))
89  {}
90  else
91  {
92  this->nested_object() = from_euclidean(std::forward<Arg>(arg), get_vector_space_descriptor<0>(nested_object(arg)));
93  }
94  return *this;
95  }
96 
97  };
98 
99 
103 #ifdef __cpp_concepts
104 template<indexible Arg>
105 #else
106  template<typename Arg, std::enable_if_t<indexible<Arg>, int> = 0>
107 #endif
109 
110 
111  namespace interface
112  {
113  template<typename NestedObject>
115  {
116  using scalar_type = scalar_type_of_t<NestedObject>;
117 
118  template<typename Arg>
119  static constexpr auto count_indices(const Arg& arg) { return OpenKalman::count_indices(nested_object(arg)); }
120 
121 
122  template<typename Arg, typename N>
123  static constexpr auto
124  get_vector_space_descriptor(Arg&& arg, const N& n)
125  {
126  if constexpr (values::fixed<N>)
127  {
128  if constexpr (n == 0_uz) return Axis;
129  else return OpenKalman::get_vector_space_descriptor(nested_object(std::forward<Arg>(arg)), n);
130  }
131  else
132  {
133  using Desc = coordinates::DynamicDescriptor<scalar_type_of<Arg>>;
134  if (n == 0) return Desc {coordinates::Axis};
135  else return Desc {OpenKalman::get_vector_space_descriptor(nested_object(std::forward<Arg>(arg)), n)};
136  }
137  }
138 
139 
140  template<typename Arg>
141  static decltype(auto)
142  nested_object(Arg&& arg)
143  {
144  return std::forward<Arg>(arg).nested_object();
145  }
146 
147 
148  template<typename Arg>
149  static constexpr auto
150  get_constant(const Arg& arg)
151  {
152  if constexpr (has_untyped_index<NestedObject, 0>)
153  return constant_coefficient {arg.nested_object()};
154  else
155  return std::monostate {};
156  }
157 
158 
159  template<typename Arg>
160  static constexpr auto
161  get_constant_diagonal(const Arg& arg)
162  {
163  if constexpr (has_untyped_index<NestedObject, 0>)
164  return constant_diagonal_coefficient {arg.nested_object()};
165  else
166  return std::monostate {};
167  }
168 
169 
170  template<Applicability b>
171  static constexpr bool
172  one_dimensional = has_untyped_index<NestedObject, 0> and OpenKalman::one_dimensional<NestedObject, b>;
173 
174 
175  template<Applicability b>
176  static constexpr bool
177  is_square = has_untyped_index<NestedObject, 0> and square_shaped<NestedObject, b>;
178 
179 
180  template<TriangleType t>
181  static constexpr bool
182  is_triangular = has_untyped_index<NestedObject, 0> and triangular_matrix<NestedObject, t>;
183 
184 
185  static constexpr bool
186  is_triangular_adapter = false;
187 
188 
189  static constexpr bool
190  is_hermitian = has_untyped_index<NestedObject, 0> and hermitian_matrix<NestedObject>;
191 
192 
193  // hermitian_adapter_type is omitted
194 
195 
196  static constexpr bool is_writable = false;
197 
198 
199 #ifdef __cpp_lib_concepts
200  template<typename Arg> requires has_untyped_index<NestedObject, 0> and raw_data_defined_for<nested_object_of_t<Arg&>>
201 #else
202  template<typename Arg, std::enable_if_t<has_untyped_index<NestedObject, 0> and raw_data_defined_for<typename nested_object_of<Arg&>::type>, int> = 0>
203 #endif
204  static constexpr auto * const
205  raw_data(Arg& arg)
206  {
207  return internal::raw_data(OpenKalman::nested_object(arg));
208  }
209 
210 
211  static constexpr Layout
212  layout = has_untyped_index<NestedObject, 0> ? layout_of_v<NestedObject> : Layout::none;
213 
214 
215 #ifdef __cpp_concepts
216  template<typename Arg> requires (layout != Layout::none)
217 #else
218  template<Layout l = layout, typename Arg, std::enable_if_t<l != Layout::none, int> = 0>
219 #endif
220  static auto
221  strides(Arg&& arg)
222  {
223  return OpenKalman::internal::strides(OpenKalman::nested_object(std::forward<Arg>(arg)));
224  }
225 
226  };
227 
228 
229  template<typename NestedObject>
230  struct library_interface<ToEuclideanExpr<NestedObject>>
231  {
232  private:
233 
235 
236  public:
237 
238  template<typename Derived>
239  using LibraryBase = internal::library_base_t<Derived, pattern_matrix_of_t<T>>;
240 
241 
242 #ifdef __cpp_lib_ranges
243  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
244  static constexpr values::scalar decltype(auto)
245 #else
246  template<typename Arg, typename Indices>
247  static constexpr decltype(auto)
248 #endif
249  get_component(Arg&& arg, const Indices& indices)
250  {
251  if constexpr (has_untyped_index<NestedObject, 0>)
252  {
253  return NestedInterface::get_component(nested_object(std::forward<Arg>(arg)), indices);
254  }
255  else
256  {
257  auto g {[&arg, is...](std::size_t ix) { return get_component(nested_object(std::forward<Arg>(arg)), ix, is...); }};
258  return coordinates::to_stat_space(get_vector_space_descriptor<0>(arg), g, i);
259  }
260  }
261 
262 
263 #ifdef __cpp_lib_ranges
264  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
265 #else
266  template<typename Arg, typename Indices>
267 #endif
268  static void
269  set_component(Arg& arg, const scalar_type_of_t<Arg>& s, const Indices& indices)
270  {
271  if constexpr (has_untyped_index<NestedObject, 0>)
272  {
274  }
275  else
276  {
277  set_component(nested_object(arg), s, indices);
278  }
279  }
280 
281 
282  template<typename Arg>
283  static decltype(auto) to_native_matrix(Arg&& arg)
284  {
285  return OpenKalman::to_native_matrix<nested_object_of_t<Arg>>(std::forward<Arg>(arg));
286  }
287 
288 
289  template<Layout layout, typename Scalar, typename D>
290  static auto make_default(D&& d)
291  {
292  return make_dense_object<NestedObject, layout, Scalar>(std::forward<D>(d));
293  }
294 
295 
296  // fill_components not necessary because T is not a dense writable matrix.
297 
298 
299  template<typename C, typename D>
300  static constexpr auto make_constant(C&& c, D&& d)
301  {
302  return make_constant<NestedObject>(std::forward<C>(c), std::forward<D>(d));
303  }
304 
305 
306  template<typename Scalar, typename D>
307  static constexpr auto make_identity_matrix(D&& d)
308  {
309  return make_identity_matrix_like<NestedObject, Scalar>(std::forward<D>(d));
310  }
311 
312 
313  // get_slice
314 
315 
316  // set_slice
317 
318 
319  template<typename Arg>
320  static auto
321  to_diagonal(Arg&& arg)
322  {
323  if constexpr( has_untyped_index<NestedObject, 0>)
324  {
325  return to_diagonal(nested_object(std::forward<Arg>(arg)));
326  }
327  else
328  {
329  using P = pattern_matrix_of_t<T>;
330  return library_interface<P>::to_diagonal(to_native_matrix<P>(std::forward<Arg>(arg)));
331  }
332  }
333 
334 
335  template<typename Arg>
336  static auto
337  diagonal_of(Arg&& arg)
338  {
339  if constexpr(has_untyped_index<NestedObject, 0>)
340  {
341  return diagonal_of(nested_object(std::forward<Arg>(arg)));
342  }
343  else
344  {
345  using P = pattern_matrix_of_t<T>;
346  return library_interface<P>::diagonal_of(to_native_matrix<P>(std::forward<Arg>(arg)));
347  }
348  }
349 
350 
351  template<typename Arg, typename...Factors>
352  static auto
353  broadcast(Arg&& arg, const Factors&...factors)
354  {
355  return library_interface<std::decay_t<nested_object_of_t<Arg>>>::broadcast(std::forward<Arg>(arg), factors...);
356  }
357 
358 
359  template<typename...Ds, typename Operation, typename...Args>
360  static constexpr decltype(auto)
361  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& op, Args&&...args)
362  {
363  using P = pattern_matrix_of_t<T>;
364  return library_interface<P>::template n_ary_operation(tup, std::forward<Operation>(op), std::forward<Args>(args)...);
365  }
366 
367 
368  template<std::size_t...indices, typename BinaryFunction, typename Arg>
369  static constexpr decltype(auto)
370  reduce(BinaryFunction&& b, Arg&& arg)
371  {
372  using P = pattern_matrix_of_t<T>;
373  return library_interface<P>::template reduce<indices...>(std::forward<BinaryFunction>(b), std::forward<Arg>(arg));
374  }
375 
376 
377  // to_euclidean not included
378 
379  // from_duclidean not included
380 
381  // wrap_angles not included
382 
383 
384  template<typename Arg>
385  static constexpr decltype(auto)
386  conjugate(Arg&& arg)
387  {
388  if constexpr(has_untyped_index<NestedObject, 0>)
389  {
390  return OpenKalman::conjugate(nested_object(std::forward<Arg>(arg)));
391  }
392  else
393  {
394  return std::forward<Arg>(arg).conjugate(); //< \todo Generalize this.
395  }
396  }
397 
398 
399  template<typename Arg>
400  static constexpr decltype(auto)
401  transpose(Arg&& arg)
402  {
403  if constexpr(has_untyped_index<NestedObject, 0>)
404  {
405  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
406  }
407  else
408  {
409  return std::forward<Arg>(arg).transpose(); //< \todo Generalize this.
410  }
411  }
412 
413 
414  template<typename Arg>
415  static constexpr decltype(auto)
416  adjoint(Arg&& arg)
417  {
418  if constexpr(has_untyped_index<NestedObject, 0>)
419  {
420  return OpenKalman::adjoint(nested_object(std::forward<Arg>(arg)));
421  }
422  else
423  {
424  return std::forward<Arg>(arg).adjoint(); //< \todo Generalize this.
425  }
426  }
427 
428 
429  template<typename Arg>
430  static constexpr auto
431  determinant(Arg&& arg)
432  {
433  if constexpr(has_untyped_index<NestedObject, 0>)
434  {
435  return OpenKalman::determinant(nested_object(std::forward<Arg>(arg)));
436  }
437  else
438  {
439  return arg.determinant(); //< \todo Generalize this.
440  }
441  }
442 
443 
444  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
445  static decltype(auto)
446  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
447  {
448  return OpenKalman::rank_update_hermitian<significant_triangle>(make_hermitian_matrix(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
449  }
450 
451 
452  template<TriangleType triangle, typename A, typename U, typename Alpha>
453  static decltype(auto) rank_update_triangular(A&& a, U&& u, const Alpha alpha)
454  {
455  return OpenKalman::rank_update_triangular(make_triangular_matrix<triangle>(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
456  }
457 
458 
459  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
460  static constexpr decltype(auto)
461  solve(A&& a, B&& b)
462  {
463  return OpenKalman::solve<must_be_unique, must_be_exact>(
464  to_native_matrix<T>(std::forward<A>(a)), std::forward<B>(b));
465  }
466 
467 
468  template<typename A>
469  static inline auto
470  LQ_decomposition(A&& a)
471  {
472  return LQ_decomposition(to_dense_object(std::forward<A>(a)));
473  }
474 
475 
476  template<typename A>
477  static inline auto
478  QR_decomposition(A&& a)
479  {
480  return QR_decomposition(to_dense_object(std::forward<A>(a)));
481  }
482 
483  };
484 
485 
486  } // namespace interface
487 
488 
489 } // OpenKalman
490 
491 
492 
493 #endif //OPENKALMAN_TOEUCLIDEANEXPR_HPP
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
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
constexpr auto n_ary_operation(const std::tuple< Ds... > &d_tup, Operation &&operation, Args &&...args)
Perform a component-wise n-ary operation, using broadcasting to match the size of a pattern matrix...
Definition: n_ary_operation.hpp:319
constexpr NestedObject & nested_object() &
Get the nested object.
Definition: AdapterBase.hpp:97
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:83
Definition: indexible_object_traits.hpp:36
typename pattern_matrix_of< std::decay_t< T > >::type pattern_matrix_of_t
Helper template for pattern_matrix_of.
Definition: forward-class-declarations.hpp:130
decltype(auto) rank_update_hermitian(A &&a, U &&u, scalar_type_of_t< A > alpha=1)
Do a rank update on a hermitian matrix.
Definition: rank_update_hermitian.hpp:45
decltype(auto) constexpr make_hermitian_matrix(Arg &&arg)
Creates a hermitian_matrix by, if necessary, wrapping the argument in a hermitian_adapter.
Definition: make_hermitian_matrix.hpp:37
No storage layout (e.g., if the elements are calculated rather than stored).
Arg && set_component(Arg &&arg, const scalar_type_of_t< Arg > &s, const Indices &indices)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: set_component.hpp:51
decltype(auto) constexpr conjugate(Arg &&arg)
Take the conjugate of a matrix.
Definition: conjugate.hpp:33
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
Linear algebra traits.
decltype(auto) to_native_matrix(Arg &&arg)
If it isn&#39;t already, convert Arg to a native object in the library associated with LibraryObject...
Definition: to_native_matrix.hpp:35
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
Definition: tuple_reverse.hpp:103
Definition of count_indices function.
decltype(auto) constexpr to_diagonal(Arg &&arg)
Convert an indexible object into a diagonal matrix.
Definition: to_diagonal.hpp:32
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 reduce(BinaryFunction &&b, Arg &&arg)
Perform a partial reduction based on an associative binary function, across one or more indices...
Definition: reduce.hpp:143
Definition: AdapterBase.hpp:36
decltype(auto) constexpr broadcast(Arg &&arg, const Factors &...factors)
Broadcast an object by replicating it by factors specified for each index.
Definition: broadcast.hpp:49
The constant associated with T, assuming T is a constant_matrix.
Definition: constant_coefficient.hpp:36
decltype(auto) constexpr transpose(Arg &&arg)
Take the transpose of a matrix.
Definition: transpose.hpp:58
The root namespace for OpenKalman.
Definition: basics.hpp:34
ToEuclideanExpr(Arg &&) -> ToEuclideanExpr< Arg >
Deduction guide.
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:37
ToEuclideanExpr(Arg &&arg)
Construct from compatible indexible object.
Definition: ToEuclideanExpr.hpp:70
The constant associated with T, assuming T is a constant_diagonal_matrix.
Definition: constant_diagonal_coefficient.hpp:32
An expression that transforms vector space descriptors into Euclidean space for application of direct...
Definition: forward-class-declarations.hpp:384
constexpr auto solve(A &&a, B &&b)
Solve the equation AX = B for X, which may or may not be a unique solution.
Definition: solve.hpp:87
constexpr auto determinant(Arg &&arg)
Take the determinant of a matrix.
Definition: determinant.hpp:44
decltype(auto) constexpr diagonal_of(Arg &&arg)
Extract a column vector (or column slice for rank>2 tensors) comprising the diagonal elements...
Definition: diagonal_of.hpp:33
constexpr ToEuclideanExpr()
Default constructor.
Definition: ToEuclideanExpr.hpp:57
decltype(auto) constexpr adjoint(Arg &&arg)
Take the adjoint of a matrix.
Definition: adjoint.hpp:33
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
decltype(auto) rank_update_triangular(A &&a, U &&u, scalar_type_of_t< A > alpha=1)
Do a rank update on triangular matrix.
Definition: rank_update_triangular.hpp:48
A structure representing the dimensions associated with of a particular index.
Definition: Dimensions.hpp:42
constexpr auto make_constant(C &&c, Descriptors &&descriptors)
Make a constant object based on a particular library object.
Definition: make_constant.hpp:37
decltype(auto) constexpr get_component(Arg &&arg, const Indices &indices)
Get a component of an object at a particular set of indices.
Definition: get_component.hpp:54
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:34
Definition for identity_matrix.
Definition for zero.
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:448
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
auto & operator=(Arg &&arg)
Assign from a compatible indexible object.
Definition: ToEuclideanExpr.hpp:85