OpenKalman
FromEuclideanExpr.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-2024 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_FROMEUCLIDEANEXPR_HPP
17 #define OPENKALMAN_FROMEUCLIDEANEXPR_HPP
18 
19 #include "basics/traits/traits.hpp"
20 
21 namespace OpenKalman
22 {
23 
24 #ifdef __cpp_concepts
25  template<has_untyped_index<0> NestedObject, coordinates::pattern V0>
26 #else
27  template<typename NestedObject, typename V0>
28 #endif
29  struct FromEuclideanExpr : internal::AdapterBase<FromEuclideanExpr<NestedObject, V0>, NestedObject>
30  {
31 
32  private:
33 
34 #ifndef __cpp_concepts
35  static_assert(indexible<NestedObject>);
36  static_assert(coordinates::pattern<V0>);
37  static_assert(has_untyped_index<NestedObject, 0>);
38 #endif
39 
40  using Scalar = scalar_type_of_t<NestedObject>;
41 
43 
44  public:
45 
49 #ifdef __cpp_concepts
50  constexpr FromEuclideanExpr() requires std::default_initializable<Base> and fixed_pattern<V0>
51 #else
52  template<typename B = Base, std::enable_if_t<std::is_default_constructible_v<B> and fixed_pattern<V0>, int> = 0>
53  constexpr FromEuclideanExpr()
54 #endif
55  {}
56 
57 
61 #ifdef __cpp_concepts
62  template<indexible Arg, coordinates::pattern D0> requires
63  std::constructible_from<NestedObject, Arg&&> and std::constructible_from<std::decay_t<V0>, D0>
64 #else
65  template<typename Arg, typename D0, std::enable_if_t<indexible<Arg> and coordinates::pattern<C> and
66  std::is_constructible_v<NestedObject, Arg&&> and std::is_constructible_v<std::decay_t<V0>, D0>, int> = 0>
67 #endif
68  explicit FromEuclideanExpr(Arg&& arg, const D0& d0) : Base {std::forward<Arg>(arg)}, vector_space_descriptor_index_0{d0} {}
69 
70 
74 #ifdef __cpp_concepts
75  template<indexible Arg> requires std::constructible_from<NestedObject, Arg&&> and fixed_index_descriptor<V0>
76 #else
77  template<typename Arg, typename D0, std::enable_if_t<indexible<Arg> and
78  std::is_constructible_v<NestedObject, Arg&&> and fixed_index_descriptor<V0>, int> = 0>
79 #endif
80  explicit FromEuclideanExpr(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
81 
82 
86 #ifdef __cpp_concepts
87  template<indexible Arg> requires (not std::is_base_of_v<FromEuclideanExpr, std::decay_t<Arg>>) and
88  std::assignable_from<std::add_lvalue_reference_t<NestedObject>, decltype(to_euclidean(std::declval<Arg&&>()))>
89 #else
90  template<typename Arg, std::enable_if_t<indexible<Arg> and (not std::is_base_of_v<ToEuclideanExpr, std::decay_t<Arg>>) and
91  std::is_assignable_v<std::add_lvalue_reference_t<NestedObject>, decltype(to_euclidean(std::declval<Arg&&>()))>, int> = 0>
92 #endif
93  auto& operator=(Arg&& arg)
94  {
95  using TArg = decltype(to_euclidean(std::declval<Arg>()));
96  if constexpr ((zero<NestedObject> and zero<TArg>) or (identity_matrix<NestedObject> and identity_matrix<TArg>))
97  {}
98  else
99  {
100  this->nested_object() = to_euclidean(std::forward<Arg>(arg));
101  }
102  return *this;
103  }
104 
105  protected:
106 
107  std::decay_t<V0> vector_space_descriptor_index_0;
108 
109  friend struct interface::indexible_object_traits<VectorSpaceAdapter>;
110  friend struct interface::library_interface<VectorSpaceAdapter>;
111 
112  }; // struct FromEuclideanExpr
113 
114 
115  // ------------------------------ //
116  // Deduction Guide //
117  // ------------------------------ //
118 
119 #ifdef __cpp_concepts
120  template<indexible Arg, coordinates::pattern V>
121 #else
122  template<typename Arg, typename V, std::enable_if_t<indexible<Arg> and coordinates::pattern<V>, int> = 0>
123 #endif
124  FromEuclideanExpr(Arg&&, const V&) -> FromEuclideanExpr<Arg, V>;
125 
126 
127  // ------------------------- //
128  // Interfaces //
129  // ------------------------- //
130 
131  namespace interface
132  {
133 
134  // --------------------------- //
135  // indexible_object_traits //
136  // --------------------------- //
137 
138  template<typename NestedObject, typename V0>
139  struct indexible_object_traits<FromEuclideanExpr<NestedObject, V0>>
140  {
141  using scalar_type = scalar_type_of_t<NestedObject>;
142 
143 
144  template<typename Arg>
145  static constexpr auto count_indices(const Arg& arg) { return OpenKalman::count_indices(nested_object(arg)); }
146 
147 
148  template<typename Arg, typename N>
149  static constexpr auto
150  get_vector_space_descriptor(Arg&& arg, const N& n)
151  {
152  if constexpr (values::fixed<N>)
153  {
154  if constexpr (n == 0_uz) return std::forward<Arg>(arg).vector_space_descriptor_index_0;
155  else return OpenKalman::get_vector_space_descriptor(nested_object(std::forward<Arg>(arg)), n);
156  }
157  else
158  {
159  using Desc = DynamicDescriptor<scalar_type_of<Arg>>;
160  if (n == 0) return Desc {std::forward<Arg>(arg).vector_space_descriptor_index_0};
161  else return Desc {OpenKalman::get_vector_space_descriptor(nested_object(std::forward<Arg>(arg)), n)};
162  }
163  }
164 
165 
166  template<typename Arg>
167  static decltype(auto)
168  nested_object(Arg&& arg)
169  {
170  return std::forward<Arg>(arg).nested_object();
171  }
172 
173 
174  template<typename Arg>
175  static constexpr auto
176  get_constant(const Arg& arg)
177  {
178  if constexpr (coordinates::euclidean_pattern<V0>)
179  return constant_coefficient {arg.nested_object()};
180  else
181  return std::monostate {};
182  }
183 
184 
185  template<typename Arg>
186  static constexpr auto
187  get_constant_diagonal(const Arg& arg)
188  {
189  if constexpr (coordinates::euclidean_pattern<V0>)
190  return constant_diagonal_coefficient {arg.nested_object()};
191  else
192  return std::monostate {};
193  }
194 
195 
196  template<Applicability b>
197  static constexpr bool
198  one_dimensional = coordinates::euclidean_pattern<V0> and OpenKalman::one_dimensional<NestedObject, b>;
199 
200 
201  template<Applicability b>
202  static constexpr bool
203  is_square = coordinates::euclidean_pattern<V0> and square_shaped<NestedObject, b>;
204 
205 
206  template<TriangleType t>
207  static constexpr bool
208  is_triangular = coordinates::euclidean_pattern<V0> and triangular_matrix<NestedObject, t>;
209 
210 
211  static constexpr bool
212  is_triangular_adapter = false;
213 
214 
215  static constexpr bool
216  is_hermitian = coordinates::euclidean_pattern<V0> and hermitian_matrix<NestedObject>;
217 
218 
219  // hermitian_adapter_type is omitted
220 
221 
222  static constexpr bool is_writable = false;
223 
224 
225 #ifdef __cpp_lib_concepts
226  template<typename Arg> requires coordinates::euclidean_pattern<V0> and raw_data_defined_for<nested_object_of_t<Arg&>>
227 #else
228  template<typename Arg, std::enable_if_t<coordinates::euclidean_pattern<V0> and raw_data_defined_for<typename nested_object_of<Arg&>::type>, int> = 0>
229 #endif
230  static constexpr auto * const
231  raw_data(Arg& arg)
232  {
233  return internal::raw_data(nested_object(arg));
234  }
235 
236 
237  static constexpr Layout
238  layout = coordinates::euclidean_pattern<V0> ? layout_of_v<NestedObject> : Layout::none;
239 
240 
241 #ifdef __cpp_concepts
242  template<typename Arg> requires (layout != Layout::none)
243 #else
244  template<Layout l = layout, typename Arg, std::enable_if_t<l != Layout::none, int> = 0>
245 #endif
246  static auto
247  strides(Arg&& arg)
248  {
249  return OpenKalman::internal::strides(OpenKalman::nested_object(std::forward<Arg>(arg)));
250  }
251 
252  };
253 
254 
255  // --------------------- //
256  // library_interface //
257  // --------------------- //
258 
259  template<typename NestedObject, typename V0>
260  struct library_interface<FromEuclideanExpr<NestedObject, V0>>
261  {
262  private:
263 
265 
266  public:
267 
268  template<typename Derived>
269  using LibraryBase = internal::library_base_t<Derived, pattern_matrix_of_t<T>>;
270 
271 
272 #ifdef __cpp_lib_ranges
273  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
274  static constexpr values::scalar decltype(auto)
275 #else
276  template<typename Arg, typename Indices>
277  static constexpr decltype(auto)
278 #endif
279  get_component(Arg&& arg, const Indices& indices)
280  {
281  if constexpr (coordinates::euclidean_pattern<V0>)
282  {
283  return NestedInterface::get_component(nested_object(std::forward<Arg>(arg)), indices);
284  }
285  else
286  {
287  auto g {[&arg, is...](std::size_t ix) { return OpenKalman::get_component(nested_object(std::forward<Arg>(arg)), ix, is...); }};
289  return coordinates::get_wrapped_component(get_vector_space_descriptor<0>(arg), g, i);
290  else
291  return coordinates::from_stat_space(get_vector_space_descriptor<0>(arg), g, i);
292  }
293  }
294 
295 
296 #ifdef __cpp_lib_ranges
297  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
298 #else
299  template<typename Arg, typename Indices>
300 #endif
301  static void
302  set_component(Arg& arg, const scalar_type_of_t<Arg>& s, const Indices& indices)
303  {
304  if constexpr (coordinates::euclidean_pattern<vector_space_descriptor_of<Arg, 0>>)
305  {
307  }
308  else if constexpr (to_euclidean_expr<nested_object_of_t<Arg>>)
309  {
310  auto s {[&arg, is...](const scalar_type_of_t<Arg>& x, std::size_t i) {
311  return OpenKalman::set_component(nested_object(nested_object(arg)), x, i, is...);
312  }};
313  auto g {[&arg, is...](std::size_t ix) {
314  return OpenKalman::get_component(nested_object(nested_object(arg)), ix, is...);
315  }};
316  coordinates::set_wrapped_component(get_vector_space_descriptor<0>(arg), s, g, s, i);
317  }
318  else
319  {
320  OpenKalman::set_component(nested_object(arg), s, i, is...);
321  }
322  }
323 
324 
325  template<typename Arg>
326  static decltype(auto) to_native_matrix(Arg&& arg)
327  {
328  return OpenKalman::to_native_matrix<nested_object_of_t<Arg>>(std::forward<Arg>(arg));
329  }
330 
331 
332  template<Layout layout, typename Scalar, typename D>
333  static auto make_default(D&& d)
334  {
335  return make_dense_object<NestedObject, layout, Scalar>(std::forward<D>(d));
336  }
337 
338 
339  // fill_components not necessary because T is not a dense writable matrix.
340 
341 
342  template<typename C, typename D>
343  static constexpr auto make_constant(C&& c, D&& d)
344  {
345  return make_constant<NestedObject>(std::forward<C>(c), std::forward<D>(d));
346  }
347 
348 
349  template<typename Scalar, typename D>
350  static constexpr auto make_identity_matrix(D&& d)
351  {
352  return make_identity_matrix_like<NestedObject, Scalar>(std::forward<D>(d));
353  }
354 
355 
356  // get_slice
357 
358 
359  // set_slice
360 
361 
362  template<typename Arg>
363  static auto
364  to_diagonal(Arg&& arg)
365  {
366  if constexpr( has_untyped_index<Arg, 0>)
367  {
368  return to_diagonal(nested_object(std::forward<Arg>(arg)));
369  }
370  else
371  {
372  return library_interface<NestedObject>::to_diagonal(to_native_matrix<NestedObject>(std::forward<Arg>(arg)));
373  }
374  }
375 
376 
377  template<typename Arg>
378  static auto
379  diagonal_of(Arg&& arg)
380  {
381  if constexpr(has_untyped_index<Arg, 0>)
382  {
383  return diagonal_of(nested_object(std::forward<Arg>(arg)));
384  }
385  else
386  {
387  return library_interface<NestedObject>::diagonal_of(to_native_matrix<NestedObject>(std::forward<Arg>(arg)));
388  }
389  }
390 
391 
392  template<typename Arg, typename...Factors>
393  static auto
394  broadcast(Arg&& arg, const Factors&...factors)
395  {
396  return library_interface<std::decay_t<nested_object_of_t<Arg>>>::broadcast(std::forward<Arg>(arg), factors...);
397  }
398 
399 
400  template<typename...Ds, typename Operation, typename...Args>
401  static constexpr decltype(auto)
402  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& op, Args&&...args)
403  {
404  return library_interface<NestedObject>::template n_ary_operation(tup, std::forward<Operation>(op), std::forward<Args>(args)...);
405  }
406 
407 
408  template<std::size_t...indices, typename BinaryFunction, typename Arg>
409  static constexpr decltype(auto)
410  reduce(BinaryFunction&& b, Arg&& arg)
411  {
412  return library_interface<NestedObject>::template reduce<indices...>(std::forward<BinaryFunction>(b), std::forward<Arg>(arg));
413  }
414 
415 
416  template<typename Arg>
417  constexpr decltype(auto)
418  to_euclidean(Arg&& arg)
419  {
420  return nested_object(std::forward<Arg>(arg)); //< from- and then to- is an identity.
421  }
422 
423 
424  // from_euclidean not included. Double application of from_euclidean does not make sense.
425 
426 
427  template<typename Arg>
428  constexpr decltype(auto)
429  wrap_angles(Arg&& arg)
430  {
431  return std::forward<Arg>(arg); //< A FromEuclideanExpr is already wrapped
432  }
433 
434 
435  template<typename Arg>
436  static constexpr decltype(auto)
437  conjugate(Arg&& arg)
438  {
439  if constexpr(has_untyped_index<Arg, 0>)
440  {
441  return OpenKalman::conjugate(nested_object(std::forward<Arg>(arg)));
442  }
443  else
444  {
445  return std::forward<Arg>(arg).conjugate(); //< \todo Generalize this.
446  }
447  }
448 
449 
450  template<typename Arg>
451  static constexpr decltype(auto)
452  transpose(Arg&& arg)
453  {
454  if constexpr(has_untyped_index<Arg, 0>)
455  {
456  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
457  }
458  else
459  {
460  return std::forward<Arg>(arg).transpose(); //< \todo Generalize this.
461  }
462  }
463 
464 
465  template<typename Arg>
466  static constexpr decltype(auto)
467  adjoint(Arg&& arg)
468  {
469  if constexpr(has_untyped_index<Arg, 0>)
470  {
471  return OpenKalman::adjoint(nested_object(std::forward<Arg>(arg)));
472  }
473  else
474  {
475  return std::forward<Arg>(arg).adjoint(); //< \todo Generalize this.
476  }
477  }
478 
479 
480  template<typename Arg>
481  static constexpr auto
482  determinant(Arg&& arg)
483  {
484  if constexpr(has_untyped_index<Arg, 0>)
485  {
486  return OpenKalman::determinant(nested_object(std::forward<Arg>(arg)));
487  }
488  else
489  {
490  return arg.determinant(); //< \todo Generalize this.
491  }
492  }
493 
494 
495  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
496  static decltype(auto)
497  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
498  {
499  return OpenKalman::rank_update_hermitian<significant_triangle>(make_hermitian_matrix(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
500  }
501 
502 
503  template<TriangleType triangle, typename A, typename U, typename Alpha>
504  static decltype(auto) rank_update_triangular(A&& a, U&& u, const Alpha alpha)
505  {
506  return OpenKalman::rank_update_triangular(make_triangular_matrix<triangle>(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
507  }
508 
509 
510  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
511  static constexpr decltype(auto)
512  solve(A&& a, B&& b)
513  {
514  return OpenKalman::solve<must_be_unique, must_be_exact>(
515  to_native_matrix<T>(std::forward<A>(a)), std::forward<B>(b));
516  }
517 
518 
519  template<typename A>
520  static inline auto
521  LQ_decomposition(A&& a)
522  {
523  return LQ_decomposition(to_dense_object(std::forward<A>(a)));
524  }
525 
526 
527  template<typename A>
528  static inline auto
529  QR_decomposition(A&& a)
530  {
531  return QR_decomposition(to_dense_object(std::forward<A>(a)));
532  }
533 
534  };
535 
536 
537  } // namespace interface
538 
539 
540 } // OpenKalman
541 
542 
543 #endif //OPENKALMAN_FROMEUCLIDEANEXPR_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
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:66
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
constexpr FromEuclideanExpr()
Default constructor.
Definition: FromEuclideanExpr.hpp:53
Definition: indexible_object_traits.hpp:36
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
constexpr bool to_euclidean_expr
Specifies that T is an expression converting coefficients to Euclidean space (i.e., ToEuclideanExpr).
Definition: forward-class-declarations.hpp:404
An expression that transforms angular or other modular vector space descriptors back from Euclidean s...
Definition: FromEuclideanExpr.hpp:29
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
The coordinates::pattern for index N of object T.
Definition: vector_space_descriptor_of.hpp:34
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
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
FromEuclideanExpr(Arg &&arg)
Construct from a compatible indexible object if the coordinate_list of index 0 is fixed...
Definition: FromEuclideanExpr.hpp:80
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
FromEuclideanExpr(Arg &&arg, const D0 &d0)
Construct from a compatible indexible object.
Definition: FromEuclideanExpr.hpp:68
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
decltype(auto) constexpr to_euclidean(Arg &&arg)
Project the vector space associated with index 0 to a Euclidean space for applying directional statis...
Definition: to_euclidean.hpp:38
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
auto & operator=(Arg &&arg)
Assign from a compatible indexible object.
Definition: FromEuclideanExpr.hpp:93
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
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
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