OpenKalman
eigen-tensor-library-interface.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) 2023 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_EIGEN_TENSOR_LIBRARY_INTERFACE_HPP
17 #define OPENKALMAN_EIGEN_TENSOR_LIBRARY_INTERFACE_HPP
18 
19 #include <type_traits>
20 #include <tuple>
21 #include <random>
23 
24 
25 // ------------------- //
26 // library_interface //
27 // ------------------- //
28 
29 namespace OpenKalman::interface
30 {
31 #ifdef __cpp_concepts
32  template<Eigen3::eigen_tensor_general<true> T>
33  struct library_interface<T>
34 #else
35  template<typename T>
36  struct library_interface<T, std::enable_if_t<Eigen3::eigen_tensor_general<T, true>>>
37 #endif
38  {
39  private:
40 
41  using IndexType = typename Eigen::internal::traits<T>::Index;
42 
43  public:
44 
45  template<typename Derived>
47 
48 
49 #ifdef __cpp_lib_concepts
50  template<indexible Arg, std::size_t NumIndices> requires (NumIndices == index_count_v<Arg>)
51  static constexpr values::scalar decltype(auto)
52 #else
53  template <typename Arg, std::size_t NumIndices, std::enable_if_t<NumIndices == index_count_v<Arg>, int> = 0>
54  static constexpr decltype(auto)
55 #endif
56  get_component(Arg&& arg, const std::array<IndexType, NumIndices>& indices)
57  {
58  if constexpr ((Eigen::internal::traits<T>::Flags & Eigen::LvalueBit) != 0)
59  return Eigen::TensorEvaluator<std::remove_reference_t<Arg>, Eigen::DefaultDevice> {std::forward<Arg>(arg), Eigen::DefaultDevice{}}.coeffRef(indices);
60  else
61  return Eigen::TensorEvaluator<std::remove_reference_t<Arg>, Eigen::DefaultDevice> {std::forward<Arg>(arg), Eigen::DefaultDevice{}}.coeff(indices);
62  }
63 
64  private:
65 
66  template<std::size_t ix_count, typename I, typename End, typename...Ixs>
67  static constexpr decltype(auto)
68  make_ix_array(I i, const End& end, Ixs...indices)
69  {
70  if constexpr (ix_count == 0)
71  {
72  if (i != end) throw std::logic_error("Too many indices on component access");
73  return std::array<IndexType, sizeof...(Ixs)> {indices...};
74  }
75  else
76  {
77  if (i == end) throw std::logic_error("Not enough indices on component access");
78  auto this_ix {*i};
79  return make_ix_array<ix_count - 1>(++i, end, indices..., static_cast<IndexType>(this_ix));
80  }
81  }
82 
83  public:
84 
85 #ifdef __cpp_lib_ranges
86  template<indexible Arg, std::ranges::input_range Indices> requires
87  std::convertible_to<std::ranges::range_value_t<Indices>, const typename std::decay_t<Arg>::Index>
88  static constexpr values::scalar decltype(auto)
89 #else
90  template<typename Arg, typename Indices>
91  static constexpr decltype(auto)
92 #endif
93  get_component(Arg&& arg, const Indices& indices)
94  {
95  constexpr std::size_t ix_count = Eigen::internal::traits<T>::NumDimensions;
96 #ifdef __cpp_lib_ranges
97  namespace ranges = std::ranges;
98 #endif
99  return get_component(std::forward<Arg>(arg), make_ix_array<ix_count>(ranges::begin(indices), ranges::end(indices)));
100  }
101 
102 
103 #ifdef __cpp_lib_concepts
104  template<indexible Arg, std::size_t NumIndices> requires (NumIndices == index_count_v<Arg>) and (not std::is_const_v<Arg>)
105 #else
107  not std::is_const<Arg>::value, int> = 0>
108 #endif
109  static void
110  set_component(Arg& arg, const scalar_type_of_t<T>& s, const std::array<IndexType, NumIndices>& indices)
111  {
112  Eigen::TensorEvaluator<Arg, Eigen::DefaultDevice> {arg, Eigen::DefaultDevice{}}.coeffRef(indices) = s;
113  }
114 
115 
116 #ifdef __cpp_lib_ranges
117  template<indexible Arg, std::ranges::input_range Indices> requires
118  std::convertible_to<std::ranges::range_value_t<Indices>, const typename std::decay_t<Arg>::Index> and
119  ((Eigen::internal::traits<std::decay_t<Arg>>::Flags & Eigen::LvalueBit) != 0x0) and (not std::is_const_v<Arg>)
120 #else
121  template<typename Arg, typename Indices, std::enable_if_t<
122  ((Eigen::internal::traits<std::decay_t<Arg>>::Flags & Eigen::LvalueBit) != 0x0) and (not std::is_const_v<Arg>), int> = 0>
123 #endif
124  static void
125  set_component(Arg& arg, const scalar_type_of_t<T>& s, const Indices& indices)
126  {
127  constexpr std::size_t ix_count = Eigen::internal::traits<T>::NumDimensions;
128 #ifdef __cpp_lib_ranges
129  namespace ranges = std::ranges;
130 #endif
131  set_component(arg, s, make_ix_array<ix_count>(ranges::begin(indices), ranges::end(indices)));
132  }
133 
134  private:
135 
136  template<auto l, typename Arg, std::size_t...Is>
137  static constexpr auto
138  make_TensorMap_impl(const Arg& arg, std::index_sequence<Is...>)
139  {
140  using M = Eigen::TensorFixedSize<scalar_type_of_t<Arg>, Eigen::Sizes<static_cast<std::ptrdiff_t>(index_dimension_of_v<Arg, Is>)...>, l, IndexType>;
141  return Eigen::TensorMap<const M, l> {internal::raw_data(arg), index_dimension_of_v<Arg, Is>...};
142  }
143 
144 
145  template<auto l, typename Arg>
146  static constexpr auto
147  make_TensorMap(const Arg& arg)
148  {
149  if constexpr (has_dynamic_dimensions<Arg>)
150  {
151  using M = Eigen::Tensor<scalar_type_of_t<Arg>, index_count_v<Arg>, l, IndexType>;
152  return Eigen::TensorMap<const M, l>{internal::raw_data(arg)};
153  }
154  else
155  {
156  return make_TensorMap_impl<l>(arg, std::make_index_sequence<index_count_v<Arg>>{});
157  }
158  }
159 
160  public:
161 
162  template<typename Arg>
163  static decltype(auto)
164  to_native_matrix(Arg&& arg)
165  {
166  if constexpr (Eigen3::eigen_tensor_wrapper<Arg>)
167  {
168  return std::forward<Arg>(arg);
169  }
170  else if constexpr (internal::library_wrapper<Arg>)
171  {
172  return to_native_matrix(OpenKalman::nested_object(std::forward<Arg>(arg)));
173  }
174  else if constexpr (not Eigen3::eigen_tensor_general<Arg> and directly_accessible<Arg> and std::is_lvalue_reference_v<Arg>)
175  {
176  if constexpr (layout_of_v<Arg> == Layout::stride and internal::has_static_strides<Arg>)
177  {
178  auto strides = internal::strides(arg);
179  constexpr std::ptrdiff_t stride0 = std::get<0>(strides);
180  constexpr std::ptrdiff_t strideN = std::get<index_count_v<Arg> - 1>(strides);
181  constexpr auto l = stride0 > strideN ? Eigen::RowMajor : Eigen::ColMajor;
182  return std::apply(
183  [](auto&& map, auto&&...s){
184  return Eigen::TensorStridingOp {map, std::array<std::size_t, index_count_v<Arg>>{s...}};
185  },
186  std::tuple_cat(make_TensorMap<l>(arg), std::move(strides)));
187  }
188  else
189  {
190  constexpr auto l = layout_of_v<Arg> == Layout::right ? Eigen::RowMajor : Eigen::ColMajor;
191  return make_TensorMap<l>(arg);
192  }
193  }
194  else
195  {
196  return Eigen3::make_eigen_tensor_wrapper(std::forward<Arg>(arg));
197  }
198  }
199 
200 
201 #ifdef __cpp_concepts
202  template<typename To, Eigen3::eigen_tensor_general From> requires (std::assignable_from<To&, From&&>)
203 #else
204  template<typename To, typename From, std::enable_if_t<Eigen3::eigen_tensor_general<From> and std::is_assignable_v<To&, From&&>, int> = 0>
205 #endif
206  static void assign(To& a, From&& b)
207  {
208  a = std::forward<From>(b);
209  }
210 
211 
212  template<Layout layout, typename Scalar, typename...D>
213  static auto make_default(D&&...d)
214  {
215  constexpr auto options = layout == Layout::right ? Eigen::RowMajor : Eigen::ColMajor;
216  if constexpr (((dynamic_pattern<D>) or ...))
217  return Eigen::Tensor<Scalar, sizeof...(D), options, IndexType>(static_cast<IndexType>(get_dimension(d))...);
218  else
219  return Eigen::TensorFixedSize<Scalar, Eigen::Sizes<static_cast<std::ptrdiff_t>(coordinates::dimension_of_v<D>)...>, options, IndexType> {};
220  }
221 
222 
223 #ifdef __cpp_concepts
224  template<Layout layout, writable Arg, std::convertible_to<scalar_type_of_t<Arg>> ... Scalars>
225  requires (layout == Layout::right) or (layout == Layout::left)
226 #else
227  template<Layout layout, typename Arg, typename...Scalars, std::enable_if_t<writable<Arg> and
228  (layout == Layout::right or or layout == Layout::left) and
229  std::conjunction<std::is_convertible<Scalars, typename scalar_type_of<Arg>::type>::value...>::value, int> = 0>
230 #endif
231  static void fill_components(Arg& arg, const Scalars ... scalars)
232  {
233  if constexpr (layout == Layout::left)
234  arg.swap_layout().setValues({scalars...});
235  else
236  arg.setValues({scalars...});
237  }
238 
239 
240 #ifdef __cpp_concepts
241  template<values::dynamic C, typename...Ds> requires (... and (not dynamic_pattern<Ds>))
242  static constexpr constant_matrix auto
243 #else
244  template<typename C, typename...Ds, std::enable_if_t<values::dynamic<C> and
245  (... and (not dynamic_pattern<Ds>)), int> = 0>
246  static constexpr auto
247 #endif
248  make_constant(C&& c, Ds&&...ds)
249  {
250  auto value = values::to_number(std::forward<C>(c));
251  using Scalar = std::decay_t<decltype(value)>;
252  auto m = make_default<Layout::none, Scalar>(std::forward<Ds>(ds)...);
253  // m will be a dangling reference to TensorFixedSize, but Eigen only references its static dimensions, so there is no bug
254  return Eigen::TensorCwiseNullaryOp {m, Eigen::internal::scalar_constant_op<Scalar>(value)};
255  }
256 
257 
258  // make_identity_matrix not defined
259 
260 
261  template<typename Arg, typename...Begin, typename...Size>
262  static auto
263  get_slice(Arg&& arg, std::tuple<Begin...> begin, std::tuple<Size...> size)
264  {
265  auto offsets = std::apply([](auto&&...a) {
266  return std::array<std::size_t, sizeof...(Begin)> {std::forward<decltype(a)>(a)...};
267  }, begin);
268  auto extents = std::apply([](auto&&...a) {
269  return std::array<std::size_t, sizeof...(Size)> {std::forward<decltype(a)>(a)...};
270  }, size);
271  return to_native_matrix(std::forward<Arg>(arg)).slice(offsets, extents);
272  }
273 
274 
275 #ifdef __cpp_concepts
276  template<Eigen3::eigen_tensor_general<true> Arg, Eigen3::eigen_tensor_general Block, typename...Begin>
277 #else
278  template<typename Arg, typename Block, typename...Begin, std::enable_if_t<
279  Eigen3::eigen_tensor_general<Arg, true> and Eigen3::eigen_tensor_general<Block>, int> = 0>
280 #endif
281  static constexpr void
282  set_slice(Arg& arg, Block&& block, Begin...begin)
283  {
284  auto offsets = std::array {static_cast<std::size_t>(begin)...};
285  auto extents = std::apply([](auto&&...a) {
286  return std::array {get_dimension(std::forward<decltype(a)>(a))...}; }, all_vector_space_descriptors(block));
287  arg.slice(offsets, extents) = std::forward<Block>(block);
288  }
289 
290  /*
291  private:
292 
293 #ifdef __cpp_concepts
294  template<typename A>
295 #else
296  template<typename A, typename = void>
297 #endif
298  struct pass_through_eigenwrapper : std::false_type {};
299 
300 #ifdef __cpp_concepts
301  template<Eigen3::eigen_wrapper A>
302  struct pass_through_eigenwrapper<A>
303 #else
304  template<typename A>
305  struct pass_through_eigenwrapper<A, std::enable_if_t<Eigen3::eigen_wrapper<A>>>
306 #endif
307  : std::bool_constant<Eigen3::eigen_dense_general<nested_object_of_t<A>> or diagonal_adapter<nested_object_of_t<A>> or
308  triangular_adapter<nested_object_of_t<A>> or hermitian_adapter<nested_object_of_t<A>>> {};
309 
310  public:
311 
312  template<TriangleType t, typename A, typename B>
313  static decltype(auto) set_triangle(A&& a, B&& b)
314  {
315  if constexpr (Eigen3::eigen_MatrixWrapper<A> or Eigen3::eigen_ArrayWrapper<A> or pass_through_eigenwrapper<A>::value)
316  {
317  return internal::set_triangle<t>(nested_object(std::forward<A>(a)), std::forward<B>(b));
318  }
319  else if constexpr (not Eigen3::eigen_dense_general<A>)
320  {
321  return set_triangle<t>(Eigen3::make_eigen_wrapper(std::forward<A>(a)), std::forward<B>(b));
322  }
323  else
324  {
325  if constexpr (t == TriangleType::diagonal)
326  {
327  if constexpr (writable<A> and std::is_lvalue_reference_v<A>)
328  {
329  a.diagonal() = OpenKalman::diagonal_of(std::forward<B>(b));
330  return std::forward<A>(a);
331  }
332  else
333  {
334  auto aw = to_dense_object(std::forward<A>(a));
335  aw.diagonal() = OpenKalman::diagonal_of(std::forward<B>(b));
336  return aw;
337  }
338  }
339  else
340  {
341  decltype(auto) aw = [](A&& a) -> decltype(auto) {
342  if constexpr (writable<A> and std::is_lvalue_reference_v<A>) return std::forward<A>(a);
343  else return to_dense_object(std::forward<A>(a));
344  }(std::forward<A>(a));
345 
346  auto tview = aw.template triangularView<t == TriangleType::upper ? Eigen::Upper : Eigen::Lower>();
347  if constexpr (std::is_assignable_v<decltype(tview), B&&>)
348  tview = std::forward<B>(b);
349  else
350  tview = Eigen3::make_eigen_wrapper(std::forward<B>(b));
351  return std::forward<decltype(aw)>(aw);
352  }
353  }
354  }
355 
356 
357 #ifdef __cpp_concepts
358  template<Eigen3::eigen_dense_general Arg> requires
359  std::is_lvalue_reference_v<nested_object_of_t<Eigen::DiagonalWrapper<std::remove_reference_t<Arg>>>>
360 #else
361  template<typename Arg, std::enable_if_t<Eigen3::eigen_dense_general<Arg> and
362  std::is_lvalue_reference_v<typename nested_object_of<Eigen::DiagonalWrapper<std::remove_reference_t<Arg>>>::type>, int> = 0>
363 #endif
364  static constexpr auto
365  to_diagonal(Arg& arg)
366  {
367  if constexpr (not vector<Arg>) if (not is_vector(arg)) throw std::invalid_argument {
368  "Argument of to_diagonal must have 1 column; instead it has " + std::to_string(get_index_dimension_of<1>(arg))};
369  return Eigen::DiagonalWrapper<std::remove_reference_t<Arg>> {arg};
370  }
371 
372 
373 #ifdef __cpp_concepts
374  template<Eigen3::eigen_SelfAdjointView Arg>
375 #else
376  template<typename Arg, std::enable_if_t<Eigen3::eigen_SelfAdjointView<Arg>, int> = 0>
377 #endif
378  static constexpr auto
379  to_diagonal(Arg&& arg)
380  {
381  // If it is a column vector, the SelfAdjointView wrapper doesn't matter, and otherwise, the following will throw an exception:
382  return OpenKalman::to_diagonal(nested_object(std::forward<Arg>(arg)));
383  }
384 
385 
386 #ifdef __cpp_concepts
387  template<Eigen3::eigen_TriangularView Arg>
388 #else
389  template<typename Arg, std::enable_if_t<Eigen3::eigen_TriangularView<Arg>, int> = 0>
390 #endif
391  static constexpr auto
392  to_diagonal(Arg&& arg)
393  {
394  // If it is a column vector, the TriangularView wrapper doesn't matter, and otherwise, the following will thow an exception:
395  return OpenKalman::to_diagonal(nested_object(std::forward<Arg>(arg)));
396  }
397 
398  private:
399 
400  template<typename Arg, typename Id>
401  static constexpr decltype(auto)
402  diagonal_of_impl(Arg&& arg, Id&& id)
403  {
404  auto diag {[](Arg&& arg){
405  if constexpr (Eigen3::eigen_array_general<Arg, true>)
406  return make_self_contained<Arg>(std::forward<Arg>(arg).matrix().diagonal());
407  else // eigen_matrix_general<Arg, true>
408  return make_self_contained<Arg>(std::forward<Arg>(arg).diagonal());
409  }(std::forward<Arg>(arg))};
410  using Diag = const std::decay_t<decltype(diag)>;
411  static_assert(vector<Diag>);
412 
413  using D = std::conditional_t<dynamic_dimension<Diag, 0> and fixed_pattern<Id>, std::decay_t<Id>, vector_space_descriptor_of_t<Diag, 0>>;
414 
415  if constexpr (not dynamic_dimension<Diag, 0> or dynamic_pattern<D>) return diag;
416  else return internal::FixedSizeAdapter<Diag, D, Dimensions<1>> {std::move(diag)};
417  }
418 
419  public:
420 
421 #ifdef __cpp_concepts
422  template<Eigen3::eigen_general<true> Arg>
423 #else
424  template<typename Arg, std::enable_if_t<Eigen3::eigen_general<Arg, true>, int> = 0>
425 #endif
426  static constexpr decltype(auto)
427  diagonal_of(Arg&& arg)
428  {
429  auto d = is_square_shaped(arg);
430  if (not d) throw std::invalid_argument {"Argument of diagonal_of must be a square matrix; instead it has " +
431  std::to_string(get_index_dimension_of<0>(arg)) + " rows and " +
432  std::to_string(get_index_dimension_of<1>(arg)) + " columns"};
433 
434  using Scalar = scalar_type_of_t<Arg>;
435 
436  if constexpr (Eigen3::eigen_DiagonalWrapper<Arg>)
437  {
438  using Scalar = scalar_type_of_t<Arg>;
439  decltype(auto) diag {nested_object(std::forward<Arg>(arg))}; //< must be nested_object(...) rather than .diagonal() because of const_cast
440  using Diag = decltype(diag);
441  using EigenTraits = Eigen::internal::traits<std::decay_t<Diag>>;
442  constexpr auto rows = EigenTraits::RowsAtCompileTime;
443  constexpr auto cols = EigenTraits::ColsAtCompileTime;
444 
445  static_assert(cols != 1, "For Eigen::DiagonalWrapper<T> interface, T should never be a column vector "
446  "because diagonal_of function handles this case.");
447  if constexpr (cols == 0)
448  {
449  return std::forward<Diag>(diag);
450  }
451  else if constexpr (rows == 1 or rows == 0)
452  {
453  return OpenKalman::transpose(std::forward<Diag>(diag));
454  }
455  else if constexpr (rows == Eigen::Dynamic or cols == Eigen::Dynamic)
456  {
457  auto d {to_dense_object(std::forward<Diag>(diag))};
458  using M = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
459  return M {M::Map(to_dense_object(std::forward<Diag>(diag)).data(),
460  get_index_dimension_of<0>(diag) * get_index_dimension_of<1>(diag))};
461  }
462  else // rows > 1 and cols > 1
463  {
464  using M = Eigen::Matrix<Scalar, rows * cols, 1>;
465  return M {M::Map(to_dense_object(std::forward<Diag>(diag)).data())};
466  }
467  }
468  else if constexpr (Eigen3::eigen_SelfAdjointView<Arg> or Eigen3::eigen_TriangularView<Arg>)
469  {
470  // Assume there are no dangling references
471  return OpenKalman::diagonal_of(nested_object(std::forward<Arg>(arg)));
472  }
473  else if constexpr (Eigen3::eigen_wrapper<Arg>)
474  {
475  if constexpr (Eigen3::eigen_general<nested_object_of_t<Arg>, true>)
476  return OpenKalman::diagonal_of(nested_object(std::forward<Arg>(arg)));
477  else
478  return diagonal_of_impl(std::forward<Arg>(arg), *d);
479  }
480  else if constexpr (Eigen3::eigen_Identity<Arg>)
481  {
482  constexpr std::size_t dim = dynamic_dimension<Arg, 0> ? index_dimension_of_v<Arg, 1> : index_dimension_of_v<Arg, 0>;
483  if constexpr (dim == dynamic_size) return make_constant<Arg, Scalar, 1>(*d, Dimensions<1>{});
484  else return make_constant<Arg, Scalar, 1>(Dimensions<dim>{}, Dimensions<1>{});
485  }
486  else
487  {
488  return diagonal_of_impl(std::forward<Arg>(arg), *d);
489  };
490  }
491 
492  private:
493 
494  template<typename...Ds, typename...ArgDs, typename Arg, std::size_t...I>
495  static decltype(auto)
496  replicate_arg_impl(const std::tuple<Ds...>& p_tup, const std::tuple<ArgDs...>& arg_tup, Arg&& arg, std::index_sequence<I...>)
497  {
498  using R = Eigen::Replicate<std::decay_t<Arg>,
499  (coordinates::dimension_of_v<Ds> == dynamic_size or coordinates::dimension_of_v<ArgDs> == dynamic_size ?
500  Eigen::Dynamic : static_cast<IndexType>(coordinates::dimension_of_v<Ds> / coordinates::dimension_of_v<ArgDs>))...>;
501 
502  if constexpr (((coordinates::dimension_of_v<Ds> != dynamic_size) and ...) and
503  ((coordinates::dimension_of_v<ArgDs> != dynamic_size) and ...))
504  {
505  if constexpr (((coordinates::dimension_of_v<Ds> == coordinates::dimension_of_v<ArgDs>) and ...))
506  return std::forward<Arg>(arg);
507  else
508  return R {std::forward<Arg>(arg)};
509  }
510  else
511  {
512  auto ret = R {std::forward<Arg>(arg), static_cast<IndexType>(
513  get_dimension(std::get<I>(p_tup)) / get_dimension(std::get<I>(arg_tup)))...};
514  return ret;
515  }
516  }
517 
518 
519  template<typename...Ds, typename Arg>
520  static decltype(auto)
521  replicate_arg(const std::tuple<Ds...>& p_tup, Arg&& arg)
522  {
523  return replicate_arg_impl(p_tup, all_vector_space_descriptors(arg), std::forward<Arg>(arg), std::index_sequence_for<Ds...> {});
524  }
525 
526  public:
527 
528 #ifdef __cpp_concepts
529  template<typename...Ds, typename Operation, typename...Args> requires (sizeof...(Args) <= 3) and
530  std::invocable<Operation&&, scalar_type_of_t<Args>...> and
531  values::number<std::invoke_result_t<Operation&&, scalar_type_of_t<Args>...>>
532 #else
533  template<typename...Ds, typename Operation, typename...Args, std::enable_if_t<(sizeof...(Args) <= 3) and
534  std::is_invocable<Operation&&, typename scalar_type_of<Args>::type...>::value and
535  values::number<typename std::invoke_result<Operation&&, typename scalar_type_of<Args>::type...>::type>, int> = 0>
536 #endif
537  static auto
538  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& operation, Args&&...args)
539  {
540  auto&& op = nat_op(std::forward<Operation>(operation));
541  using Op = decltype(op);
542 
543  if constexpr (sizeof...(Args) == 0)
544  {
545  using P = dense_writable_matrix_t<T, Layout::none, scalar_type_of_t<T>, std::tuple<Ds...>>;
546  IndexType r = get_dimension(std::get<0>(tup));
547  IndexType c = get_dimension(std::get<1>(tup));
548  return Eigen::CwiseNullaryOp<std::decay_t<Op>, P> {r, c, std::forward<Op>(op)};
549  }
550  else if constexpr (sizeof...(Args) == 1)
551  {
552  return make_self_contained<Args...>(Eigen::CwiseUnaryOp {
553  replicate_arg(tup, std::forward<Args>(args))..., std::forward<Op>(op)});
554  }
555  else if constexpr (sizeof...(Args) == 2)
556  {
557  return make_self_contained<Args...>(Eigen::CwiseBinaryOp<std::decay_t<Op>,
558  std::decay_t<decltype(replicate_arg(tup, std::forward<Args>(args)))>...> {
559  replicate_arg(tup, std::forward<Args>(args))..., std::forward<Op>(op)});
560  }
561  else
562  {
563  return make_self_contained<Args...>(Eigen::CwiseTernaryOp<std::decay_t<Op>,
564  std::decay_t<decltype(replicate_arg(tup, std::forward<Args>(args)))>...> {
565  replicate_arg(tup, std::forward<Args>(args))..., std::forward<Op>(op)});
566  }
567  }
568  */
569 
570 
571  /*
572  template<std::size_t...indices, typename BinaryFunction, typename Arg>
573  static constexpr auto
574  reduce(BinaryFunction&& b, Arg&& arg)
575  {
576  if constexpr (Eigen3::eigen_dense_general<Arg>)
577  {
578  auto&& op = nat_op(std::forward<BinaryFunction>(b));
579  using Op = decltype(op);
580 
581  if constexpr (sizeof...(indices) == 2) // reduce in both directions
582  {
583  return std::forward<Arg>(arg).redux(std::forward<Op>(op));
584  }
585  else
586  {
587  using OpWrapper = Eigen::internal::member_redux<std::decay_t<Op>, scalar_type_of_t<Arg>>;
588  constexpr auto dir = ((indices == 0) and ...) ? Eigen::Vertical : Eigen::Horizontal;
589  using P = Eigen::PartialReduxExpr<std::decay_t<Arg>, OpWrapper, dir>;
590  return make_self_contained<Arg>(P {std::forward<Arg>(arg), OpWrapper {std::forward<Op>(op)}});
591  }
592  }
593  else
594  {
595  return reduce<indices...>(std::forward<BinaryFunction>(b), Eigen3::make_eigen_wrapper(std::forward<Arg>(arg)));
596  }
597  }
598 
599  // to_euclidean not defined--rely on default
600 
601  // from_euclidean not defined--rely on default
602 
603  // wrap_angles not defined--rely on default
604 
605 
606  template<typename Arg>
607  static constexpr decltype(auto)
608  conjugate(Arg&& arg)
609  {
610  // The global conjugate function already handles Eigen::DiagonalMatrix and Eigen::DiagonalWrapper
611  return std::forward<Arg>(arg).conjugate();
612  }
613 
614 
615  template<typename Arg>
616  static constexpr decltype(auto)
617  transpose(Arg&& arg)
618  {
619  if constexpr (Eigen3::eigen_wrapper<Arg>)
620  {
621  if constexpr (Eigen3::eigen_general<nested_object_of_t<Arg>, true>)
622  return transpose(nested_object(std::forward<Arg>(arg)));
623  else
624  return std::forward<Arg>(arg).transpose(); // Rely on inherited Eigen transpose method
625  }
626  else if constexpr (Eigen3::eigen_matrix_general<Arg, true> or Eigen3::eigen_TriangularView<Arg> or Eigen3::eigen_SelfAdjointView<Arg>)
627  return std::forward<Arg>(arg).transpose();
628  else if constexpr (Eigen3::eigen_array_general<Arg, true>)
629  return std::forward<Arg>(arg).matrix().transpose();
630  else if constexpr (triangular_matrix<Arg>)
631  return OpenKalman::transpose(TriangularAdapter {std::forward<Arg>(arg)});
632  else
633  return Eigen3::make_eigen_wrapper(std::forward<Arg>(arg)).transpose();
634  // Note: the global transpose function already handles zero, constant, constant-diagonal, and symmetric cases.
635  }
636 
637 
638  template<typename Arg>
639  static constexpr decltype(auto)
640  adjoint(Arg&& arg)
641  {
642  if constexpr (Eigen3::eigen_wrapper<Arg>)
643  {
644  if constexpr (Eigen3::eigen_general<nested_object_of_t<Arg>, true>)
645  return adjoint(nested_object(std::forward<Arg>(arg)));
646  else
647  return std::forward<Arg>(arg).adjoint(); // Rely on inherited Eigen adjoint method
648  }
649  else if constexpr (Eigen3::eigen_matrix_general<Arg, true> or Eigen3::eigen_TriangularView<Arg> or Eigen3::eigen_SelfAdjointView<Arg>)
650  return std::forward<Arg>(arg).adjoint();
651  else if constexpr (Eigen3::eigen_array_general<Arg, true>)
652  return std::forward<Arg>(arg).matrix().adjoint();
653  else if constexpr (triangular_matrix<Arg>)
654  return OpenKalman::adjoint(TriangularAdapter {std::forward<Arg>(arg)});
655  else
656  return Eigen3::make_eigen_wrapper(std::forward<Arg>(arg)).adjoint();
657  // Note: the global adjoint function already handles zero, constant, diagonal, non-complex, and hermitian cases.
658  }
659 
660 
661  template<typename Arg>
662  static constexpr auto
663  determinant(Arg&& arg)
664  {
665  if constexpr (Eigen3::eigen_matrix_general<Arg, true>)
666  return std::forward<Arg>(arg).determinant();
667  else if constexpr (Eigen3::eigen_array_general<Arg, true>)
668  return std::forward<Arg>(arg).matrix().determinant();
669  else
670  return Eigen3::make_eigen_wrapper(std::forward<Arg>(arg)).determinant();
671  // Note: the global determinant function already handles Eigen::TriangularView, Eigen::DiagonalMatrix, and Eigen::DiagonalWrapper
672  }
673 
674 
675  template<typename A, typename B>
676  static constexpr auto
677  sum(A&& a, B&& b)
678  {
679  auto s = make_self_contained<A, B>(std::forward<A>(a) + std::forward<B>(b));
680  if constexpr ((dynamic_dimension<decltype(s), 0> and (not dynamic_dimension<A, 0> or not dynamic_dimension<B, 0>)) or
681  (dynamic_dimension<decltype(s), 1> and (not dynamic_dimension<A, 1> or not dynamic_dimension<B, 1>)))
682  {
683  using S0 = vector_space_descriptor_of_t<decltype(s), 0>; using S1 = vector_space_descriptor_of_t<decltype(s), 1>;
684  using A0 = vector_space_descriptor_of_t<A, 0>; using A1 = vector_space_descriptor_of_t<A, 1>;
685  using B0 = vector_space_descriptor_of_t<B, 0>; using B1 = vector_space_descriptor_of_t<B, 1>;
686 
687  using R = std::conditional_t<dynamic_pattern<S0>,
688  std::conditional_t<dynamic_pattern<A0>, std::conditional_t<dynamic_pattern<B0>, S0, B0>, A0>, S0>;
689  using C = std::conditional_t<dynamic_pattern<S1>,
690  std::conditional_t<dynamic_pattern<A1>, std::conditional_t<dynamic_pattern<B1>, S1, B1>, A1>, S1>;
691  return internal::FixedSizeAdapter<std::decay_t<decltype(s)>, R, C> {std::move(s)};
692  }
693  else return s;
694  }
695 
696 
697  template<typename A, typename B>
698  static constexpr auto
699  contract(A&& a, B&& b)
700  {
701  if constexpr (diagonal_adapter<A>)
702  {
703  if constexpr (Eigen3::eigen_DiagonalWrapper<A> or Eigen3::eigen_DiagonalMatrix<A>)
704  return make_self_contained<A, B>(std::forward<A>(a) * OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
705  else
706  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(nested_object(std::forward<A>(a))).asDiagonal() *
707  OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
708  }
709  else if constexpr (diagonal_adapter<B>)
710  {
711  if constexpr (Eigen3::eigen_DiagonalWrapper<B> or Eigen3::eigen_DiagonalMatrix<B>)
712  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(std::forward<A>(a)) * std::forward<B>(b));
713  else
714  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(std::forward<A>(a)) *
715  OpenKalman::to_native_matrix<T>(nested_object(std::forward<B>(b))).asDiagonal());
716  }
717  else if constexpr (triangular_adapter<A>)
718  {
719  constexpr auto uplo = triangular_matrix<A, TriangleType::upper> ? Eigen::Upper : Eigen::Lower;
720  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(nested_object(std::forward<A>(a))).template triangularView<uplo>() *
721  OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
722  }
723  else if constexpr (triangular_adapter<B>)
724  {
725  constexpr auto uplo = triangular_matrix<A, TriangleType::upper> ? Eigen::Upper : Eigen::Lower;
726  auto prod = OpenKalman::to_native_matrix<T>(std::forward<A>(a));
727  prod.applyOnTheRight(OpenKalman::to_native_matrix<T>(nested_object(std::forward<B>(b))).template triangularView<uplo>());
728  return prod;
729  }
730  else if constexpr (hermitian_adapter<A>)
731  {
732  constexpr auto uplo = hermitian_adapter<A, HermitianAdapterType::upper> ? Eigen::Upper : Eigen::Lower;
733  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(nested_object(std::forward<A>(a))).template selfadjointView<uplo>() *
734  OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
735  }
736  else if constexpr (hermitian_adapter<B>)
737  {
738  constexpr auto uplo = triangular_matrix<A, TriangleType::upper> ? Eigen::Upper : Eigen::Lower;
739  auto prod = OpenKalman::to_native_matrix<T>(std::forward<A>(a));
740  prod.applyOnTheRight(OpenKalman::to_native_matrix<T>(nested_object(std::forward<B>(b))).template selfadjointView<uplo>());
741  return prod;
742  }
743  else
744  {
745  return make_self_contained<A, B>(OpenKalman::to_native_matrix<T>(std::forward<A>(a)) *
746  OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
747  }
748  }
749 
750 
751 #ifdef __cpp_concepts
752  template<bool on_the_right, writable A, indexible B> requires Eigen3::eigen_dense_general<A>
753 #else
754  template<bool on_the_right, typename A, typename B, std::enable_if_t<writable<A> and Eigen3::eigen_dense_general<A>,int> = 0>
755 #endif
756  static A&
757  contract_in_place(A& a, B&& b)
758  {
759  auto&& ma = [](A& a){
760  if constexpr (Eigen3::eigen_array_general<A, true>) return a.matrix();
761  else return (a);
762  }(a);
763 
764  if constexpr (on_the_right)
765  return ma.applyOnTheRight(OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
766  else
767  return ma.applyOnTheLeft(OpenKalman::to_native_matrix<T>(std::forward<B>(b)));
768  return a;
769  }
770 
771 
772  template<TriangleType triangle_type, typename A>
773  static constexpr auto
774  cholesky_factor(A&& a)
775  {
776  using NestedMatrix = std::decay_t<nested_object_of_t<A>>;
777  using Scalar = scalar_type_of_t<A>;
778  constexpr auto dim = index_dimension_of_v<A, 0>;
779  using M = dense_writable_matrix_t<A>;
780 
781  if constexpr (std::is_same_v<
782  const NestedMatrix, const typename Eigen::MatrixBase<NestedMatrix>::ConstantReturnType>)
783  {
784  // If nested matrix is a positive constant matrix, construct the Cholesky factor using a shortcut.
785 
786  auto s = nested_object(std::forward<A>(a)).functor()();
787 
788  if (s < Scalar(0))
789  {
790  // Cholesky factor elements are complex, so throw an exception.
791  throw (std::runtime_error("cholesky_factor of constant HermitianAdapter: covariance is indefinite"));
792  }
793 
794  if constexpr(triangle_type == TriangleType::diagonal)
795  {
796  static_assert(diagonal_matrix<A>);
797  auto vec = make_constant<A>(square_root(s), Dimensions<dim>{}, Dimensions<1>{});
798  return DiagonalAdapter<decltype(vec)> {vec};
799  }
800  else if constexpr(triangle_type == TriangleType::lower)
801  {
802  auto col0 = make_constant<A>(square_root(s), Dimensions<dim>{}, Dimensions<1>{});
803  auto othercols = make_zero<A>(get_vector_space_descriptor<0>(a), get_vector_space_descriptor<0>(a) - 1);
804  return TriangularAdapter<M, triangle_type> {concatenate_horizontal(col0, othercols)};
805  }
806  else
807  {
808  static_assert(triangle_type == TriangleType::upper);
809  auto row0 = make_constant<A>(square_root(s), Dimensions<1>{}, Dimensions<dim>{});
810  auto otherrows = make_zero<A>(get_vector_space_descriptor<0>(a) - 1, get_vector_space_descriptor<0>(a));
811  return TriangularAdapter<M, triangle_type> {concatenate_vertical(row0, otherrows)};
812  }
813  }
814  else
815  {
816  // For the general case, perform an LLT Cholesky decomposition.
817  M b;
818  auto LL_x = a.view().llt();
819  if (LL_x.info() == Eigen::Success)
820  {
821  if constexpr(triangle_type == hermitian_adapter_type_of_v<A>)
822  {
823  b = std::move(LL_x.matrixLLT());
824  }
825  else
826  {
827  constexpr unsigned int uplo = triangle_type == TriangleType::upper ? Eigen::Upper : Eigen::Lower;
828  b.template triangularView<uplo>() = LL_x.matrixLLT().adjoint();
829  }
830  }
831  else [[unlikely]]
832  {
833  // If covariance is not positive definite, use the more robust LDLT decomposition.
834  auto LDL_x = nested_object(std::forward<A>(a)).ldlt();
835  if ((not LDL_x.isPositive() and not LDL_x.isNegative()) or LDL_x.info() != Eigen::Success) [[unlikely]]
836  {
837  if (LDL_x.isPositive() and LDL_x.isNegative()) // Covariance is zero, even though decomposition failed.
838  {
839  if constexpr(triangle_type == TriangleType::lower)
840  b.template triangularView<Eigen::Lower>() = make_zero(nested_object(a));
841  else
842  b.template triangularView<Eigen::Upper>() = make_zero(nested_object(a));
843  }
844  else // Covariance is indefinite, so throw an exception.
845  {
846  throw (std::runtime_error("cholesky_factor of HermitianAdapter: covariance is indefinite"));
847  }
848  }
849  else if constexpr(triangle_type == TriangleType::lower)
850  {
851  b.template triangularView<Eigen::Lower>() =
852  LDL_x.matrixL().toDenseMatrix() * LDL_x.vectorD().cwiseSqrt().asDiagonal();
853  }
854  else
855  {
856  b.template triangularView<Eigen::Upper>() =
857  LDL_x.vectorD().cwiseSqrt().asDiagonal() * LDL_x.matrixU().toDenseMatrix();
858  }
859  }
860  return TriangularAdapter<M, triangle_type> {std::move(b)};
861  }
862  }
863 
864 
865  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
866  static decltype(auto)
867  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
868  {
869  if constexpr (OpenKalman::Eigen3::eigen_ArrayWrapper<A>)
870  {
871  return rank_update_hermitian<significant_triangle>(nested_object(std::forward<A>(a)), std::forward<U>(u), alpha);
872  }
873  else if constexpr (OpenKalman::Eigen3::eigen_array_general<A, true>)
874  {
875  return rank_update_hermitian<significant_triangle>(std::forward<A>(a).matrix(), std::forward<U>(u), alpha);
876  }
877  else
878  {
879  static_assert(writable<A>);
880  constexpr auto s = significant_triangle == HermitianAdapterType::lower ? Eigen::Lower : Eigen::Upper;
881  a.template selfadjointView<s>().template rankUpdate(std::forward<U>(u), alpha);
882  return std::forward<A>(a);
883  }
884  }
885 
886 
887  template<TriangleType triangle, typename A, typename U, typename Alpha>
888  static decltype(auto)
889  rank_update_triangular(A&& a, U&& u, const Alpha alpha)
890  {
891  if constexpr (OpenKalman::Eigen3::eigen_ArrayWrapper<A>)
892  {
893  return rank_update_triangular<triangle>(nested_object(std::forward<A>(a)), std::forward<U>(u), alpha);
894  }
895  else if constexpr (OpenKalman::Eigen3::eigen_array_general<A, true>)
896  {
897  return rank_update_triangular<triangle>(std::forward<A>(a).matrix(), std::forward<U>(u), alpha);
898  }
899  else
900  {
901  static_assert(writable<A>);
902  constexpr auto t = triangle == TriangleType::lower ? Eigen::Lower : Eigen::Upper;
903  using Scalar = scalar_type_of_t<A>;
904  for (std::size_t i = 0; i < get_index_dimension_of<1>(u); i++)
905  {
906  if (Eigen::internal::llt_inplace<Scalar, t>::rankUpdate(a, get_chip<1>(u, i), alpha) >= 0)
907  throw (std::runtime_error("rank_update_triangular: product is not positive definite"));
908  }
909  return std::forward<A>(a);
910  }
911  }
912 
913 
914  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
915  static constexpr auto
916  solve(A&& a, B&& b)
917  {
918  using Scalar = scalar_type_of_t<A>;
919 
920  constexpr std::size_t a_rows = dynamic_dimension<A, 0> ? index_dimension_of_v<B, 0> : index_dimension_of_v<A, 0>;
921  constexpr std::size_t a_cols = index_dimension_of_v<A, 1>;
922  constexpr std::size_t b_cols = index_dimension_of_v<B, 1>;
923 
924  if constexpr (not Eigen3::eigen_matrix_general<A, true>)
925  {
926  auto&& n = OpenKalman::to_native_matrix(std::forward<A>(a));
927  static_assert(Eigen3::eigen_matrix_general<decltype(n), true>);
928  return solve<must_be_unique, must_be_exact>(std::forward<decltype(n)>(n), std::forward<B>(b));
929  }
930  else if constexpr (triangular_matrix<A>)
931  {
932  constexpr auto uplo = triangular_matrix<A, TriangleType::upper> ? Eigen::Upper : Eigen::Lower;
933  return make_self_contained<A, B>(
934  Eigen::Solve {std::forward<A>(a).template triangularView<uplo>(), std::forward<B>(b)});
935  }
936  else if constexpr (hermitian_matrix<A>)
937  {
938  constexpr auto uplo = hermitian_adapter_type_of_v<A> == TriangleType::upper ? Eigen::Upper : Eigen::Lower;
939  auto v {std::forward<A>(a).template selfadjointView<uplo>()};
940  auto llt {v.llt()};
941 
942  Eigen3::eigen_matrix_t<Scalar, a_cols, b_cols> ret;
943  if (llt.info() == Eigen::Success)
944  {
945  ret = Eigen::Solve {llt, std::forward<B>(b)};
946  }
947  else [[unlikely]]
948  {
949  // A is semidefinite. Use LDLT decomposition instead.
950  auto ldlt {v.ldlt()};
951  if ((not ldlt.isPositive() and not ldlt.isNegative()) or ldlt.info() != Eigen::Success)
952  {
953  throw (std::runtime_error("Eigen solve (hermitian case): A is indefinite"));
954  }
955  ret = Eigen::Solve {ldlt, std::forward<B>(b)};
956  }
957  return ret;
958  }
959  else
960  {
961  if constexpr (must_be_exact or must_be_unique or true)
962  {
963  auto a_cols_rt = get_index_dimension_of<1>(a);
964  Eigen::ColPivHouseholderQR<Eigen3::eigen_matrix_t<Scalar, a_rows, a_cols>> QR {std::forward<A>(a)};
965  if constexpr (must_be_unique)
966  {
967  if (QR.rank() < a_cols_rt) throw std::runtime_error {"solve function requests a "
968  "unique solution, but A is rank-deficient, so result X is not unique"};
969  }
970 
971  auto res = QR.solve(std::forward<B>(b));
972 
973  if constexpr (must_be_exact)
974  {
975  bool a_solution_exists = (a*res).isApprox(b, a_cols_rt * std::numeric_limits<scalar_type_of_t<A>>::epsilon());
976 
977  if (a_solution_exists)
978  return make_self_contained(std::move(res));
979  else
980  throw std::runtime_error {"solve function requests an exact solution, "
981  "but the solution is only an approximation"};
982  }
983  else
984  {
985  return make_self_contained(std::move(res));
986  }
987  }
988  else
989  {
990  Eigen::HouseholderQR<Eigen3::eigen_matrix_t<Scalar, a_rows, a_cols>> QR {std::forward<A>(a)};
991  return make_self_contained(QR.solve(std::forward<B>(b)));
992  }
993  }
994  }
995 
996  private:
997 
998  template<typename A>
999  static constexpr auto
1000  QR_decomp_impl(A&& a)
1001  {
1002  using Scalar = scalar_type_of_t<A>;
1003  constexpr auto rows = index_dimension_of_v<A, 0>;
1004  constexpr auto cols = index_dimension_of_v<A, 1>;
1005  using MatrixType = Eigen3::eigen_matrix_t<Scalar, rows, cols>;
1006  using ResultType = Eigen3::eigen_matrix_t<Scalar, cols, cols>;
1007 
1008  Eigen::HouseholderQR<MatrixType> QR {std::forward<A>(a)};
1009 
1010  if constexpr (dynamic_dimension<A, 1>)
1011  {
1012  auto rt_cols = get_index_dimension_of<1>(a);
1013 
1014  ResultType ret {rt_cols, rt_cols};
1015 
1016  if constexpr (dynamic_dimension<A, 0>)
1017  {
1018  auto rt_rows = get_index_dimension_of<0>(a);
1019 
1020  if (rt_rows < rt_cols)
1021  ret << QR.matrixQR().topRows(rt_rows),
1022  Eigen3::eigen_matrix_t<Scalar, dynamic_size, dynamic_size>::Zero(rt_cols - rt_rows, rt_cols);
1023  else
1024  ret = QR.matrixQR().topRows(rt_cols);
1025  }
1026  else
1027  {
1028  if (rows < rt_cols)
1029  ret << QR.matrixQR().template topRows<rows>(),
1030  Eigen3::eigen_matrix_t<Scalar, dynamic_size, dynamic_size>::Zero(rt_cols - rows, rt_cols);
1031  else
1032  ret = QR.matrixQR().topRows(rt_cols);
1033  }
1034 
1035  return ret;
1036  }
1037  else
1038  {
1039  ResultType ret;
1040 
1041  if constexpr (dynamic_dimension<A, 0>)
1042  {
1043  auto rt_rows = get_index_dimension_of<0>(a);
1044 
1045  if (rt_rows < cols)
1046  ret << QR.matrixQR().topRows(rt_rows),
1047  Eigen3::eigen_matrix_t<Scalar, dynamic_size, dynamic_size>::Zero(cols - rt_rows, cols);
1048  else
1049  ret = QR.matrixQR().template topRows<cols>();
1050  }
1051  else
1052  {
1053  if constexpr (rows < cols)
1054  ret << QR.matrixQR().template topRows<rows>(), Eigen3::eigen_matrix_t<Scalar, cols - rows, cols>::Zero();
1055  else
1056  ret = QR.matrixQR().template topRows<cols>();
1057  }
1058 
1059  return ret;
1060  }
1061  }
1062 
1063  public:
1064 
1065  template<typename A>
1066  static constexpr auto
1067  LQ_decomposition(A&& a)
1068  {
1069  return make_triangular_matrix<TriangleType::lower>(make_self_contained(adjoint(QR_decomp_impl(adjoint(std::forward<A>(a))))));
1070  }
1071 
1072 
1073  template<typename A>
1074  static constexpr auto
1075  QR_decomposition(A&& a)
1076  {
1077  return make_triangular_matrix<TriangleType::upper>(QR_decomp_impl(std::forward<A>(a)));
1078  }*/
1079 
1080  };
1081 
1082 
1083 } // namespace OpenKalman::interface
1084 
1085 #endif //OPENKALMAN_EIGEN_TENSOR_LIBRARY_INTERFACE_HPP
Definition: basics.hpp:41
Forward declarations for OpenKalman&#39;s Eigen Tensor module interface.
Row-major storage (C or C++ style): contiguous storage in which the right-most index has a stride of ...
constexpr bool constant_matrix
Specifies that all components of an object are the same constant value.
Definition: constant_matrix.hpp:31
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 get_slice(Arg &&arg, const std::tuple< Offset... > &offsets, const std::tuple< Extent... > &extents)
Extract a slice from a matrix or tensor.
Definition: get_slice.hpp:101
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
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
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
constexpr auto to_number(Arg arg)
Convert any values::value to a values::number.
Definition: to_number.hpp:34
decltype(auto) constexpr all_vector_space_descriptors(const T &t)
Return a collection of coordinates::pattern objects associated with T.
Definition: all_vector_space_descriptors.hpp:52
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:37
Column-major storage (Fortran, Matlab, or Eigen style): contiguous storage in which the left-most ext...
Definition: eigen-tensor-forward-declarations.hpp:30
A generalization of the above: a custom stride is specified for each index.
constexpr bool size
T is either an index representing a size, or void which represents that there is no size...
Definition: size.hpp:32
Arg && fill_components(Arg &&arg, S...s)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: fill_components.hpp:44
Layout
The layout format of a multidimensional array.
Definition: global-definitions.hpp:47
constexpr bool dynamic
T is a values::value that is not determinable at compile time.
Definition: dynamic.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
constexpr To && assign(To &&a, From &&b)
Assign a writable object from an indexible object.
Definition: assign.hpp:51
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
constexpr Arg && set_slice(Arg &&arg, Block &&block, const Begin &...begin)
Assign an object to a particular slice of a matrix or tensor.
Definition: set_slice.hpp:56