OpenKalman
diagonal_adapter.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) 2020-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_DIAGONAL_ADAPTER_HPP
17 #define OPENKALMAN_DIAGONAL_ADAPTER_HPP
18 
19 namespace OpenKalman
20 {
21 
22 #ifdef __cpp_concepts
23  template<vector<0, applicability::permitted> NestedObject>
24 #else
25  template<typename NestedObject>
26 #endif
27  struct diagonal_adapter : OpenKalman::internal::AdapterBase<diagonal_adapter<NestedObject>, NestedObject>
28  {
29 
30 #ifndef __cpp_concepts
31  static_assert(vector<NestedObject, 0, applicability::permitted>);
32 #endif
33 
34  private:
35 
37 
38  static constexpr auto dim = index_dimension_of_v<NestedObject, 0>;
39 
40 #ifndef __cpp_concepts
41  template<typename T, typename = void>
42  struct is_constructible_from_diagonal : std::false_type {};
43 
44  template<typename T>
45  struct is_constructible_from_diagonal<T, std::void_t<decltype(NestedObject {diagonal_of(std::declval<T>())})>>
46  : std::true_type {};
47 #endif
48 
49  public:
50 
51  using Scalar = scalar_type_of_t<NestedObject>;
52 
53 
57 #ifdef __cpp_concepts
58  constexpr diagonal_adapter() requires std::default_initializable<NestedObject> and (not has_dynamic_dimensions<NestedObject>)
59 #else
60  template<bool Enable = true, std::enable_if_t<Enable and
61  stdex::default_initializable<NestedObject> and (not has_dynamic_dimensions<NestedObject>), int> = 0>
62  constexpr diagonal_adapter()
63 #endif
64  : Base {} {}
65 
66 
70 #ifdef __cpp_concepts
71  template<vector_space_descriptors_may_match_with<NestedObject> Arg> requires
72  (not std::is_base_of_v<diagonal_adapter, std::decay_t<Arg>>) and std::constructible_from<NestedObject, Arg&&>
73 #else
74  template<typename Arg, std::enable_if_t<vector_space_descriptors_may_match_with<Arg, NestedObject> and
75  (not std::is_base_of_v<diagonal_adapter, std::decay_t<Arg>>) and stdex::constructible_from<NestedObject, Arg&&>, int> = 0>
76 #endif
77  constexpr explicit diagonal_adapter(Arg&& arg) : Base {std::forward<Arg>(arg)} {}
78 
79 
80 #ifndef __cpp_concepts
81  private:
82 
83  template<typename Arg, typename = void>
84  struct constants_match : std::true_type {};
85 
86  template<typename Arg>
87  struct constants_match<Arg, std::enable_if_t<
88  constant_value<NestedObject>::value != constant_diagonal_value<Arg>::value>>
89  : std::false_type {};
90 
91  public:
92 #endif
93 
94 
96 #ifdef __cpp_concepts
97  template<diagonal_matrix Arg> requires (not std::is_base_of_v<diagonal_adapter, std::decay_t<Arg>>) and
98  vector_space_descriptors_may_match_with<NestedObject, decltype(diagonal_of(std::declval<Arg>()))> and
99  (not constant_matrix<NestedObject> or constant_diagonal_matrix<Arg>) and
101  std::assignable_from<std::add_lvalue_reference_t<NestedObject>, decltype(diagonal_of(std::declval<Arg>()))>
102 #else
103  template<typename Arg, std::enable_if_t<
104  diagonal_matrix<Arg> and (not std::is_base_of<diagonal_adapter, std::decay_t<Arg>>::value) and
105  vector_space_descriptors_may_match_with<NestedObject, decltype(diagonal_of(std::declval<Arg>()))> and
106  (not constant_matrix<NestedObject> or constant_diagonal_matrix<Arg>) and constants_match<Arg>::value and
107  std::is_assignable<std::add_lvalue_reference_t<NestedObject>, decltype(diagonal_of(std::declval<Arg>()))>::value, int> = 0>
108 #endif
109  constexpr auto& operator=(Arg&& arg)
110  {
111  using Arg_diag = decltype(diagonal_of(std::declval<Arg>()));
112 
113  if constexpr (not vector_space_descriptors_match_with<NestedObject, Arg_diag>)
114  if (not vector_space_descriptors_match(this->nested_object(), diagonal_of(std::declval<Arg>())))
115  throw std::invalid_argument {"Argument to diagonal_adapter assignment operator has non-matching vector space descriptors."};
116 
117  if constexpr (constant_matrix<NestedObject>)
118  {
119  if constexpr (not values::fixed<constant_value<NestedObject>> or not values::fixed<constant_diagonal_value<Arg>>)
120  if (values::to_value_type(this->nested_object()) != values::to_value_type(diagonal_of(std::forward<Arg>(arg))))
121  throw std::invalid_argument {"Argument to constant_diagonal diagonal_adapter assignment operator has non-matching constant value."};
122  }
123  else
124  {
125  this->nested_object() = diagonal_of(std::forward<Arg>(arg));
126  }
127  return *this;
128  }
129 
130 
131 #ifdef __cpp_concepts
132  template<diagonal_matrix Arg>
133  requires (dynamic_dimension<Arg, 0> or dynamic_dimension<NestedObject, 0> or index_dimension_of_v<Arg, 0> == dim)
134 #else
135  template<typename Arg, std::enable_if_t<diagonal_matrix<Arg> and
136  (dynamic_dimension<Arg, 0> or dynamic_dimension<NestedObject, 0> or index_dimension_of<Arg, 0>::value == dim), int> = 0>
137 #endif
138  auto& operator+=(Arg&& arg)
139  {
140  if constexpr (dynamic_dimension<NestedObject, 0>)
141  assert(get_pattern_collection<0>(this->nested_object()) == get_pattern_collection<0>(arg));
142 
143  this->nested_object() += diagonal_of(std::forward<Arg>(arg));
144  return *this;
145  }
146 
147 
148 #ifdef __cpp_concepts
149  template<diagonal_matrix Arg>
150  requires (dynamic_dimension<Arg, 0> or dynamic_dimension<NestedObject, 0> or index_dimension_of_v<Arg, 0> == dim)
151 #else
152  template<typename Arg, std::enable_if_t<diagonal_matrix<Arg> and
153  (dynamic_dimension<Arg, 0> or dynamic_dimension<NestedObject, 0> or index_dimension_of<Arg, 0>::value == dim), int> = 0>
154 #endif
155  auto& operator-=(Arg&& arg)
156  {
157  if constexpr (dynamic_dimension<NestedObject, 0>)
158  assert(get_pattern_collection<0>(this->nested_object()) == get_pattern_collection<0>(arg));
159 
160  this->nested_object() -= diagonal_of(std::forward<Arg>(arg));
161  return *this;
162  }
163 
164 
165 #ifdef __cpp_concepts
166  template<std::convertible_to<Scalar> S>
167 #else
168  template<typename S, std::enable_if_t<stdex::convertible_to<S, Scalar>, int> = 0>
169 #endif
170  auto& operator*=(const S s)
171  {
172  this->nested_object() *= s;
173  return *this;
174  }
175 
176 
177 #ifdef __cpp_concepts
178  template<std::convertible_to<Scalar> S>
179 #else
180  template<typename S, std::enable_if_t<stdex::convertible_to<S, Scalar>, int> = 0>
181 #endif
182  auto& operator/=(const S s)
183  {
184  this->nested_object() /= s;
185  return *this;
186  }
187 
188 
189 #ifdef __cpp_concepts
190  template<typename Arg> requires (index_dimension_of_v<Arg, 0> == dim)
191 #else
193 #endif
194  auto& operator*=(const diagonal_adapter<Arg>& arg)
195  {
196  static_assert(index_dimension_of_v<Arg, 0> == dim);
197  this->nested_object() = this->nested_object().array() * arg.nested_object().array();
198  return *this;
199  }
200 
201 
202 #ifdef __cpp_concepts
203  template<typename Arg> requires std::same_as<std::decay_t<Arg>, diagonal_adapter>
204  friend decltype(auto) operator-(Arg&& arg)
205  {
206  return to_diagonal(-nested_object(std::forward<Arg>(arg)));
207  }
208 #else
209  decltype(auto) operator-() const&
210  {
211  return to_diagonal(-nested_object(*this));
212  }
213 
214  decltype(auto) operator-() const&&
215  {
216  return to_diagonal(-nested_object(std::move(*this)));
217  }
218 #endif
219 
220 
221 #ifdef __cpp_concepts
222  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, diagonal_adapter>
223 #else
224  template<typename Arg, typename S, std::enable_if_t<
225  std::is_same_v<std::decay_t<Arg>, diagonal_adapter> and stdex::convertible_to<S, const scalar_type_of_t<Arg>>>>
226 #endif
227  friend decltype(auto) operator*(Arg&& arg, S s)
228  {
229  return to_diagonal(scalar_product(nested_object(std::forward<Arg>(arg)), s));
230  }
231 
232 
233 #ifdef __cpp_concepts
234  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, diagonal_adapter>
235 #else
236  template<typename Arg, typename S, std::enable_if_t<
237  std::is_same_v<std::decay_t<Arg>, diagonal_adapter> and stdex::convertible_to<S, const scalar_type_of_t<Arg>>>>
238 #endif
239  friend decltype(auto) operator*(S s, Arg&& arg)
240  {
241  return to_diagonal(scalar_product(nested_object(std::forward<Arg>(arg)), s));
242  }
243 
244 
245 #ifdef __cpp_concepts
246  template<typename Arg, std::convertible_to<const scalar_type_of_t<Arg>> S> requires std::same_as<std::decay_t<Arg>, diagonal_adapter>
247 #else
248  template<typename Arg, typename S, std::enable_if_t<
249  std::is_same_v<std::decay_t<Arg>, diagonal_adapter> and stdex::convertible_to<S, const scalar_type_of_t<Arg>>>>
250 #endif
251  friend decltype(auto) operator/(Arg&& arg, S s)
252  {
253  return to_diagonal(scalar_quotient(nested_object(std::forward<Arg>(arg)), s));
254  }
255 
256  };
257 
258 
259  // ------------------------------- //
260  // Deduction guides //
261  // ------------------------------- //
262 
266 #ifdef __cpp_concepts
267  template<indexible Arg>
268 #else
269  template<typename Arg, std::enable_if_t<indexible<Arg>, int> = 0>
270 #endif
271  explicit diagonal_adapter(Arg&&) -> diagonal_adapter<Arg>;
272 
273 
274  // ------------------------- //
275  // Interfaces //
276  // ------------------------- //
277 
278  namespace interface
279  {
280  template<typename NestedObject>
281  struct object_traits<diagonal_adapter<NestedObject>>
282  {
283  using scalar_type = scalar_type_of_t<NestedObject>;
284 
285 
286  template<typename Arg>
287  static constexpr auto count_indices(const Arg& arg) { return std::integral_constant<std::size_t, 2>{}; }
288 
289 
290  template<typename Arg, typename N>
291  static constexpr auto get_pattern_collection(Arg&& arg, N)
292  {
293  return OpenKalman::get_pattern_collection<0>(std::forward<Arg>(arg).nested_object());
294  }
295 
296 
297  template<typename Arg>
298  static decltype(auto) nested_object(Arg&& arg)
299  {
300  return std::forward<Arg>(arg).nested_object();
301  }
302 
303  // get_constant(const Arg& arg) not defined
304 
305  template<typename Arg>
306  static constexpr auto get_constant_diagonal(const Arg& arg)
307  {
308  return constant_value {arg.nested_object()};
309  }
310 
311 
312  template<applicability b>
313  static constexpr bool one_dimensional = OpenKalman::one_dimensional<NestedObject, b>;
314 
315 
316  template<applicability b>
317  static constexpr bool is_square = true;
318 
319 
320  static constexpr triangle_type triangle_type_value = triangle_type::diagonal;
321 
322 
323  static constexpr bool is_writable = false;
324 
325 
326 #ifdef __cpp_lib_concepts
327  template<typename Arg> requires OpenKalman::one_dimensional<nested_object_of_t<Arg&>> and directly_accessible<nested_object_of_t<Arg&>>
328 #else
329  template<typename Arg, std::enable_if_t<one_dimensional<typename nested_object_of<Arg&>::type> and
330  directly_accessible<typename nested_object_of<Arg&>::type>, int> = 0>
331 #endif
332  static constexpr auto * const
333  raw_data(Arg& arg) { return internal::raw_data(nested_object(arg)); }
334 
335 
336  static constexpr data_layout layout = OpenKalman::one_dimensional<NestedObject> ? layout_of_v<NestedObject> : data_layout::none;
337 
338  };
339 
340  }
341 
342 }
343 
344 
345 
346 #endif
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
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 to_value_type(Arg &&arg)
Convert, if necessary, a fixed or dynamic value to its underlying base type.
Definition: to_value_type.hpp:28
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).
Definition: AdapterBase.hpp:37
The root namespace for OpenKalman.
Definition: basics.hpp:34
An adapter for creating a diagonal matrix or tensor.
Definition: diagonal_adapter.hpp:27
Definition: object_traits.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
constexpr diagonal_adapter()
Default constructor.
Definition: diagonal_adapter.hpp:62
constexpr auto & operator=(Arg &&arg)
Assign from another diagonal_matrix.
Definition: diagonal_adapter.hpp:109
constexpr diagonal_adapter(Arg &&arg)
Construct from a vector, matrix, or other tensor reflecting the diagonal.
Definition: diagonal_adapter.hpp:77
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
constexpr bool fixed
T is a value that is determinable at compile time.
Definition: fixed.hpp:66