OpenKalman
DiagonalAdapter.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-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_DIAGONALADAPTER_HPP
17 #define OPENKALMAN_DIAGONALADAPTER_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 DiagonalAdapter : OpenKalman::internal::AdapterBase<DiagonalAdapter<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 DiagonalAdapter() requires std::default_initializable<NestedObject> and (not has_dynamic_dimensions<NestedObject>)
59 #else
60  template<typename T = NestedObject, std::enable_if_t<
61  std::is_default_constructible_v<T> and (not has_dynamic_dimensions<NestedObject>), int> = 0>
62  constexpr DiagonalAdapter()
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<DiagonalAdapter, 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<DiagonalAdapter, std::decay_t<Arg>>) and std::is_constructible_v<NestedObject, Arg&&>, int> = 0>
76 #endif
77  constexpr explicit DiagonalAdapter(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_coefficient<NestedObject>::value != constant_diagonal_coefficient<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<DiagonalAdapter, 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<DiagonalAdapter, 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 DiagonalAdapter assignment operator has non-matching vector space descriptors."};
116 
117  if constexpr (constant_matrix<NestedObject>)
118  {
120  if (values::to_number(this->nested_object()) != values::to_number(diagonal_of(std::forward<Arg>(arg))))
121  throw std::invalid_argument {"Argument to constant_diagonal DiagonalAdapter 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_vector_space_descriptor<0>(this->nested_object()) == get_vector_space_descriptor<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_vector_space_descriptor<0>(this->nested_object()) == get_vector_space_descriptor<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<std::is_convertible_v<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<std::is_convertible_v<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 DiagonalAdapter<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>, DiagonalAdapter>
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>, DiagonalAdapter>
223 #else
224  template<typename Arg, typename S, std::enable_if_t<
225  std::is_same_v<std::decay_t<Arg>, DiagonalAdapter> and std::is_convertible_v<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>, DiagonalAdapter>
235 #else
236  template<typename Arg, typename S, std::enable_if_t<
237  std::is_same_v<std::decay_t<Arg>, DiagonalAdapter> and std::is_convertible_v<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>, DiagonalAdapter>
247 #else
248  template<typename Arg, typename S, std::enable_if_t<
249  std::is_same_v<std::decay_t<Arg>, DiagonalAdapter> and std::is_convertible_v<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 DiagonalAdapter(Arg&&) -> DiagonalAdapter<Arg>;
272 
273 
274  // ------------------------- //
275  // Interfaces //
276  // ------------------------- //
277 
278  namespace interface
279  {
280  template<typename 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_vector_space_descriptor(Arg&& arg, N)
292  {
293  return OpenKalman::get_vector_space_descriptor<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_coefficient {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  template<TriangleType t>
321  static constexpr bool is_triangular = true;
322 
323 
324  static constexpr bool is_writable = false;
325 
326 
327 #ifdef __cpp_lib_concepts
328  template<typename Arg> requires OpenKalman::one_dimensional<nested_object_of_t<Arg&>> and directly_accessible<nested_object_of_t<Arg&>>
329 #else
330  template<typename Arg, std::enable_if_t<one_dimensional<typename nested_object_of<Arg&>::type> and
331  directly_accessible<typename nested_object_of<Arg&>::type>, int> = 0>
332 #endif
333  static constexpr auto * const
334  raw_data(Arg& arg) { return internal::raw_data(nested_object(arg)); }
335 
336 
337  static constexpr Layout layout = OpenKalman::one_dimensional<NestedObject> ? layout_of_v<NestedObject> : Layout::none;
338 
339  };
340 
341  } // namespace interface
342 
343 } // OpenKalman
344 
345 
346 
347 #endif //OPENKALMAN_DIAGONALADAPTER_HPP
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
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
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
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
Definition: AdapterBase.hpp:36
constexpr auto to_number(Arg arg)
Convert any values::value to a values::number.
Definition: to_number.hpp:34
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 bool vector_space_descriptors_match(const Ts &...ts)
Return true if every set of coordinates::pattern of a set of objects match.
Definition: vector_space_descriptors_match.hpp:62
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
constexpr auto & operator=(Arg &&arg)
Assign from another diagonal_matrix.
Definition: DiagonalAdapter.hpp:109
Layout
The layout format of a multidimensional array.
Definition: global-definitions.hpp:47
constexpr DiagonalAdapter()
Default constructor.
Definition: DiagonalAdapter.hpp:62
constexpr bool fixed
T is a values::value that is determinable at compile time.
Definition: fixed.hpp:60
constexpr DiagonalAdapter(Arg &&arg)
Construct from a vector, matrix, or other tensor reflecting the diagonal.
Definition: DiagonalAdapter.hpp:77
An adapter for creating a diagonal matrix or tensor.
Definition: DiagonalAdapter.hpp:27
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