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