OpenKalman
adapters-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) 2019-2021 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_ADAPTERS_INTERFACE_HPP
17 #define OPENKALMAN_ADAPTERS_INTERFACE_HPP
18 
19 #include <complex>
20 
21 
22 namespace OpenKalman::interface
23 {
24 
25 #ifdef __cpp_concepts
26  template<typename T> requires OpenKalman::internal::diagonal_expr<T> or OpenKalman::internal::triangular_expr<T> or OpenKalman::internal::hermitian_expr<T>
27  struct library_interface<T>
28 #else
29  template<typename T>
30  struct library_interface<T, std::enable_if_t<OpenKalman::internal::diagonal_expr<T> or OpenKalman::internal::triangular_expr<T> or OpenKalman::internal::hermitian_expr<T>>>
31 #endif
32  : library_interface<std::decay_t<nested_object_of_t<T>>>
33  {
34  private:
35 
36  using Nested = std::decay_t<nested_object_of_t<T>>;
38 
39  public:
40 
41  template<typename Derived>
42  using LibraryBase = internal::library_base_t<Derived, Nested>;
43 
44 
45 #ifdef __cpp_lib_ranges
46  template<indexible Arg, std::ranges::input_range Indices> requires
47  std::convertible_to<std::ranges::range_value_t<Indices>, const typename std::decay_t<Arg>::Index>
48 #else
49  template<typename Arg, typename Indices>
50 #endif
51  static constexpr scalar_type_of_t<Arg>
52  get_component(Arg&& arg, const Indices& indices)
53  {
54  using Scalar = scalar_type_of_t<Arg>;
55 #ifdef __cpp_lib_ranges
56  namespace ranges = std::ranges;
57 #endif
58  auto it = ranges::begin(indices);
59  std::size_t i {*it};
60  std::size_t j {++it == ranges::end(indices) ? 1 : *it};
61 
62  if constexpr (OpenKalman::internal::diagonal_expr<Arg>)
63  {
64  if (i == j)
65  return OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), indices);
66  else
67  return static_cast<Scalar>(0);
68  }
69  else if constexpr (OpenKalman::internal::triangular_expr<Arg>)
70  {
71  if (triangular_matrix<Arg, TriangleType::lower> ? i >= j : j >= i)
72  return OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), indices);
73  else
74  return static_cast<Scalar>(0);
75  }
76  else
77  {
78  static_assert(OpenKalman::internal::hermitian_expr<Arg>);
79 
80  if constexpr (values::complex<Scalar>)
81  {
82  if (i == j)
83  return values::real(OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), indices));
84  else if (hermitian_adapter<Arg, HermitianAdapterType::lower> ? i > j : j > i)
85  return OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), indices);
86  else
87  return values::conj(OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), j, i));
88  }
89  else
90  {
91  if (hermitian_adapter<Arg, HermitianAdapterType::lower> ? i >= j : j >= i)
92  return OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), indices);
93  else
94  return OpenKalman::get_component(OpenKalman::nested_object(std::forward<Arg>(arg)), j, i);
95  }
96  }
97  }
98 
99 
100 #ifdef __cpp_lib_ranges
101  template<indexible Arg, std::ranges::input_range Indices> requires (not std::is_const_v<Arg>) and
102  std::convertible_to<std::ranges::range_value_t<Indices>, const typename Arg::Index>
103 #else
104  template<typename Arg, typename Indices, std::enable_if_t<(not std::is_const_v<Arg>), int> = 0>
105 #endif
106  static void
107  set_component(Arg& arg, const scalar_type_of_t<Arg>& s, const Indices& indices)
108  {
109  using Scalar = scalar_type_of_t<Arg>;
110 #ifdef __cpp_lib_ranges
111  namespace ranges = std::ranges;
112 #endif
113  auto it = ranges::begin(indices);
114  std::size_t i {*it};
115  std::size_t j {++it == ranges::end(indices) ? 1 : *it};
116 
117  if constexpr (OpenKalman::internal::diagonal_expr<Arg>)
118  {
119  if (i == j)
120  OpenKalman::set_component(arg, s, indices);
121  else
122  if (s != static_cast<Scalar>(0)) throw std::out_of_range("Cannot set non-diagonal element of a diagonal matrix to a non-zero value.");
123  }
124  else if constexpr (OpenKalman::internal::triangular_expr<Arg>)
125  {
126  if (triangular_matrix<Arg, TriangleType::lower> ? i >= j : j >= i)
127  OpenKalman::set_component(arg, s, indices);
128  else
129  if (s != static_cast<Scalar>(0)) throw std::out_of_range("Cannot set elements of a triangular matrix to non-zero values outside the triangle.");
130  }
131  else
132  {
133  static_assert(OpenKalman::internal::hermitian_expr<Arg>);
134 
135  if (hermitian_adapter<Arg, HermitianAdapterType::lower> ? i >= j : j >= i)
136  OpenKalman::set_component(arg, s, indices);
137  else
139  }
140  }
141 
142 
143  // to_native_matrix inherited
144 
145  // make_default inherited
146 
147  // fill_components inherited
148 
149  // make_constant inherited
150 
151  // make_identity_matrix inherited
152 
153  // make_triangular_matrix inherited
154 
155  // make_hermitian_adapter inherited
156 
157 
158  template<typename Arg, typename...Begin, typename...Size>
159  static auto
160  get_slice(Arg&& arg, std::tuple<Begin...> begin, std::tuple<Size...> size)
161  {
162  auto dense = to_dense_object<scalar_type_of_t<Arg>>(std::forward<Arg>(arg));
163  static_assert(not OpenKalman::internal::diagonal_expr<decltype(dense)> and not OpenKalman::internal::triangular_expr<decltype(dense)> and
164  not OpenKalman::internal::hermitian_expr<decltype(dense)>);
165  return OpenKalman::get_slice(std::move(dense), begin, size);
166  }
167 
168 
169  template<typename Arg, typename Block, typename...Begin>
170  static constexpr Arg&
171  set_slice(Arg& arg, Block&& block, Begin...begin) = delete;
172 
173 
174  // set_triangle not defined because it is handled by global set_triangle function
175 
176 
177  template<typename Arg>
178  static decltype(auto)
179  to_diagonal(Arg&& arg)
180  {
181  // Note: the interface only needs to handle constant and dynamic-sized zero matrices.
182  return DiagonalAdapter {std::forward<Arg>(arg)};
183  }
184 
185 
186  template<typename Arg>
187  static decltype(auto)
188  diagonal_of(Arg&& arg)
189  {
190  // Note: the global diagonal_of function already handles all zero and constant cases.
191  if constexpr (OpenKalman::internal::diagonal_expr<Arg>) return nested_object(std::forward<Arg>(arg));
192  else return OpenKalman::diagonal_of(nested_object(std::forward<Arg>(arg)));
193  }
194 
195 
196  // broadcast
197 
198 
199  template<typename...Ds, typename Operation, typename...Args>
200  static constexpr decltype(auto)
201  n_ary_operation(const std::tuple<Ds...>& tup, Operation&& op, Args&&...args)
202  {
204  return Traits::n_ary_operation(tup, std::forward<Operation>(op), std::forward<Args>(args)...);
205  }
206 
207 
208  template<std::size_t...indices, typename BinaryFunction, typename Arg>
209  static constexpr decltype(auto)
210  reduce(BinaryFunction&& b, Arg&& arg)
211  {
213  return Traits::template reduce<indices...>(std::forward<BinaryFunction>(b), std::forward<Arg>(arg));
214  }
215 
216  // to_euclidean not defined
217 
218  // from_euclidean not defined
219 
220  // wrap_angles not defined
221 
222  template<typename Arg>
223  static constexpr decltype(auto)
224  conjugate(Arg&& arg)
225  {
226  if constexpr (OpenKalman::internal::hermitian_expr<Arg>)
227  {
228  constexpr auto t = hermitian_adapter_type_of_v<Arg>;
229  return make_hermitian_matrix<t>(OpenKalman::conjugate(nested_object(std::forward<Arg>(arg))));
230  }
231  else
232  {
233  static_assert(OpenKalman::internal::triangular_expr<Arg>);
234  constexpr auto t = triangle_type_of_v<Arg>;
235  return make_triangular_matrix<t>(OpenKalman::conjugate(nested_object(std::forward<Arg>(arg))));
236  }
237  // Global conjugate function already handles DiagonalAdapter
238  }
239 
240 
241  template<typename Arg>
242  static constexpr decltype(auto)
243  transpose(Arg&& arg)
244  {
245  if constexpr (OpenKalman::internal::hermitian_expr<Arg>)
246  {
248  {
249  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
250  }
251  else
252  {
253  constexpr auto t = (hermitian_adapter<Arg, HermitianAdapterType::lower> ? TriangleType::upper : TriangleType::lower);
254  return make_hermitian_matrix<t>(OpenKalman::transpose(nested_object(std::forward<Arg>(arg))));
255  }
256  }
257  else
258  {
259  static_assert(OpenKalman::internal::triangular_expr<Arg>);
261  {
262  return OpenKalman::transpose(nested_object(std::forward<Arg>(arg)));
263  }
264  else
265  {
266  constexpr auto t = triangular_matrix<Arg, TriangleType::lower> ? TriangleType::upper : TriangleType::lower;
267  return make_triangular_matrix<t>(OpenKalman::transpose(nested_object(std::forward<Arg>(arg))));
268  }
269  }
270  // Global transpose function already handles DiagonalAdapter
271  }
272 
273 
274  template<typename Arg>
275  static constexpr decltype(auto)
276  adjoint(Arg&& arg)
277  {
278  // Global conjugate function already handles HermitianAdapter and DiagonalAdapter
279  static_assert(OpenKalman::internal::triangular_expr<Arg>);
280 
281  constexpr auto t = triangular_matrix<Arg, TriangleType::lower> ? TriangleType::upper : TriangleType::lower;
282  return make_triangular_matrix<t>(OpenKalman::adjoint(nested_object(std::forward<Arg>(arg))));
283  }
284 
285 
286  template<typename Arg>
287  static constexpr auto
288  determinant(Arg&& arg)
289  {
290  // The general determinant function already handles TriangularAdapter and DiagonalAdapter.
291  static_assert(OpenKalman::internal::hermitian_expr<Arg>);
292  return OpenKalman::determinant(to_dense_object(std::forward<Arg>(arg)));
293  }
294 
295 
296  template<typename Arg, typename...Args>
297  static constexpr auto
298  sum(Arg&& arg, Args&&...args)
299  {
300  return library_interface<Nested>::sum(std::forward<Arg>(arg), std::forward<Args>(args)...);
301  }
302 
303 
304  template<typename A, typename B>
305  static constexpr auto
306  contract(A&& a, B&& b)
307  {
308  return library_interface<std::decay_t<nested_object_of_t<T>>>::contract(std::forward<A>(a), std::forward<B>(b));
309  }
310 
311 
312  // contract_in_place
313 
314 
315  template<TriangleType triangle_type, typename A>
316  static constexpr auto
317  cholesky_factor(A&& a)
318  {
319  static_assert(not OpenKalman::internal::diagonal_expr<A>); // DiagonalAdapter case should be handled by cholesky_factor function
320 
321  if constexpr (OpenKalman::internal::hermitian_expr<A>)
322  {
324  if constexpr (hermitian_adapter<A, h>)
325  return cholesky_factor<triangle_type>(nested_object(std::forward<A>(a)));
326  else
327  return cholesky_factor<triangle_type>(adjoint(nested_object(std::forward<A>(a))));
328  }
329  else
330  {
331  static_assert(OpenKalman::internal::triangular_expr<A>);
332  if constexpr (triangular_matrix<A, triangle_type>)
333  return OpenKalman::cholesky_factor<triangle_type>(nested_object(std::forward<A>(a)));
334  else
335  return OpenKalman::cholesky_factor<triangle_type>(to_diagonal(diagonal_of(std::forward<A>(a))));
336  }
337  }
338 
339 
340  template<HermitianAdapterType significant_triangle, typename A, typename U, typename Alpha>
341  static decltype(auto)
342  rank_update_hermitian(A&& a, U&& u, const Alpha alpha)
343  {
344  auto&& n = nested_object(std::forward<A>(a));
346  if constexpr (OpenKalman::internal::hermitian_expr<A>)
347  {
348  auto&& m = Trait::template rank_update_hermitian<significant_triangle>(std::forward<decltype(n)>(n), std::forward<U>(u), alpha);
349  return make_hermitian_matrix<significant_triangle>(std::forward<decltype(m)>(m));
350  }
351  else
352  {
353  static_assert(OpenKalman::internal::diagonal_expr<A>);
354  return Trait::template rank_update_hermitian<significant_triangle>(to_native_matrix(std::forward<A>(a)), std::forward<U>(u), alpha);
355  }
356  }
357 
358 
359  template<TriangleType triangle, typename A, typename U, typename Alpha>
360  static decltype(auto)
361  rank_update_triangular(A&& a, U&& u, const Alpha alpha)
362  {
363  static_assert(OpenKalman::internal::diagonal_expr<A>);
364  using N = std::decay_t<nested_object_of_t<T>>;
365  return library_interface<N>::template rank_update_triangular<triangle>(std::forward<A>(a), std::forward<U>(u), alpha);
366  }
367 
368 
369  template<bool must_be_unique, bool must_be_exact, typename A, typename B>
370  static constexpr decltype(auto)
371  solve(A&& a, B&& b)
372  {
373  using N = std::decay_t<nested_object_of_t<T>>;
374  return library_interface<N>::template solve<must_be_unique, must_be_exact>(to_native_matrix(std::forward<A>(a)), std::forward<B>(b));
375  }
376 
377 
378  template<typename A>
379  static constexpr decltype(auto)
380  LQ_decomposition(A&& a)
381  {
382  using N = std::decay_t<nested_object_of_t<T>>;
383  return library_interface<N>::LQ_decomposition(to_native_matrix(std::forward<A>(a)));
384  }
385 
386 
387  template<typename A>
388  static constexpr decltype(auto)
389  QR_decomposition(A&& a)
390  {
391  using N = std::decay_t<nested_object_of_t<T>>;
392  return library_interface<N>::QR_decomposition(to_native_matrix(std::forward<A>(a)));
393  }
394 
395  };
396 
397 
398 } // namespace OpenKalman::interface
399 
400 
401 #endif //OPENKALMAN_ADAPTERS_INTERFACE_HPP
typename nested_object_of< T >::type nested_object_of_t
Helper type for nested_object_of.
Definition: nested_object_of.hpp:66
constexpr auto n_ary_operation(const std::tuple< Ds... > &d_tup, Operation &&operation, Args &&...args)
Perform a component-wise n-ary operation, using broadcasting to match the size of a pattern matrix...
Definition: n_ary_operation.hpp:319
decltype(auto) constexpr contract(A &&a, B &&b)
Matrix multiplication of A * B.
Definition: contract.hpp:54
Definition: basics.hpp:41
decltype(auto) rank_update_hermitian(A &&a, U &&u, scalar_type_of_t< A > alpha=1)
Do a rank update on a hermitian matrix.
Definition: rank_update_hermitian.hpp:45
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 conjugate(Arg &&arg)
Take the conjugate of a matrix.
Definition: conjugate.hpp:33
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
decltype(auto) constexpr QR_decomposition(A &&a)
Perform a QR decomposition of matrix A=Q[U,0], U is a upper-triangular matrix, and Q is orthogonal...
Definition: QR_decomposition.hpp:33
Definition: tuple_reverse.hpp:103
HermitianAdapterType
The type of a hermitian adapter, indicating which triangle of the nested matrix is used...
Definition: global-definitions.hpp:78
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.
decltype(auto) constexpr reduce(BinaryFunction &&b, Arg &&arg)
Perform a partial reduction based on an associative binary function, across one or more indices...
Definition: reduce.hpp:143
constexpr bool triangular_matrix
Specifies that a type is a triangular matrix (upper, lower, or diagonal).
Definition: triangular_matrix.hpp:37
decltype(auto) constexpr transpose(Arg &&arg)
Take the transpose of a matrix.
Definition: transpose.hpp:58
An interface to various routines from the linear algebra library associated with indexible object T...
Definition: library_interface.hpp:37
constexpr auto solve(A &&a, B &&b)
Solve the equation AX = B for X, which may or may not be a unique solution.
Definition: solve.hpp:87
constexpr auto conj(const Arg &arg)
A constexpr function for the complex conjugate of a (complex) number.
Definition: conj.hpp:39
constexpr auto determinant(Arg &&arg)
Take the determinant of a matrix.
Definition: determinant.hpp:44
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 real(Arg arg)
A constexpr function to obtain the real part of a (complex) number.
Definition: real.hpp:40
constexpr bool size
T is either an index representing a size, or void which represents that there is no size...
Definition: size.hpp:32
decltype(auto) constexpr adjoint(Arg &&arg)
Take the adjoint of a matrix.
Definition: adjoint.hpp:33
decltype(auto) constexpr sum(Ts &&...ts)
Element-by-element sum of one or more objects.
Definition: sum.hpp:112
decltype(auto) constexpr LQ_decomposition(A &&a)
Perform an LQ decomposition of matrix A=[L,0]Q, L is a lower-triangular matrix, and Q is orthogonal...
Definition: LQ_decomposition.hpp:33
decltype(auto) rank_update_triangular(A &&a, U &&u, scalar_type_of_t< A > alpha=1)
Do a rank update on triangular matrix.
Definition: rank_update_triangular.hpp:48
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
An adapter for creating a diagonal matrix or tensor.
Definition: DiagonalAdapter.hpp:27
A lower-left triangular matrix.
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:448
constexpr bool hermitian_matrix
Specifies that a type is a hermitian matrix (assuming it is square_shaped).
Definition: hermitian_matrix.hpp:50
decltype(auto) constexpr cholesky_factor(A &&a)
Take the Cholesky factor of a matrix.
Definition: cholesky_factor.hpp:38
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