lapacke_helpers.h 6.81 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2021 Erik Schultheis <erik.schultheis@aalto.fi>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_LAPACKE_HELPERS_H
#define EIGEN_LAPACKE_HELPERS_H

// IWYU pragma: private
#include "./InternalHeaderCheck.h"

#ifdef EIGEN_USE_MKL
#include "mkl_lapacke.h"
#else
#include "lapacke.h"
#endif

namespace Eigen {
namespace internal {
/**
 * \internal
 * \brief Implementation details and helper functions for the lapacke glue code.
 */
namespace lapacke_helpers {

// ---------------------------------------------------------------------------------------------------------------------
//                  Translation from Eigen to Lapacke for types and constants
// ---------------------------------------------------------------------------------------------------------------------

// For complex numbers, the types in Eigen and Lapacke are different, but layout compatible.
template <typename Scalar>
struct translate_type_imp;
template <>
struct translate_type_imp<float> {
  using type = float;
};
template <>
struct translate_type_imp<double> {
  using type = double;
};
template <>
struct translate_type_imp<std::complex<double>> {
  using type = lapack_complex_double;
};
template <>
struct translate_type_imp<std::complex<float>> {
  using type = lapack_complex_float;
};

/// Given an Eigen types, this is defined to be the corresponding, layout-compatible lapack type
template <typename Scalar>
using translated_type = typename translate_type_imp<Scalar>::type;

/// These functions convert their arguments from Eigen to Lapack types
/// This function performs conversion for any of the translations defined above.
template <typename Source, typename Target = translated_type<Source>>
EIGEN_ALWAYS_INLINE auto to_lapack(Source value) {
  return static_cast<Target>(value);
}

/// This function performs conversions for pointer types corresponding to the translations abovce.
/// This is valid because the translations are between layout-compatible types.
template <typename Source, typename Target = translated_type<Source>>
EIGEN_ALWAYS_INLINE auto to_lapack(Source *value) {
  return reinterpret_cast<Target *>(value);
}

/// This function converts the Eigen Index to a lapack index, with possible range checks
/// \sa internal::convert_index
EIGEN_ALWAYS_INLINE lapack_int to_lapack(Index index) { return convert_index<lapack_int>(index); }

/// translates storage order of the given Eigen object to the corresponding lapack constant
template <typename Derived>
EIGEN_ALWAYS_INLINE constexpr lapack_int lapack_storage_of(const EigenBase<Derived> &) {
  return Derived::IsRowMajor ? LAPACK_ROW_MAJOR : LAPACK_COL_MAJOR;
}

// ---------------------------------------------------------------------------------------------------------------------
//              Automatic generation of low-level wrappers
// ---------------------------------------------------------------------------------------------------------------------

/*!
 * \internal
 * \brief Helper type to facilitate the wrapping of raw LAPACKE functions for different types into a single, overloaded
 * C++ function. This is achieved in combination with \r EIGEN_MAKE_LAPACKE_WRAPPER \details This implementation works
 * by providing an overloaded call function that just forwards its arguments to the underlying lapack function. Each of
 * these overloads is enabled only if the call is actually well formed. Because these lapack functions take pointers to
 * the underlying scalar type as arguments, even though the actual Scalars would be implicitly convertible, the pointers
 * are not and therefore only a single overload can be valid at the same time. Thus, despite all functions taking fully
 * generic `Args&&... args` as arguments, there is never any ambiguity.
 */
template <typename DoubleFn, typename SingleFn, typename DoubleCpxFn, typename SingleCpxFn>
struct WrappingHelper {
  // The naming of double, single, double complex and single complex is purely for readability
  // and doesn't actually affect the workings of this class. In principle, the arguments can
  // be supplied in any permuted order.
  DoubleFn double_;
  SingleFn single_;
  DoubleCpxFn double_cpx_;
  SingleCpxFn single_cpx_;

  template <typename... Args>
  auto call(Args &&...args) -> decltype(double_(std::forward<Args>(args)...)) {
    return double_(std::forward<Args>(args)...);
  }

  template <typename... Args>
  auto call(Args &&...args) -> decltype(single_(std::forward<Args>(args)...)) {
    return single_(std::forward<Args>(args)...);
  }

  template <typename... Args>
  auto call(Args &&...args) -> decltype(double_cpx_(std::forward<Args>(args)...)) {
    return double_cpx_(std::forward<Args>(args)...);
  }

  template <typename... Args>
  auto call(Args &&...args) -> decltype(single_cpx_(std::forward<Args>(args)...)) {
    return single_cpx_(std::forward<Args>(args)...);
  }
};

/** \internal Helper function that generates a `WrappingHelper` object with the given function pointers and
 * invokes its `call` method, thus selecting one of the overloads.
 * \sa EIGEN_MAKE_LAPACKE_WRAPPER
 */
template <typename DoubleFn, typename SingleFn, typename DoubleCpxFn, typename SingleCpxFn, typename... Args>
EIGEN_ALWAYS_INLINE auto call_wrapper(DoubleFn df, SingleFn sf, DoubleCpxFn dcf, SingleCpxFn scf, Args &&...args) {
  WrappingHelper<DoubleFn, SingleFn, DoubleCpxFn, SingleCpxFn> helper{df, sf, dcf, scf};
  return helper.call(std::forward<Args>(args)...);
}

/**
 * \internal
 * Generates a new function `Function` that dispatches to the corresponding LAPACKE_? prefixed functions.
 * \sa WrappingHelper
 */
#define EIGEN_MAKE_LAPACKE_WRAPPER(FUNCTION)                                                                \
  template <typename... Args>                                                                               \
  EIGEN_ALWAYS_INLINE auto FUNCTION(Args &&...args) {                                                       \
    return call_wrapper(LAPACKE_d##FUNCTION, LAPACKE_s##FUNCTION, LAPACKE_z##FUNCTION, LAPACKE_c##FUNCTION, \
                        std::forward<Args>(args)...);                                                       \
  }

// Now with this macro and the helper wrappers, we can generate the dispatch for all the lapacke functions that are
// used in Eigen.
// We define these here instead of in the files where they are used because this allows us to #undef the macro again
// right here
EIGEN_MAKE_LAPACKE_WRAPPER(potrf)
EIGEN_MAKE_LAPACKE_WRAPPER(getrf)
EIGEN_MAKE_LAPACKE_WRAPPER(geqrf)
EIGEN_MAKE_LAPACKE_WRAPPER(gesdd)

#undef EIGEN_MAKE_LAPACKE_WRAPPER
}  // namespace lapacke_helpers
}  // namespace internal
}  // namespace Eigen

#endif  // EIGEN_LAPACKE_HELPERS_H