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 "basics/basics.hpp"
20 #include "coordinates/coordinates.hpp"
23 #include "../traits/count_indices.hpp"
24 #include "linear-algebra/traits/traits.hpp"
25 #include "linear-algebra/adapters/internal/AdapterBase.hpp"
26 
27 namespace OpenKalman
28 {
29 
31 #ifdef __cpp_concepts
32  template<indexible NestedObject>
33 #else
34  template<typename NestedObject>
35 #endif
36  struct ToEuclideanExpr : internal::AdapterBase<ToEuclideanExpr<NestedObject>, NestedObject>
37  {
38 
39  private:
40 
41 #ifndef __cpp_concepts
42  static_assert(indexible<NestedObject>);
43 #endif
44 
45  using Scalar = element_type_of_t<NestedObject>;
46 
47  using Base = internal::AdapterBase<ToEuclideanExpr, NestedObject>;
48 
49  public:
50 
54 #ifdef __cpp_concepts
55  constexpr ToEuclideanExpr() requires std::default_initializable<Base>
56 #else
57  template<bool Enable = true, std::enable_if_t<Enable and stdex::default_initializable<Base>, int> = 0>
58  constexpr ToEuclideanExpr()
59 #endif
60  {}
61 
62 
66 #ifdef __cpp_concepts
67  template<indexible Arg> requires std::constructible_from<NestedObject, Arg&&>
68 #else
69  template<typename Arg, std::enable_if_t<indexible<Arg> and stdex::constructible_from<NestedObject, Arg&&>, int> = 0>
70 #endif
71  explicit ToEuclideanExpr(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
72 
73 
77 #ifdef __cpp_concepts
78  template<indexible Arg> requires (not std::is_base_of_v<ToEuclideanExpr, std::decay_t<Arg>>) and
79  std::assignable_from<std::add_lvalue_reference_t<NestedObject>,
80  decltype(from_euclidean(std::declval<Arg>(), get_pattern_collection<0>(std::declval<NestedObject>())))>
81 #else
82  template<typename Arg, std::enable_if_t<indexible<Arg> and (not std::is_base_of_v<ToEuclideanExpr, std::decay_t<Arg>>) and
83  std::is_assignable_v<std::add_lvalue_reference_t<NestedObject>,
84  decltype(from_euclidean(std::declval<Arg>(), get_pattern_collection<0>(std::declval<NestedObject>())))>, int> = 0>
85 #endif
86  auto& operator=(Arg&& arg)
87  {
88  using FArg = decltype(from_euclidean(std::declval<Arg>(), get_pattern_collection<0>(std::declval<NestedObject>())));
89  if constexpr ((zero<NestedObject> and zero<FArg>) or (identity_matrix<NestedObject> and identity_matrix<FArg>))
90  {}
91  else
92  {
93  this->nested_object() = from_euclidean(std::forward<Arg>(arg), get_pattern_collection<0>(nested_object(arg)));
94  }
95  return *this;
96  }
97 
98  };
99 
100 
104 #ifdef __cpp_concepts
105 template<indexible Arg>
106 #else
107  template<typename Arg, std::enable_if_t<indexible<Arg>, int> = 0>
108 #endif
110 
111 
112  namespace interface
113  {
114  template<typename NestedObject>
115  struct object_traits<ToEuclideanExpr<NestedObject>>
116  {
117  using scalar_type = element_type_of_t<NestedObject>;
118 
119  template<typename Arg>
120  static constexpr auto count_indices(const Arg& arg) { return OpenKalman::count_indices(nested_object(arg)); }
121 
122 
123  template<typename Arg, typename N>
124  static constexpr auto
125  get_pattern_collection(Arg&& arg, const N& n)
126  {
127  if constexpr (values::fixed<N>)
128  {
129  if constexpr (n == 0_uz) return Axis;
130  else return OpenKalman::get_pattern_collection(nested_object(std::forward<Arg>(arg)), n);
131  }
132  else
133  {
134  using Desc = coordinates::DynamicDescriptor<element_type_of<Arg>>;
135  if (n == 0) return Desc {coordinates::Axis};
136  else return Desc {OpenKalman::get_pattern_collection(nested_object(std::forward<Arg>(arg)), n)};
137  }
138  }
139 
140 
141  template<typename Arg>
142  static decltype(auto)
143  nested_object(Arg&& arg)
144  {
145  return std::forward<Arg>(arg).nested_object();
146  }
147 
148 
149  template<typename Arg>
150  static constexpr auto
151  get_constant(const Arg& arg)
152  {
153  if constexpr (has_untyped_index<NestedObject, 0>)
154  return constant_value {arg.nested_object()};
155  else
156  return std::monostate {};
157  }
158 
159 
160  template<typename Arg>
161  static constexpr auto
162  get_constant_diagonal(const Arg& arg)
163  {
164  if constexpr (has_untyped_index<NestedObject, 0>)
165  return constant_diagonal_value {arg.nested_object()};
166  else
167  return std::monostate {};
168  }
169 
170 
171  template<applicability b>
172  static constexpr bool
173  one_dimensional = has_untyped_index<NestedObject, 0> and OpenKalman::one_dimensional<NestedObject, b>;
174 
175 
176  template<applicability b>
177  static constexpr bool
178  is_square = has_untyped_index<NestedObject, 0> and square_shaped<NestedObject, b>;
179 
180 
181  template<triangle_type t>
182  static constexpr bool
183  triangle_type_value = has_untyped_index<NestedObject, 0> and triangular_matrix<NestedObject, t>;
184 
185 
186  static constexpr bool
187  is_triangular_adapter = false;
188 
189 
190  static constexpr bool
191  is_hermitian = has_untyped_index<NestedObject, 0> and hermitian_matrix<NestedObject>;
192 
193 
194  // hermitian_adapter_type is omitted
195 
196 
197  static constexpr bool is_writable = false;
198 
199 
200 #ifdef __cpp_lib_concepts
201  template<typename Arg> requires has_untyped_index<NestedObject, 0> and raw_data_defined_for<nested_object_of_t<Arg&>>
202 #else
203  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>
204 #endif
205  static constexpr auto * const
206  raw_data(Arg& arg)
207  {
208  return internal::raw_data(OpenKalman::nested_object(arg));
209  }
210 
211 
212  static constexpr data_layout
213  layout = has_untyped_index<NestedObject, 0> ? layout_of_v<NestedObject> : data_layout::none;
214 
215 
216 #ifdef __cpp_concepts
217  template<typename Arg> requires (layout != data_layout::none)
218 #else
219  template<data_layout l = layout, typename Arg, std::enable_if_t<l != data_layout::none, int> = 0>
220 #endif
221  static auto
222  strides(Arg&& arg)
223  {
224  return OpenKalman::internal::strides(OpenKalman::nested_object(std::forward<Arg>(arg)));
225  }
226 
227  };
228 
229 
230  template<typename NestedObject>
231  struct library_interface<ToEuclideanExpr<NestedObject>>
232  {
233  private:
234 
236 
237  public:
238 
239  template<typename Derived>
240  using library_base = internal::library_base_t<Derived, std::decay_t<NestedObject>>;
241 
242 
243 #ifdef __cpp_lib_ranges
244  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
245  static constexpr values::scalar decltype(auto)
246 #else
247  template<typename Arg, typename Indices>
248  static constexpr decltype(auto)
249 #endif
250  access(Arg&& arg, const Indices& indices)
251  {
252  if constexpr (has_untyped_index<NestedObject, 0>)
253  {
254  return NestedInterface::access(nested_object(std::forward<Arg>(arg)), indices);
255  }
256  else
257  {
258  auto g {[&arg, is...](std::size_t ix) { return access(nested_object(std::forward<Arg>(arg)), ix, is...); }};
259  return coordinates::to_stat_space(get_pattern_collection<0>(arg), g, i);
260  }
261  }
262 
263 
264 #ifdef __cpp_lib_ranges
265  template<indexible Arg, std::ranges::input_range Indices> requires values::index<std::ranges::range_value_t<Indices>>
266 #else
267  template<typename Arg, typename Indices>
268 #endif
269  static void
270  set_component(Arg& arg, const element_type_of_t<Arg>& s, const Indices& indices)
271  {
272  if constexpr (has_untyped_index<NestedObject, 0>)
273  {
274  NestedInterface::set_component(nested_object(arg), s, indices);
275  }
276  else
277  {
278  set_component(nested_object(arg), s, indices);
279  }
280  }
281 
282 
283  template<typename Arg>
284  static decltype(auto) to_native_matrix(Arg&& arg)
285  {
286  return OpenKalman::to_native_matrix<nested_object_of_t<Arg>>(std::forward<Arg>(arg));
287  }
288 
289 
290  template<data_layout layout, typename Scalar, typename D>
291  static auto make_default(D&& d)
292  {
293  return make_dense_object<NestedObject, layout, Scalar>(std::forward<D>(d));
294  }
295 
296 
297  // fill_components not necessary because T is not a dense writable matrix.
298 
299 
300  template<typename C, typename D>
301  static constexpr auto make_constant(C&& c, D&& d)
302  {
303  return make_constant<NestedObject>(std::forward<C>(c), std::forward<D>(d));
304  }
305 
306 
307  template<typename Scalar, typename D>
308  static constexpr auto make_identity_matrix(D&& d)
309  {
310  return make_identity_matrix_like<NestedObject, Scalar>(std::forward<D>(d));
311  }
312 
313 
314  // get_slice
315 
316 
317  // set_slice
318 
319 
320  template<typename Arg>
321  static auto
322  to_diagonal(Arg&& arg)
323  {
324  if constexpr( has_untyped_index<NestedObject, 0>)
325  {
326  return to_diagonal(nested_object(std::forward<Arg>(arg)));
327  }
328  else
329  {
330  return library_interface<P>::to_diagonal(to_native_matrix<NestedObject>(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  return library_interface<P>::diagonal_of(to_native_matrix<NestedObject>(std::forward<Arg>(arg)));
346  }
347  }
348 
349 
350  template<typename Arg, typename...Factors>
351  static auto
352  broadcast(Arg&& arg, const Factors&...factors)
353  {
354  return library_interface<std::decay_t<nested_object_of_t<Arg>>>::broadcast(std::forward<Arg>(arg), factors...);
355  }
356 
357 
358  template<typename...Ds, typename Operation, typename...Args>
359  static constexpr decltype(auto)
360  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& op, Args&&...args)
361  {
362  using P = std::decay_t<NestedObject>;
363  return library_interface<P>::template n_ary_operation(tup, std::forward<Operation>(op), std::forward<Args>(args)...);
364  }
365 
366 
367  template<std::size_t...indices, typename BinaryFunction, typename Arg>
368  static constexpr decltype(auto)
369  reduce(BinaryFunction&& b, Arg&& arg)
370  {
371  using P = std::decay_t<NestedObject>;
372  return library_interface<P>::template reduce<indices...>(std::forward<BinaryFunction>(b), std::forward<Arg>(arg));
373  }
374 
375 
376  // to_euclidean not included
377 
378  // from_duclidean not included
379 
380  // wrap_angles not included
381 
382 
383  template<typename Arg>
384  static constexpr decltype(auto)
385  conjugate(Arg&& arg)
386  {
387  if constexpr(has_untyped_index<NestedObject, 0>)
388  {
389  return OpenKalman::conjugate(nested_object(std::forward<Arg>(arg)));
390  }
391  else
392  {
393  return std::forward<Arg>(arg).conjugate(); //< \todo Generalize this.
394  }
395  }
396 
397 
398  template<typename Arg>
399  static constexpr decltype(auto)
400  transpose(Arg&& arg)
401  {
402  if constexpr(has_untyped_index<NestedObject, 0>)
403  {
404  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
405  }
406  else
407  {
408  return std::forward<Arg>(arg).transpose(); //< \todo Generalize this.
409  }
410  }
411 
412 
413  template<typename Arg>
414  static constexpr decltype(auto)
415  adjoint(Arg&& arg)
416  {
417  if constexpr(has_untyped_index<NestedObject, 0>)
418  {
419  return OpenKalman::adjoint(nested_object(std::forward<Arg>(arg)));
420  }
421  else
422  {
423  return std::forward<Arg>(arg).adjoint(); //< \todo Generalize this.
424  }
425  }
426 
427 
428  template<typename Arg>
429  static constexpr auto
430  determinant(Arg&& arg)
431  {
432  if constexpr(has_untyped_index<NestedObject, 0>)
433  {
434  return OpenKalman::determinant(nested_object(std::forward<Arg>(arg)));
435  }
436  else
437  {
438  return arg.determinant(); //< \todo Generalize this.
439  }
440  }
441 
442 
443  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
444  static decltype(auto)
445  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
446  {
447  return OpenKalman::rank_update_hermitian<significant_triangle>(make_hermitian_matrix(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
448  }
449 
450 
451  template<triangle_type triangle, typename A, typename U, typename Alpha>
452  static decltype(auto) rank_update_triangular(A&& a, U&& u, const Alpha alpha)
453  {
454  return OpenKalman::rank_update_triangular(make_triangular_matrix<triangle>(to_dense_object(std::forward<A>(a))), std::forward<U>(u), alpha);
455  }
456 
457 
458  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
459  static constexpr decltype(auto)
460  solve(A&& a, B&& b)
461  {
462  return OpenKalman::solve<must_be_unique, must_be_exact>(
463  to_native_matrix<T>(std::forward<A>(a)), std::forward<B>(b));
464  }
465 
466 
467  template<typename A>
468  static inline auto
469  LQ_decomposition(A&& a)
470  {
471  return LQ_decomposition(to_dense_object(std::forward<A>(a)));
472  }
473 
474 
475  template<typename A>
476  static inline auto
477  QR_decomposition(A&& a)
478  {
479  return QR_decomposition(to_dense_object(std::forward<A>(a)));
480  }
481 
482  };
483 
484 
485  }
486 
487 
488 } // OpenKalman
489 
490 
491 
492 #endif
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:325
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:56
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 to_stat_space(const T &t, R &&data_view)
Maps a range reflecting vector-space data to a corresponding range in a vector space for directional ...
Definition: to_stat_space.hpp:44
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
decltype(auto) constexpr conjugate(Arg &&arg)
Take the complex conjugate of an indexible object.
Definition: conjugate.hpp:44
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
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:42
decltype(auto) constexpr access(Arg &&arg, const Indices &indices)
Access a component of an indexible object at a given set of indices.
Definition: access.hpp:74
Definition: object_traits.hpp:38
ToEuclideanExpr(Arg &&arg)
Construct from compatible indexible object.
Definition: ToEuclideanExpr.hpp:71
decltype(auto) constexpr transpose(Arg &&arg)
Swap any two indices of an indexible_object.
Definition: transpose.hpp:163
An expression that transforms vector space descriptors into Euclidean space for application of direct...
Definition: forward-class-declarations.hpp:228
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
constexpr ToEuclideanExpr()
Default constructor.
Definition: ToEuclideanExpr.hpp:58
constexpr NestedObject & nested_object() &
Get the nested object.
Definition: AdapterBase.hpp:76
Dimensions< 1 > Axis
Alias for a 1D Euclidean coordinates::pattern object.
Definition: Dimensions.hpp:171
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
A structure representing the dimensions associated with of a particular index.
Definition: Dimensions.hpp:42
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 adjoint(Arg &&arg)
Take the conjugate-transpose of an indexible_object.
Definition: adjoint.hpp:35
Basic definitions for OpenKalman as a whole.
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:35
Definition for identity_matrix.
Definition for zero.
typename element_type_of< T >::type element_type_of_t
helper template for element_type_of.
Definition: element_type_of.hpp:54
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:292
auto & operator=(Arg &&arg)
Assign from a compatible indexible object.
Definition: ToEuclideanExpr.hpp:86