OpenKalman
TriangularAdapter.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) 2019-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_TRIANGULARADAPTER_HPP
17 #define OPENKALMAN_TRIANGULARADAPTER_HPP
18 
19 namespace OpenKalman
20 {
21 #ifdef __cpp_concepts
22  template<square_shaped<Applicability::permitted> NestedObject, TriangleType triangle_type>
23  requires (index_count_v<NestedObject> <= 2)
24 #else
25  template<typename NestedObject, TriangleType triangle_type>
26 #endif
27  struct TriangularAdapter : OpenKalman::internal::AdapterBase<TriangularAdapter<NestedObject, triangle_type>, NestedObject>
28  {
29 
30 #ifndef __cpp_concepts
31  static_assert(square_shaped<NestedObject, Applicability::permitted>);
32  static_assert(index_count_v<NestedObject> <= 2);
33 #endif
34 
35  private:
36 
38 
39  static constexpr auto dim = dynamic_dimension<NestedObject, 0> ? index_dimension_of_v<NestedObject, 1> :
40  index_dimension_of_v<NestedObject, 0>;
41 
42  template<typename Arg>
43  static bool constexpr dimensions_match = dimension_size_of_index_is<Arg, 0, dim, Applicability::permitted> and
44  dimension_size_of_index_is<Arg, 1, dim, Applicability::permitted>;
45 
46  public:
47 
48  using Scalar = scalar_type_of_t<NestedObject>;
49 
50 
52 #ifdef __cpp_concepts
53  TriangularAdapter() requires std::default_initializable<NestedObject> and (not has_dynamic_dimensions<NestedObject>)
54 #else
55  template<typename T = NestedObject, std::enable_if_t<
56  std::is_default_constructible_v<T> and (not has_dynamic_dimensions<NestedObject>), int> = 0>
58 #endif
59  : Base {} {}
60 
61 
63 #ifdef __cpp_concepts
64  template<triangular_adapter Arg> requires (not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and
65  triangular_matrix<Arg, triangle_type> and (not diagonal_matrix<NestedObject>) and
66  dimensions_match<nested_object_of_t<Arg>> and
67 # if OPENKALMAN_CPP_FEATURE_CONCEPTS
68  requires(Arg&& arg) { NestedObject {nested_object(std::forward<Arg>(arg))}; } //-- not accepted in GCC 10.1.0
69 # else
70  std::constructible_from<NestedObject, decltype(nested_object(std::declval<Arg&&>()))>
71 # endif
72 #else
73  template<typename Arg, std::enable_if_t<triangular_adapter<Arg> and (not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and
74  (triangular_matrix<Arg, triangle_type>) and (not diagonal_matrix<NestedObject>) and
75  dimensions_match<typename nested_object_of<Arg>::type> and
76  std::is_constructible<NestedObject, decltype(nested_object(std::declval<Arg&&>()))>::value, int> = 0>
77 #endif
78  TriangularAdapter(Arg&& arg) : Base {nested_object(std::forward<Arg>(arg))} {}
79 
80 
82 #ifdef __cpp_concepts
83  template<square_shaped<Applicability::permitted> Arg> requires (not triangular_matrix<Arg, triangle_type>) and
84  (not diagonal_matrix<NestedObject>) and dimensions_match<Arg> and std::constructible_from<NestedObject, Arg&&>
85 #else
86  template<typename Arg, std::enable_if_t<square_shaped<Arg, Applicability::permitted> and
87  (not triangular_matrix<Arg, triangle_type>) and (not diagonal_matrix<NestedObject>) and
88  dimensions_match<Arg> and std::is_constructible_v<NestedObject, Arg&&>, int> = 0>
89 #endif
90  explicit TriangularAdapter(Arg&& arg) : Base {
91  [](Arg&& arg) -> decltype(auto) {
92  if constexpr (has_dynamic_dimensions<Arg>) if (not is_square_shaped(arg)) throw std::invalid_argument {
93  "Argument to TriangularAdapter must be a square matrix, but the argument has dimensions " +
94  std::to_string(get_index_dimension_of<0>(arg)) + "×" + std::to_string(get_index_dimension_of<1>(arg)) +
95  " in " + __func__ + " at line " + std::to_string(__LINE__) + " of " + __FILE__};
96  return std::forward<Arg>(arg);
97  }(std::forward<Arg>(arg))
98  } {}
99 
100 
102 #ifdef __cpp_concepts
103  template<triangular_matrix<triangle_type> Arg> requires (not triangular_adapter<Arg>) and
104  (not has_nested_object<Arg> or (diagonal_matrix<NestedObject> and diagonal_matrix<Arg>)) and
105  dimensions_match<Arg> and std::constructible_from<NestedObject, Arg&&>
106 #else
107  template<typename Arg, std::enable_if_t<triangular_matrix<Arg, triangle_type> and (not triangular_adapter<Arg>) and
108  (not has_nested_object<Arg> or (diagonal_matrix<NestedObject> and diagonal_matrix<Arg>)) and
109  dimensions_match<Arg> and std::is_constructible<NestedObject, Arg&&>::value, int> = 0>
110 #endif
111  TriangularAdapter(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
112 
113 
115 #ifdef __cpp_concepts
116  template<diagonal_matrix Arg> requires (not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and
117  diagonal_matrix<NestedObject> and dimensions_match<Arg> and (not std::constructible_from<NestedObject, Arg&&>) and
118  requires(Arg&& arg) { NestedObject {diagonal_of(std::forward<Arg>(arg))}; }
119 #else
120  template<typename Arg, std::enable_if_t<(not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and
121  diagonal_matrix<NestedObject> and dimensions_match<Arg> and (not std::is_constructible_v<NestedObject, Arg&&>) and
122  std::is_constructible<NestedObject, decltype(diagonal_of(std::declval<Arg&&>()))>::value, int> = 0>
123 #endif
124  TriangularAdapter(Arg&& arg) : Base {diagonal_of(std::forward<Arg>(arg))} {}
125 
126 
128 #ifdef __cpp_concepts
129  template<square_shaped<Applicability::permitted> Arg> requires (not triangular_matrix<Arg>) and diagonal_matrix<NestedObject> and
130  dimensions_match<Arg> and requires(Arg&& arg) { NestedObject {diagonal_of(std::forward<Arg>(arg))}; }
131 #else
132  template<typename Arg, std::enable_if_t<square_shaped<Arg, Applicability::permitted> and
133  (not triangular_matrix<Arg>) and diagonal_matrix<NestedObject> and
134  dimensions_match<Arg> and std::is_constructible<NestedObject, decltype(diagonal_of(std::declval<Arg&&>()))>::value, int> = 0>
135 #endif
136  explicit TriangularAdapter(Arg&& arg) : Base {
137  [](Arg&& arg) -> decltype(auto) {
138  if constexpr (has_dynamic_dimensions<Arg>) if (not is_square_shaped(arg)) throw std::invalid_argument {
139  "Argument to TriangularAdapter must be a square matrix, but the argument has dimensions " +
140  std::to_string(get_index_dimension_of<0>(arg)) + "×" + std::to_string(get_index_dimension_of<1>(arg)) +
141  " in " + __func__ + " at line " + std::to_string(__LINE__) + " of " + __FILE__};
142  return diagonal_of(std::forward<Arg>(arg));
143  }(std::forward<Arg>(arg))
144  } {}
145 
146 
153 #ifdef __cpp_concepts
154  template<std::convertible_to<const Scalar>...Args> requires (sizeof...(Args) > 0) and
155  (triangle_type != TriangleType::diagonal) and (not diagonal_matrix<NestedObject>) and
156  requires(Args...args) { NestedObject {make_dense_object_from<NestedObject>(static_cast<const Scalar>(args)...)}; }
157 #else
158  template<typename...Args, std::enable_if_t<std::conjunction_v<std::is_convertible<Args, const Scalar>...> and
159  (sizeof...(Args) > 0) and (triangle_type != TriangleType::diagonal) and (not diagonal_matrix<NestedObject>), int> = 0>
160 #endif
161  TriangularAdapter(Args...args)
162  : Base {make_dense_object_from<NestedObject>(static_cast<const Scalar>(args)...)} {}
163 
164 
169 #if defined(__cpp_concepts) and OPENKALMAN_CPP_FEATURE_CONCEPTS
170  template<std::convertible_to<const Scalar>...Args> requires (sizeof...(Args) > 0) and
171  (triangle_type == TriangleType::diagonal or diagonal_matrix<NestedObject>) and
172  requires(Args ... args) { NestedObject {
173  to_diagonal(make_dense_object_from<NestedObject>(
174  std::tuple<Dimensions<sizeof...(Args)>, Dimensions<1>>{}, static_cast<const Scalar>(args)...))}; }
175 #else
176  template<typename ... Args, std::enable_if_t<std::conjunction_v<std::is_convertible<Args, const Scalar>...> and
177  (sizeof...(Args) > 0) and (triangle_type == TriangleType::diagonal or diagonal_matrix<NestedObject>), int> = 0>
178 #endif
179  TriangularAdapter(Args...args) : Base {to_diagonal(make_dense_object_from<NestedObject>(
180  std::tuple<Dimensions<sizeof...(Args)>, Dimensions<1>>{}, static_cast<const Scalar>(args)...))} {}
181 
182 
184 #ifdef __cpp_concepts
185  template<triangular_matrix<triangle_type> Arg> requires
186  (not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and
187  vector_space_descriptors_may_match_with<NestedObject, Arg> and
190  (not (diagonal_matrix<NestedObject> or triangle_type == TriangleType::diagonal) or diagonal_matrix<Arg>)
191 #else
192  template<typename Arg, std::enable_if_t<triangular_matrix<Arg, triangle_type> and
193  (not std::is_base_of_v<TriangularAdapter, std::decay_t<Arg>>) and vector_space_descriptors_may_match_with<NestedObject, Arg> and
194  (not constant_diagonal_matrix<NestedObject> or constant_diagonal_matrix<Arg>) and
195  (not (diagonal_matrix<NestedObject> or triangle_type == TriangleType::diagonal) or diagonal_matrix<Arg>), int> = 0>
196 #endif
197  auto& operator=(Arg&& arg)
198  {
199  if constexpr (not constant_diagonal_matrix<NestedObject>)
200  internal::set_triangle<triangle_type>(this->nested_object(), std::forward<Arg>(arg));
201  return *this;
202  }
203 
204 
205 #ifdef __cpp_concepts
206  template<vector_space_descriptors_may_match_with<NestedObject> Arg, TriangleType t>
207 #else
208  template<typename Arg, TriangleType t, std::enable_if_t<vector_space_descriptors_may_match_with<Arg, NestedObject>, int> = 0>
209 #endif
210  auto& operator+=(const TriangularAdapter<Arg, triangle_type>& arg)
211  {
212  internal::set_triangle<triangle_type>(this->nested_object(), this->nested_object() + std::forward<Arg>(arg));
213  return *this;
214  }
215 
216 
217 #ifdef __cpp_concepts
218  template<vector_space_descriptors_may_match_with<NestedObject> Arg, TriangleType t>
219 #else
220  template<typename Arg, TriangleType t, std::enable_if_t<vector_space_descriptors_may_match_with<Arg, NestedObject>, int> = 0>
221 #endif
222  auto& operator-=(const TriangularAdapter<Arg, triangle_type>& arg)
223  {
224  internal::set_triangle<triangle_type>(this->nested_object(), this->nested_object() - std::forward<Arg>(arg));
225  return *this;
226  }
227 
228 
229 #ifdef __cpp_concepts
230  template<std::convertible_to<Scalar> S>
231 #else
232  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar>, int> = 0>
233 #endif
234  auto& operator*=(const S s)
235  {
236  internal::set_triangle<triangle_type>(this->nested_object(), scalar_product(this->nested_object(), s));
237  return *this;
238  }
239 
240 
241 #ifdef __cpp_concepts
242  template<std::convertible_to<Scalar> S>
243 #else
244  template<typename S, std::enable_if_t<std::is_convertible_v<S, Scalar>, int> = 0>
245 #endif
246  auto& operator/=(const S s)
247  {
248  internal::set_triangle<triangle_type>(this->nested_object(), scalar_quotient(this->nested_object(), s));
249  return *this;
250  }
251 
252 
253 #ifdef __cpp_concepts
254  template<vector_space_descriptors_may_match_with<NestedObject> Arg>
255 #else
256  template<typename Arg, std::enable_if_t<vector_space_descriptors_may_match_with<Arg, NestedObject>, int> = 0>
257 #endif
258  auto& operator*=(const TriangularAdapter<Arg, triangle_type>& arg)
259  {
261  return *this;
262  }
263 
264 
265 #ifdef __cpp_concepts
266  template<typename Arg> requires std::same_as<std::decay_t<Arg>, TriangularAdapter>
267  friend decltype(auto) operator-(Arg&& arg)
268  {
269  return make_triangular_matrix<triangle_type_of_v<Arg>>(-nested_object(std::forward<Arg>(arg)));
270  }
271 #else
272  decltype(auto) operator-() const&
273  {
274  return make_triangular_matrix<triangle_type>(-nested_object(*this));
275  }
276 
277  decltype(auto) operator-() const&&
278  {
279  return make_triangular_matrix<triangle_type>(-nested_object(std::move(*this)));
280  }
281 #endif
282 
283 
284 #ifdef __cpp_concepts
285  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, TriangularAdapter>
286 #else
287  template<typename Arg, typename S, std::enable_if_t<
288  std::is_same_v<std::decay_t<Arg>, TriangularAdapter> and std::is_convertible_v<S, const scalar_type_of_t<Arg>>>>
289 #endif
290  friend decltype(auto) operator*(Arg&& arg, S s)
291  {
292  return make_triangular_matrix<triangle_type_of_v<Arg>>(nested_object(std::forward<Arg>(arg)) * s);
293  }
294 
295 
296 #ifdef __cpp_concepts
297  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, TriangularAdapter>
298 #else
299  template<typename Arg, typename S, std::enable_if_t<
300  std::is_same_v<std::decay_t<Arg>, TriangularAdapter> and std::is_convertible_v<S, const scalar_type_of_t<Arg>>>>
301 #endif
302  friend decltype(auto) operator*(S s, Arg&& arg)
303  {
304  return make_triangular_matrix<triangle_type_of_v<Arg>>(s * nested_object(std::forward<Arg>(arg)));
305  }
306 
307 
308 #ifdef __cpp_concepts
309  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, TriangularAdapter>
310 #else
311  template<typename Arg, typename S, std::enable_if_t<
312  std::is_same_v<std::decay_t<Arg>, TriangularAdapter> and std::is_convertible_v<S, const scalar_type_of_t<Arg>>>>
313 #endif
314  friend decltype(auto) operator/(Arg&& arg, S s)
315  {
316  return make_triangular_matrix<triangle_type_of_v<Arg>>(nested_object(std::forward<Arg>(arg)) / s);
317  }
318 
319  };
320 
321 
322  // ------------------------------- //
323  // Deduction Guides //
324  // ------------------------------- //
325 
326 #ifdef __cpp_concepts
327  template<triangular_matrix M>
328 #else
329  template<typename M, std::enable_if_t<triangular_matrix<M>, int> = 0>
330 #endif
332  std::conditional_t<triangular_adapter<M>, nested_object_of_t<M>, M>,
333  triangle_type_of_v<M>>;
334 
335 
336 #ifdef __cpp_concepts
337  template<hermitian_matrix<Applicability::permitted> M> requires (not triangular_matrix<M>)
338 #else
339  template<typename M, std::enable_if_t<hermitian_matrix<M, Applicability::permitted> and
340  (not triangular_matrix<M>), int> = 0>
341 #endif
342  explicit TriangularAdapter(M&&) -> TriangularAdapter<
343  std::conditional_t<hermitian_adapter<M>, nested_object_of_t<M>, M>,
344  hermitian_adapter<M, HermitianAdapterType::upper> ? TriangleType::upper : TriangleType::lower>;
345 
346 
347 #ifdef __cpp_concepts
348  template<indexible M> requires (not triangular_matrix<M>) and
349  (not hermitian_matrix<M, Applicability::permitted>)
350 #else
351  template<typename M, std::enable_if_t<indexible<M> and (not triangular_matrix<M>) and
352  (not hermitian_matrix<M, Applicability::permitted>), int> = 0>
353 #endif
355 
356 
357  // ------------------------- //
358  // Interfaces //
359  // ------------------------- //
360 
361  namespace interface
362  {
363  template<typename NestedObject, TriangleType triangle_type>
364  struct indexible_object_traits<TriangularAdapter<NestedObject, triangle_type>>
365  {
366  using scalar_type = scalar_type_of_t<NestedObject>;
367 
368 
369  template<typename Arg>
370  static constexpr auto count_indices(const Arg& arg) { return std::integral_constant<std::size_t, 2>{}; }
371 
372 
373  template<typename Arg, typename N>
374  static constexpr auto get_vector_space_descriptor(Arg&& arg, N n)
375  {
376  return internal::best_vector_space_descriptor(
377  OpenKalman::get_vector_space_descriptor<0>(nested_object(arg)),
378  OpenKalman::get_vector_space_descriptor<1>(nested_object(arg)));
379  }
380 
381 
382  template<typename Arg>
383  static decltype(auto) nested_object(Arg&& arg)
384  {
385  return std::forward<Arg>(arg).nested_object();
386  }
387 
388 
389  template<typename Arg>
390  static constexpr auto get_constant_diagonal(const Arg& arg)
391  {
392  if constexpr (triangle_type == TriangleType::diagonal and not diagonal_matrix<NestedObject>)
394  else
396  }
397 
398 
399  template<Applicability b>
400  static constexpr bool one_dimensional = OpenKalman::one_dimensional<NestedObject, b>;
401 
402 
403  template<Applicability b>
404  static constexpr bool is_square = OpenKalman::square_shaped<NestedObject, b>;
405 
406 
407  template<TriangleType t>
408  static constexpr bool is_triangular = t == TriangleType::any or triangle_type == TriangleType::diagonal or triangle_type == t or
409  triangular_matrix<NestedObject, t>;
410 
411 
412  static constexpr bool is_triangular_adapter = true;
413 
414 
415  static constexpr bool is_writable = false;
416 
417 
418 #ifdef __cpp_lib_concepts
419  template<typename Arg> requires OpenKalman::one_dimensional<nested_object_of_t<Arg&>> and raw_data_defined_for<nested_object_of_t<Arg&>>
420 #else
421  template<typename Arg, std::enable_if_t<one_dimensional<typename nested_object_of<Arg&>::type> and
422  raw_data_defined_for<typename nested_object_of<Arg&>::type>, int> = 0>
423 #endif
424  static constexpr auto * const
425  raw_data(Arg& arg) { return internal::raw_data(OpenKalman::nested_object(arg)); }
426 
427 
428  static constexpr Layout layout = OpenKalman::one_dimensional<NestedObject> ? layout_of_v<NestedObject> : Layout::none;
429 
430  };
431 
432  } // namespace interface
433 
434 } // namespace OpenKalman
435 
436 
437 #endif //OPENKALMAN_TRIANGULARADAPTER_HPP
438 
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
TriangularAdapter(Arg &&arg)
Construct from a non-triangular or square matrix if NestedObject is non-diagonal. ...
Definition: TriangularAdapter.hpp:90
constexpr NestedObject & nested_object() &
Get the nested object.
Definition: AdapterBase.hpp:97
constexpr bool one_dimensional
Specifies that a type is one-dimensional in every index.
Definition: one_dimensional.hpp:83
Definition: indexible_object_traits.hpp:36
TriangularAdapter()
Default constructor.
Definition: TriangularAdapter.hpp:57
auto & operator=(Arg &&arg)
Assign from another triangular_matrix.
Definition: TriangularAdapter.hpp:197
TriangularAdapter(Arg &&arg)
Construct from a triangular adapter if NestedObject is non-diagonal.
Definition: TriangularAdapter.hpp:78
No storage layout (e.g., if the elements are calculated rather than stored).
typename scalar_type_of< T >::type scalar_type_of_t
helper template for scalar_type_of.
Definition: scalar_type_of.hpp:54
TriangularAdapter(Args...args)
Construct from a list of scalar coefficients, in row-major order.
Definition: TriangularAdapter.hpp:161
A triangular_adapter, where components above or below the diagonal (or both) are zero.
Definition: forward-class-declarations.hpp:257
Lower, upper, or diagonal matrix.
constexpr auto is_square_shaped(const T &t)
Determine whether an object is square_shaped at runtime.
Definition: is_square_shaped.hpp:63
Definition: tuple_reverse.hpp:103
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
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
An upper-right triangular matrix.
Definition: AdapterBase.hpp:36
The constant associated with T, assuming T is a constant_matrix.
Definition: constant_coefficient.hpp:36
The root namespace for OpenKalman.
Definition: basics.hpp:34
The constant associated with T, assuming T is a constant_diagonal_matrix.
Definition: constant_diagonal_coefficient.hpp:32
constexpr A && contract_in_place(A &&a, B &&b)
In-place matrix multiplication of A * B, storing the result in A.
Definition: contract_in_place.hpp:38
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
Layout
The layout format of a multidimensional array.
Definition: global-definitions.hpp:47
constexpr bool has_dynamic_dimensions
Specifies that T has at least one index with dynamic dimensions.
Definition: has_dynamic_dimensions.hpp:29
A diagonal matrix (both a lower-left and an upper-right triangular matrix).
constexpr bool fixed
T is a values::value that is determinable at compile time.
Definition: fixed.hpp:60
decltype(auto) constexpr nested_object(Arg &&arg)
Retrieve a nested object of Arg, if it exists.
Definition: nested_object.hpp:34
A lower-left triangular matrix.
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