// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2021 Erik Schultheis // // 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 struct translate_type_imp; template <> struct translate_type_imp { using type = float; }; template <> struct translate_type_imp { using type = double; }; template <> struct translate_type_imp> { using type = lapack_complex_double; }; template <> struct translate_type_imp> { using type = lapack_complex_float; }; /// Given an Eigen types, this is defined to be the corresponding, layout-compatible lapack type template using translated_type = typename translate_type_imp::type; /// These functions convert their arguments from Eigen to Lapack types /// This function performs conversion for any of the translations defined above. template > EIGEN_ALWAYS_INLINE auto to_lapack(Source value) { return static_cast(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 > EIGEN_ALWAYS_INLINE auto to_lapack(Source *value) { return reinterpret_cast(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(index); } /// translates storage order of the given Eigen object to the corresponding lapack constant template EIGEN_ALWAYS_INLINE constexpr lapack_int lapack_storage_of(const EigenBase &) { 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 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 auto call(Args &&...args) -> decltype(double_(std::forward(args)...)) { return double_(std::forward(args)...); } template auto call(Args &&...args) -> decltype(single_(std::forward(args)...)) { return single_(std::forward(args)...); } template auto call(Args &&...args) -> decltype(double_cpx_(std::forward(args)...)) { return double_cpx_(std::forward(args)...); } template auto call(Args &&...args) -> decltype(single_cpx_(std::forward(args)...)) { return single_cpx_(std::forward(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 EIGEN_ALWAYS_INLINE auto call_wrapper(DoubleFn df, SingleFn sf, DoubleCpxFn dcf, SingleCpxFn scf, Args &&...args) { WrappingHelper helper{df, sf, dcf, scf}; return helper.call(std::forward(args)...); } /** * \internal * Generates a new function `Function` that dispatches to the corresponding LAPACKE_? prefixed functions. * \sa WrappingHelper */ #define EIGEN_MAKE_LAPACKE_WRAPPER(FUNCTION) \ template \ 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)...); \ } // 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