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<bool Enable = true, std::enable_if_t<Enable and stdex::default_initializable<Base> 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  stdex::constructible_from<NestedObject, Arg&&> and stdex::constructible_from<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  stdex::constructible_from<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::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  // object_traits //
136  // --------------------------- //
137 
138  template<typename NestedObject, typename V0>
139  struct object_traits<FromEuclideanExpr<NestedObject, V0>>
140  {
141  static const bool is_specialized = true;
142 
143  using scalar_type = scalar_type_of_t<NestedObject>;
144 
145 
146  template<typename Arg>
147  static constexpr auto count_indices(const Arg& arg) { return OpenKalman::count_indices(nested_object(arg)); }
148 
149 
150  template<typename Arg, typename N>
151  static constexpr auto
152  get_pattern_collection(Arg&& arg, const N& n)
153  {
154  if constexpr (values::fixed<N>)
155  {
156  if constexpr (n == 0_uz) return std::forward<Arg>(arg).vector_space_descriptor_index_0;
157  else return OpenKalman::get_pattern_collection(nested_object(std::forward<Arg>(arg)), n);
158  }
159  else
160  {
161  using Desc = DynamicDescriptor<scalar_type_of<Arg>>;
162  if (n == 0) return Desc {std::forward<Arg>(arg).vector_space_descriptor_index_0};
163  else return Desc {OpenKalman::get_pattern_collection(nested_object(std::forward<Arg>(arg)), n)};
164  }
165  }
166 
167 
168  template<typename Arg>
169  static decltype(auto)
170  nested_object(Arg&& arg)
171  {
172  return std::forward<Arg>(arg).nested_object();
173  }
174 
175 
176  template<typename Arg>
177  static constexpr auto
178  get_constant(const Arg& arg)
179  {
180  if constexpr (coordinates::euclidean_pattern<V0>)
181  return constant_value {arg.nested_object()};
182  else
183  return std::monostate {};
184  }
185 
186 
187  template<typename Arg>
188  static constexpr auto
189  get_constant_diagonal(const Arg& arg)
190  {
191  if constexpr (coordinates::euclidean_pattern<V0>)
192  return constant_diagonal_value {arg.nested_object()};
193  else
194  return std::monostate {};
195  }
196 
197 
198  template<applicability b>
199  static constexpr bool
200  one_dimensional = coordinates::euclidean_pattern<V0> and OpenKalman::one_dimensional<NestedObject, b>;
201 
202 
203  template<applicability b>
204  static constexpr bool
205  is_square = coordinates::euclidean_pattern<V0> and square_shaped<NestedObject, b>;
206 
207 
208  template<triangle_type t>
209  static constexpr bool
210  triangle_type_value = coordinates::euclidean_pattern<V0> and triangular_matrix<NestedObject, t>;
211 
212 
213  static constexpr bool
214  is_triangular_adapter = false;
215 
216 
217  static constexpr bool
218  is_hermitian = coordinates::euclidean_pattern<V0> and hermitian_matrix<NestedObject>;
219 
220 
221  // hermitian_adapter_type is omitted
222 
223 
224  static constexpr bool is_writable = false;
225 
226 
227 #ifdef __cpp_lib_concepts
228  template<typename Arg> requires coordinates::euclidean_pattern<V0> and raw_data_defined_for<nested_object_of_t<Arg&>>
229 #else
230  template<typename Arg, std::enable_if_t<coordinates::euclidean_pattern<V0> and raw_data_defined_for<typename nested_object_of<Arg&>::type>, int> = 0>
231 #endif
232  static constexpr auto * const
233  raw_data(Arg& arg)
234  {
235  return internal::raw_data(nested_object(arg));
236  }
237 
238 
239  static constexpr data_layout
240  layout = coordinates::euclidean_pattern<V0> ? layout_of_v<NestedObject> : data_layout::none;
241 
242 
243 #ifdef __cpp_concepts
244  template<typename Arg> requires (layout != data_layout::none)
245 #else
246  template<data_layout l = layout, typename Arg, std::enable_if_t<l != data_layout::none, int> = 0>
247 #endif
248  static auto
249  strides(Arg&& arg)
250  {
251  return OpenKalman::internal::strides(OpenKalman::nested_object(std::forward<Arg>(arg)));
252  }
253 
254  };
255 
256 
257  // --------------------- //
258  // library_interface //
259  // --------------------- //
260 
261  template<typename NestedObject, typename V0>
262  struct library_interface<FromEuclideanExpr<NestedObject, V0>>
263  {
264  private:
265 
267 
268  public:
269 
270  template<typename Derived>
271  using library_base = internal::library_base_t<Derived, std::decay_t<NestedObject>>;
272 
273 
274 #ifdef __cpp_lib_ranges
275  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
276  static constexpr values::scalar decltype(auto)
277 #else
278  template<typename Arg, typename Indices>
279  static constexpr decltype(auto)
280 #endif
281  get_component(Arg&& arg, const Indices& indices)
282  {
283  if constexpr (coordinates::euclidean_pattern<V0>)
284  {
285  return NestedInterface::get_component(nested_object(std::forward<Arg>(arg)), indices);
286  }
287  else
288  {
289  auto g {[&arg, is...](std::size_t ix) { return OpenKalman::get_component(nested_object(std::forward<Arg>(arg)), ix, is...); }};
291  return coordinates::wrap(get_pattern_collection<0>(arg), g, i);
292  else
293  return coordinates::from_stat_space(get_pattern_collection<0>(arg), g, i);
294  }
295  }
296 
297 
298 #ifdef __cpp_lib_ranges
299  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
300 #else
301  template<typename Arg, typename Indices>
302 #endif
303  static void
304  set_component(Arg& arg, const scalar_type_of_t<Arg>& s, const Indices& indices)
305  {
306  if constexpr (coordinates::euclidean_pattern<vector_space_descriptor_of<Arg, 0>>)
307  {
308  OpenKalman::set_component(nested_object(nested_object(arg)), s, indices);
309  }
310  else if constexpr (to_euclidean_expr<nested_object_of_t<Arg>>)
311  {
312  auto s {[&arg, is...](const scalar_type_of_t<Arg>& x, std::size_t i) {
313  return OpenKalman::set_component(nested_object(nested_object(arg)), x, i, is...);
314  }};
315  auto g {[&arg, is...](std::size_t ix) {
316  return OpenKalman::get_component(nested_object(nested_object(arg)), ix, is...);
317  }};
318  coordinates::set_wrapped_component(get_pattern_collection<0>(arg), s, g, s, i);
319  }
320  else
321  {
322  OpenKalman::set_component(nested_object(arg), s, i, is...);
323  }
324  }
325 
326 
327  template<typename Arg>
328  static decltype(auto) to_native_matrix(Arg&& arg)
329  {
330  return OpenKalman::to_native_matrix<nested_object_of_t<Arg>>(std::forward<Arg>(arg));
331  }
332 
333 
334  template<data_layout layout, typename Scalar, typename D>
335  static auto make_default(D&& d)
336  {
337  return make_dense_object<NestedObject, layout, Scalar>(std::forward<D>(d));
338  }
339 
340 
341  // fill_components not necessary because T is not a dense writable matrix.
342 
343 
344  template<typename C, typename D>
345  static constexpr auto make_constant(C&& c, D&& d)
346  {
347  return make_constant<NestedObject>(std::forward<C>(c), std::forward<D>(d));
348  }
349 
350 
351  template<typename Scalar, typename D>
352  static constexpr auto make_identity_matrix(D&& d)
353  {
354  return make_identity_matrix_like<NestedObject, Scalar>(std::forward<D>(d));
355  }
356 
357 
358  // get_slice
359 
360 
361  // set_slice
362 
363 
364  template<typename Arg>
365  static auto
366  to_diagonal(Arg&& arg)
367  {
368  if constexpr( has_untyped_index<Arg, 0>)
369  {
370  return to_diagonal(nested_object(std::forward<Arg>(arg)));
371  }
372  else
373  {
374  return library_interface<NestedObject>::to_diagonal(to_native_matrix<NestedObject>(std::forward<Arg>(arg)));
375  }
376  }
377 
378 
379  template<typename Arg>
380  static auto
381  diagonal_of(Arg&& arg)
382  {
383  if constexpr(has_untyped_index<Arg, 0>)
384  {
385  return diagonal_of(nested_object(std::forward<Arg>(arg)));
386  }
387  else
388  {
389  return library_interface<NestedObject>::diagonal_of(to_native_matrix<NestedObject>(std::forward<Arg>(arg)));
390  }
391  }
392 
393 
394  template<typename Arg, typename...Factors>
395  static auto
396  broadcast(Arg&& arg, const Factors&...factors)
397  {
398  return library_interface<std::decay_t<nested_object_of_t<Arg>>>::broadcast(std::forward<Arg>(arg), factors...);
399  }
400 
401 
402  template<typename...Ds, typename Operation, typename...Args>
403  static constexpr decltype(auto)
404  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& op, Args&&...args)
405  {
406  return library_interface<NestedObject>::template n_ary_operation(tup, std::forward<Operation>(op), std::forward<Args>(args)...);
407  }
408 
409 
410  template<std::size_t...indices, typename BinaryFunction, typename Arg>
411  static constexpr decltype(auto)
412  reduce(BinaryFunction&& b, Arg&& arg)
413  {
414  return library_interface<NestedObject>::template reduce<indices...>(std::forward<BinaryFunction>(b), std::forward<Arg>(arg));
415  }
416 
417 
418  template<typename Arg>
419  constexpr decltype(auto)
420  to_euclidean(Arg&& arg)
421  {
422  return nested_object(std::forward<Arg>(arg)); //< from- and then to- is an identity.
423  }
424 
425 
426  // from_euclidean not included. Double application of from_euclidean does not make sense.
427 
428 
429  template<typename Arg>
430  constexpr decltype(auto)
431  wrap_angles(Arg&& arg)
432  {
433  return std::forward<Arg>(arg); //< A FromEuclideanExpr is already wrapped
434  }
435 
436 
437  template<typename Arg>
438  static constexpr decltype(auto)
439  conjugate(Arg&& arg)
440  {
441  if constexpr(has_untyped_index<Arg, 0>)
442  {
443  return OpenKalman::conjugate(nested_object(std::forward<Arg>(arg)));
444  }
445  else
446  {
447  return std::forward<Arg>(arg).conjugate(); //< \todo Generalize this.
448  }
449  }
450 
451 
452  template<typename Arg>
453  static constexpr decltype(auto)
454  transpose(Arg&& arg)
455  {
456  if constexpr(has_untyped_index<Arg, 0>)
457  {
458  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
459  }
460  else
461  {
462  return std::forward<Arg>(arg).transpose(); //< \todo Generalize this.
463  }
464  }
465 
466 
467  template<typename Arg>
468  static constexpr decltype(auto)
469  adjoint(Arg&& arg)
470  {
471  if constexpr(has_untyped_index<Arg, 0>)
472  {
473  return OpenKalman::adjoint(nested_object(std::forward<Arg>(arg)));
474  }
475  else
476  {
477  return std::forward<Arg>(arg).adjoint(); //< \todo Generalize this.
478  }
479  }
480 
481 
482  template<typename Arg>
483  static constexpr auto
484  determinant(Arg&& arg)
485  {
486  if constexpr(has_untyped_index<Arg, 0>)
487  {
488  return OpenKalman::determinant(nested_object(std::forward<Arg>(arg)));
489  }
490  else
491  {
492  return arg.determinant(); //< \todo Generalize this.
493  }
494  }
495 
496 
497  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
498  static decltype(auto)
499  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
500  {
501  return OpenKalman::rank_update_hermitian<significant_triangle>(make_hermitian_matrix(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
502  }
503 
504 
505  template<triangle_type triangle, typename A, typename U, typename Alpha>
506  static decltype(auto) rank_update_triangular(A&& a, U&& u, const Alpha alpha)
507  {
508  return OpenKalman::rank_update_triangular(make_triangular_matrix<triangle>(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
509  }
510 
511 
512  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
513  static constexpr decltype(auto)
514  solve(A&& a, B&& b)
515  {
516  return OpenKalman::solve<must_be_unique, must_be_exact>(
517  to_native_matrix<T>(std::forward<A>(a)), std::forward<B>(b));
518  }
519 
520 
521  template<typename A>
522  static inline auto
523  LQ_decomposition(A&& a)
524  {
525  return LQ_decomposition(to_dense_object(std::forward<A>(a)));
526  }
527 
528 
529  template<typename A>
530  static inline auto
531  QR_decomposition(A&& a)
532  {
533  return QR_decomposition(to_dense_object(std::forward<A>(a)));
534  }
535 
536  };
537 
538 
539  }
540 
541 
542 } // OpenKalman
543 
544 
545 #endif
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:58
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:325
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:56
constexpr FromEuclideanExpr()
Default constructor.
Definition: FromEuclideanExpr.hpp:53
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:248
An expression that transforms angular or other modular vector space descriptors back from Euclidean s...
Definition: FromEuclideanExpr.hpp:29
decltype(auto) constexpr conjugate(Arg &&arg)
Take the complex conjugate of an indexible object.
Definition: conjugate.hpp:44
decltype(auto) constexpr wrap(const T &t, R &&data_view)
wraps a range reflecting vector-space data to its primary range.
Definition: wrap.hpp:59
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
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
decltype(auto) constexpr to_diagonal(Arg &&arg)
Convert an indexible object into a diagonal matrix.
Definition: to_diagonal.hpp:33
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:37
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 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:42
Definition: object_traits.hpp:38
decltype(auto) constexpr transpose(Arg &&arg)
Swap any two indices of an indexible_object.
Definition: transpose.hpp:163
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:36
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
constexpr NestedObject & nested_object() &
Get the nested object.
Definition: AdapterBase.hpp:76
auto & operator=(Arg &&arg)
Assign from a compatible indexible object.
Definition: FromEuclideanExpr.hpp:93
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
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, stdex::extents< IndexType, Extents... > extents)
Make an indexible object in which every element is a constant value.
Definition: make_constant.hpp:39
decltype(auto) constexpr from_stat_space(const T &t, R &&stat_data_view)
Maps a range in a vector space for directional-statistics back to a range reflecting vector-space dat...
Definition: from_stat_space.hpp:44
decltype(auto) constexpr adjoint(Arg &&arg)
Take the conjugate-transpose of an indexible_object.
Definition: adjoint.hpp:35
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:35
constexpr bool euclidean_pattern
A coordinates::pattern for a normal Euclidean vector.
Definition: euclidean_pattern.hpp:49
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:292