GPU: Centralize expression type traits libeigen/eigen!2799
diff --git a/unsupported/Eigen/GPU b/unsupported/Eigen/GPU index 06dcd0a..4128fd9 100644 --- a/unsupported/Eigen/GPU +++ b/unsupported/Eigen/GPU
@@ -43,6 +43,8 @@ #ifdef EIGEN_USE_GPU // IWYU pragma: begin_exports #include "src/GPU/FwdDecl.h" +#include "src/GPU/Meta.h" +#include "src/GPU/type_traits.h" #include "src/GPU/DeviceScalar.h" #include "src/GPU/DeviceMatrix.h" #include "src/GPU/GpuContext.h"
diff --git a/unsupported/Eigen/src/GPU/DeviceExpr.h b/unsupported/Eigen/src/GPU/DeviceExpr.h index 9084001..f0880f4 100644 --- a/unsupported/Eigen/src/GPU/DeviceExpr.h +++ b/unsupported/Eigen/src/GPU/DeviceExpr.h
@@ -27,30 +27,11 @@ #include "./InternalHeaderCheck.h" #include "./CuBlasSupport.h" -#include "./FwdDecl.h" +#include "./type_traits.h" namespace Eigen { namespace gpu { namespace internal { -// Forward declaration — specializations follow below, after the class definitions. -template <typename Expr> -struct device_expr_traits; - -// Shorthand for the scalar type of a device expression. -template <typename Expr> -using scalar_type_t = typename device_expr_traits<Expr>::scalar_type; -} // namespace internal - -namespace internal { -// Identifies gpu::DeviceScalar so the generic scalar-times-matrix overloads -// below can exclude it (DeviceScalar has dedicated device-pointer overloads -// and is implicitly convertible to its host scalar, which would otherwise -// make the overload sets ambiguous). -template <typename T> -struct is_device_scalar : std::false_type {}; -template <typename S> -struct is_device_scalar<DeviceScalar<S>> : std::true_type {}; - // SFINAE gate for scalar factors: any type convertible to the expression's // scalar (so `2 * d_A` and `2.0 * d_cplx` work), except DeviceScalar. template <typename T, typename S> @@ -60,8 +41,7 @@ } // namespace internal -// Returned by DeviceMatrix::adjoint(). Maps to cublasXgemm transA/B = C. - +/** \brief View returned by DeviceMatrix::adjoint(); maps to the cuBLAS conjugate-transpose operand flag. */ template <typename Scalar_> class AdjointView { public: @@ -73,8 +53,7 @@ const DeviceMatrix<Scalar>& mat_; }; -// Returned by DeviceMatrix::transpose(). Maps to cublasXgemm transA/B = T. - +/** \brief View returned by DeviceMatrix::transpose(); maps to the cuBLAS transpose operand flag. */ template <typename Scalar_> class TransposeView { public: @@ -86,23 +65,29 @@ const DeviceMatrix<Scalar>& mat_; }; -// Returned by operator*(Scalar, DeviceMatrix/View). Carries the scalar factor. - -template <typename Inner> +/** \brief Expression returned by operator*(Scalar, DeviceMatrix/View), carrying the scalar factor. + * + * \c Inner names the scaled operand, decayed, for callers that need to reason + * about what is being scaled. The is_scaled_* predicates in type_traits.h + * deliberately do not read it: naming a member instantiates Scaled, and + * Scaled<GemmExpr<...>>::Scalar is ill-formed. They deduce the operand from the + * template-id instead. + */ +template <typename Inner_> class Scaled { public: + using Inner = std::decay_t<Inner_>; using Scalar = internal::scalar_type_t<Inner>; - Scaled(Scalar alpha, const Inner& inner) : alpha_(alpha), inner_(inner) {} + Scaled(Scalar alpha, const Inner_& inner) : alpha_(alpha), inner_(inner) {} Scalar scalar() const { return alpha_; } - const Inner& inner() const { return inner_; } + const Inner_& inner() const { return inner_; } private: Scalar alpha_; - const Inner& inner_; + const Inner_& inner_; }; -// Returned by operator*(lhs_expr, rhs_expr). Dispatches to cuBLAS GEMM. - +/** \brief Expression returned by operator*(lhs_expr, rhs_expr), dispatched to cuBLAS GEMM. */ template <typename Lhs, typename Rhs> class GemmExpr { public: @@ -183,7 +168,7 @@ } namespace internal { -// Default: a DeviceMatrix is NoTrans. +// Default: a DeviceMatrix is NoTrans. Documented on the FwdDecl.h forward declaration. template <typename T> struct device_expr_traits { static constexpr bool is_device_expr = false; @@ -236,9 +221,12 @@ return {a, b}; } -// Like Scaled but carries a DeviceScalar (device pointer) instead of -// a host scalar. operator+= dispatches to cuBLAS axpy with POINTER_MODE_DEVICE. - +/** + * \brief Expression that scales a device matrix by a DeviceScalar. + * + * Unlike Scaled, this expression carries a device pointer. operator+= dispatches to cuBLAS AXPY with device pointer + * mode. + */ template <typename Scalar_> class DeviceScaledDevice { public: @@ -267,6 +255,7 @@ // If DeviceMatrix is ever made an Eigen expression type, these would need to // be revisited. +/** \brief Linear combination of two device matrices. */ template <typename Scalar_> class DeviceAddExpr { public:
diff --git a/unsupported/Eigen/src/GPU/DeviceScalar.h b/unsupported/Eigen/src/GPU/DeviceScalar.h index 8652b91..e24d122 100644 --- a/unsupported/Eigen/src/GPU/DeviceScalar.h +++ b/unsupported/Eigen/src/GPU/DeviceScalar.h
@@ -28,6 +28,7 @@ namespace Eigen { namespace gpu { +/** \brief RAII wrapper for a scalar in GPU device memory. */ template <typename Scalar_> class DeviceScalar { public:
diff --git a/unsupported/Eigen/src/GPU/FwdDecl.h b/unsupported/Eigen/src/GPU/FwdDecl.h index a8b74d9..9cf0457 100644 --- a/unsupported/Eigen/src/GPU/FwdDecl.h +++ b/unsupported/Eigen/src/GPU/FwdDecl.h
@@ -18,6 +18,22 @@ class Context; +enum class GpuOp; + +namespace internal { +class DeviceBuffer; + +/** \brief Describes GPU device expression types. + * + * Specializations report the expression's scalar type, its transpose op, and + * how to reach the underlying matrix and deferred scalar. They live in + * DeviceExpr.h; this declaration is what the query aliases in type_traits.h + * are written against. + */ +template <typename Expr> +struct device_expr_traits; +} // namespace internal + template <typename Scalar_> class DeviceMatrix; template <typename Scalar_> @@ -27,6 +43,8 @@ class LLT; template <typename Scalar_> class LU; +template <typename Scalar_> +class QR; template <typename Scalar_> class AdjointView;
diff --git a/unsupported/Eigen/src/GPU/GpuSupport.h b/unsupported/Eigen/src/GPU/GpuSupport.h index cf7c90a..5027564 100644 --- a/unsupported/Eigen/src/GPU/GpuSupport.h +++ b/unsupported/Eigen/src/GPU/GpuSupport.h
@@ -214,6 +214,7 @@ } }; +/** \brief Internal RAII owner for an untyped GPU device allocation. */ class DeviceBuffer { public: DeviceBuffer() = default;
diff --git a/unsupported/Eigen/src/GPU/Meta.h b/unsupported/Eigen/src/GPU/Meta.h index 21895d2..c483220 100644 --- a/unsupported/Eigen/src/GPU/Meta.h +++ b/unsupported/Eigen/src/GPU/Meta.h
@@ -30,6 +30,9 @@ template <typename T1, typename T2> using require_not_same_t = require_not_t<std::is_same<std::decay_t<T1>, std::decay_t<T2>>>; +template <bool... Values> +using require_all_t = require_t<Eigen::internal::reduce_all<Values...>>; + } // namespace internal } // namespace gpu } // namespace Eigen
diff --git a/unsupported/Eigen/src/GPU/type_traits.h b/unsupported/Eigen/src/GPU/type_traits.h new file mode 100644 index 0000000..ae9f81d --- /dev/null +++ b/unsupported/Eigen/src/GPU/type_traits.h
@@ -0,0 +1,954 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// 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/. +// SPDX-FileCopyrightText: The Eigen Authors +// SPDX-License-Identifier: MPL-2.0 + +#ifndef EIGEN_GPU_TYPE_TRAITS_H +#define EIGEN_GPU_TYPE_TRAITS_H + +// IWYU pragma: private +#include "./InternalHeaderCheck.h" + +#include "./FwdDecl.h" +#include "./Meta.h" + +#include <complex> +#include <type_traits> + +namespace Eigen { +namespace gpu { + +/** + * @name internal:: type traits + * + * Each one keys off the *name* of a forward-declared class. None of them + * inspect members so they parse cleanly with FwdDecl.h alone. is_scaled_leaf + * and is_scaled_gemm compose with an inner predicate, but still by matching the + * template-id rather than by reading a member. + */ + +namespace internal { + +///@{ + +template <typename Expr> +using scalar_type_t = typename device_expr_traits<Expr>::scalar_type; + +template <typename T> +struct is_device_buffer : Eigen::internal::bool_constant<false> {}; +template <> +struct is_device_buffer<DeviceBuffer> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_device_matrix : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_device_matrix<DeviceMatrix<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_gemm_expr : Eigen::internal::bool_constant<false> {}; +template <typename Lhs, typename Rhs> +struct is_gemm_expr<GemmExpr<Lhs, Rhs>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_adjoint_view : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_adjoint_view<AdjointView<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_transpose_view : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_transpose_view<TransposeView<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_scaled : Eigen::internal::bool_constant<false> {}; +template <typename Inner> +struct is_scaled<Scaled<Inner>> : Eigen::internal::bool_constant<true> {}; + +/** + * @brief Detects a Scaled directly over a leaf DeviceMatrix (no view in between). + * @tparam T The type to test. + * @note Defers to is_device_matrix on the scaled operand rather than restating + * the nested pattern, so it tracks whatever is_device_matrix accepts. + * @note The operand is deduced from the template-id, not read from Scaled::Inner: + * naming a member would instantiate Scaled, and Scaled<GemmExpr<...>>::Scalar is + * ill-formed because GemmExpr has no device_expr_traits specialization. Matching + * the template-id keeps this predicate usable on any Scaled, complete or not. + * @note Such a node arriving as a donation (owned rvalue) is materialized by + * stealing the leaf and applying the scalar in place (scal) instead of a geam + * into a fresh buffer. + */ +template <typename T> +struct is_scaled_leaf : Eigen::internal::bool_constant<false> {}; +template <typename Inner> +struct is_scaled_leaf<Scaled<Inner>> : is_device_matrix<std::decay_t<Inner>> {}; + +/** + * @brief Detects a Scaled directly over a GemmExpr (a product carrying ONE deferred scalar). + * @tparam T The type to test. + * @note Defers to is_gemm_expr on the scaled operand, deduced the same way as in + * is_scaled_leaf and for the same reason. + * @note Such a summand routes through the GEMM epilogue with its factor as the + * gemm's alpha_scale (no temporary). + */ +template <typename T> +struct is_scaled_gemm : Eigen::internal::bool_constant<false> {}; +template <typename Inner> +struct is_scaled_gemm<Scaled<Inner>> : is_gemm_expr<std::decay_t<Inner>> {}; + +template <typename T> +struct is_triangular_view : Eigen::internal::bool_constant<false> {}; +template <typename Inner, int UpLo> +struct is_triangular_view<TriangularView<Inner, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_trsm_expr : Eigen::internal::bool_constant<false> {}; +template <typename Scalar, int UpLo> +struct is_trsm_expr<TrsmExpr<Scalar, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_selfadjoint_view : Eigen::internal::bool_constant<false> {}; +template <typename Scalar, int UpLo> +struct is_selfadjoint_view<SelfAdjointView<Scalar, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_const_selfadjoint_view : Eigen::internal::bool_constant<false> {}; +template <typename Inner, int UpLo> +struct is_const_selfadjoint_view<ConstSelfAdjointView<Inner, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_symm_expr : Eigen::internal::bool_constant<false> {}; +template <typename Scalar, int UpLo> +struct is_symm_expr<SymmExpr<Scalar, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_syrk_expr : Eigen::internal::bool_constant<false> {}; +template <typename A, int UpLo> +struct is_syrk_expr<SyrkExpr<A, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_llt_solve_expr : Eigen::internal::bool_constant<false> {}; +template <typename Scalar, int UpLo> +struct is_llt_solve_expr<LltSolveExpr<Scalar, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_lu_solve_expr : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_lu_solve_expr<LuSolveExpr<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_llt_view : Eigen::internal::bool_constant<false> {}; +template <typename Scalar, int UpLo> +struct is_llt_view<LLTView<Scalar, UpLo>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_lu_view : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_lu_view<LUView<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_device_add_expr : Eigen::internal::bool_constant<false> {}; +template <typename Scalar> +struct is_device_add_expr<DeviceAddExpr<Scalar>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_device_scaled_device : Eigen::internal::bool_constant<false> {}; +template <typename Inner> +struct is_device_scaled_device<DeviceScaledDevice<Inner>> : Eigen::internal::bool_constant<true> {}; + +template <typename T> +struct is_device_scalar : Eigen::internal::bool_constant<false> {}; +template <typename S> +struct is_device_scalar<DeviceScalar<S>> : Eigen::internal::bool_constant<true> {}; + +///@} + +} // namespace internal + +/** + * @defgroup gpu_type_traits GPU type traits + * @name Public is_* / is_*_v / require_* / require_all_* wrappers + * + * Type traits for the Eigen GPU library + */ +///@{ + +/** + * Detect if a type is a @ref Eigen::gpu::internal::DeviceBuffer + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_buffer : internal::is_device_buffer<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::internal::DeviceBuffer + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_buffer_v = is_device_buffer<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::internal::DeviceBuffer + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_buffer = internal::require_t<is_device_buffer<T>>; + +/** + * Detect if a type is a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_matrix : internal::is_device_matrix<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_matrix_v = is_device_matrix<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_matrix = internal::require_t<is_device_matrix<T>>; + +/** + * Require a type is not a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_not_device_matrix = internal::require_not_t<is_device_matrix<T>>; + +/** + * Require all types are @ref Eigen::gpu::DeviceMatrix types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_device_matrix = internal::require_all_t<is_device_matrix_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_gemm_expr : internal::is_gemm_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_gemm_expr_v = is_gemm_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_gemm_expr = internal::require_t<is_gemm_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::GemmExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_gemm_expr = internal::require_all_t<is_gemm_expr_v<Types>...>; + +/** + * Detect if a type is an @ref Eigen::gpu::AdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_adjoint_view : internal::is_adjoint_view<std::decay_t<T>> {}; + +/** + * True iff a type is an @ref Eigen::gpu::AdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_adjoint_view_v = is_adjoint_view<T>::value; + +/** + * Require a type is an @ref Eigen::gpu::AdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_adjoint_view = internal::require_t<is_adjoint_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::AdjointView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_adjoint_view = internal::require_all_t<is_adjoint_view_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::TransposeView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_transpose_view : internal::is_transpose_view<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::TransposeView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_transpose_view_v = is_transpose_view<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::TransposeView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_transpose_view = internal::require_t<is_transpose_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::TransposeView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_transpose_view = internal::require_all_t<is_transpose_view_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::Scaled expression. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_scaled : internal::is_scaled<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::Scaled expression. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_scaled_v = is_scaled<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::Scaled expression. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_scaled = internal::require_t<is_scaled<T>>; + +/** + * Detect if a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_scaled_leaf : internal::is_scaled_leaf<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_scaled_leaf_v = is_scaled_leaf<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::DeviceMatrix + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_scaled_leaf = internal::require_t<is_scaled_leaf<T>>; + +/** + * Detect if a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_scaled_gemm : internal::is_scaled_gemm<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_scaled_gemm_v = is_scaled_gemm<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::Scaled expression directly over a @ref Eigen::gpu::GemmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_scaled_gemm = internal::require_t<is_scaled_gemm<T>>; + +/** + * True iff a type is a @ref Eigen::gpu::GemmExpr or a @ref Eigen::gpu::Scaled expression directly over one. + * @tparam T The type to test. + * @note Such a product may carry one deferred scalar, which becomes the GEMM alpha scale. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_gemm_like_v = is_gemm_expr_v<T> || is_scaled_gemm_v<T>; + +/** + * Require a type is an @ref Eigen::gpu::AdjointView or a @ref Eigen::gpu::TransposeView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_adjoint_or_transpose_view = + internal::require_t<Eigen::internal::bool_constant<is_adjoint_view_v<T> || is_transpose_view_v<T>>>; + +/** + * Detect if a type is a @ref Eigen::gpu::TriangularView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_triangular_view : internal::is_triangular_view<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::TriangularView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_triangular_view_v = is_triangular_view<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::TriangularView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_triangular_view = internal::require_t<is_triangular_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::TriangularView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_triangular_view = internal::require_all_t<is_triangular_view_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::TrsmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_trsm_expr : internal::is_trsm_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::TrsmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_trsm_expr_v = is_trsm_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::TrsmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_trsm_expr = internal::require_t<is_trsm_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::TrsmExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_trsm_expr = internal::require_all_t<is_trsm_expr_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::SelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_selfadjoint_view : internal::is_selfadjoint_view<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::SelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_selfadjoint_view_v = is_selfadjoint_view<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::SelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_selfadjoint_view = internal::require_t<is_selfadjoint_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::SelfAdjointView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_selfadjoint_view = internal::require_all_t<is_selfadjoint_view_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::ConstSelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_const_selfadjoint_view : internal::is_const_selfadjoint_view<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::ConstSelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_const_selfadjoint_view_v = is_const_selfadjoint_view<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::ConstSelfAdjointView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_const_selfadjoint_view = internal::require_t<is_const_selfadjoint_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::ConstSelfAdjointView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_const_selfadjoint_view = internal::require_all_t<is_const_selfadjoint_view_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::SymmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_symm_expr : internal::is_symm_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::SymmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_symm_expr_v = is_symm_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::SymmExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_symm_expr = internal::require_t<is_symm_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::SymmExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_symm_expr = internal::require_all_t<is_symm_expr_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::SyrkExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_syrk_expr : internal::is_syrk_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::SyrkExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_syrk_expr_v = is_syrk_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::SyrkExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_syrk_expr = internal::require_t<is_syrk_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::SyrkExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_syrk_expr = internal::require_all_t<is_syrk_expr_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::LltSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_llt_solve_expr : internal::is_llt_solve_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::LltSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_llt_solve_expr_v = is_llt_solve_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::LltSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_llt_solve_expr = internal::require_t<is_llt_solve_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::LltSolveExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_llt_solve_expr = internal::require_all_t<is_llt_solve_expr_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::LuSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_lu_solve_expr : internal::is_lu_solve_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::LuSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_lu_solve_expr_v = is_lu_solve_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::LuSolveExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_lu_solve_expr = internal::require_t<is_lu_solve_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::LuSolveExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_lu_solve_expr = internal::require_all_t<is_lu_solve_expr_v<Types>...>; + +/** + * Detect if a type is an @ref Eigen::gpu::LLTView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_llt_view : internal::is_llt_view<std::decay_t<T>> {}; + +/** + * True iff a type is an @ref Eigen::gpu::LLTView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_llt_view_v = is_llt_view<T>::value; + +/** + * Require a type is an @ref Eigen::gpu::LLTView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_llt_view = internal::require_t<is_llt_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::LLTView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_llt_view = internal::require_all_t<is_llt_view_v<Types>...>; + +/** + * Detect if a type is an @ref Eigen::gpu::LUView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_lu_view : internal::is_lu_view<std::decay_t<T>> {}; + +/** + * True iff a type is an @ref Eigen::gpu::LUView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_lu_view_v = is_lu_view<T>::value; + +/** + * Require a type is an @ref Eigen::gpu::LUView + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_lu_view = internal::require_t<is_lu_view<T>>; + +/** + * Require all types are @ref Eigen::gpu::LUView types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_lu_view = internal::require_all_t<is_lu_view_v<Types>...>; + +/** + * True iff a type is an @ref Eigen::gpu::LLTView or an @ref Eigen::gpu::LUView + * @tparam T The type to test. + * @note Arithmetic operators exclude factorization handles so they cannot be lowered as matrix operands. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_factor_expr_v = is_lu_view_v<T> || is_llt_view_v<T>; + +/** + * Detect if a type is a @ref Eigen::gpu::DeviceAddExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_add_expr : internal::is_device_add_expr<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::DeviceAddExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_add_expr_v = is_device_add_expr<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::DeviceAddExpr + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_add_expr = internal::require_t<is_device_add_expr<T>>; + +/** + * Require all types are @ref Eigen::gpu::DeviceAddExpr types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_device_add_expr = internal::require_all_t<is_device_add_expr_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::DeviceScaledDevice + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_scaled_device : internal::is_device_scaled_device<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::DeviceScaledDevice + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_scaled_device_v = is_device_scaled_device<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::DeviceScaledDevice + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_scaled_device = internal::require_t<is_device_scaled_device<T>>; + +/** + * Require all types are @ref Eigen::gpu::DeviceScaledDevice types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_device_scaled_device = internal::require_all_t<is_device_scaled_device_v<Types>...>; + +/** + * Detect if a type is a @ref Eigen::gpu::DeviceScalar + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_scalar : internal::is_device_scalar<std::decay_t<T>> {}; + +/** + * True iff a type is a @ref Eigen::gpu::DeviceScalar + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_scalar_v = is_device_scalar<T>::value; + +/** + * Require a type is a @ref Eigen::gpu::DeviceScalar + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_scalar = internal::require_t<is_device_scalar<T>>; + +/** + * Require all types are @ref Eigen::gpu::DeviceScalar types. + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_device_scalar = internal::require_all_t<is_device_scalar_v<Types>...>; + +/** + * @name device_expr_traits query aliases + * + * Lazy readers of the member-inspecting internal::device_expr_traits, whose + * specializations live in DeviceExpr.h. Declared from the FwdDecl.h forward + * declaration; they resolve only when instantiated on a complete specialization. + */ +///@{ + +template <typename T> +using scalar_type_t = internal::scalar_type_t<std::decay_t<T>>; + +/** + * Detect if a type is marked as a device expression by + * @ref Eigen::gpu::internal::device_expr_traits + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_device_expr : Eigen::internal::bool_constant<internal::device_expr_traits<std::decay_t<T>>::is_device_expr> { +}; + +/** + * True iff a type is marked as a device expression by + * @ref Eigen::gpu::internal::device_expr_traits + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_device_expr_v = is_device_expr<T>::value; + +/** + * Require a type is marked as a device expression by + * @ref Eigen::gpu::internal::device_expr_traits + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_device_expr = internal::require_t<is_device_expr<T>>; + +/** + * Require all types are marked as device expressions by + * @ref Eigen::gpu::internal::device_expr_traits + * @tparam Types The types to test. + * @ingroup gpu_type_traits + */ +template <typename... Types> +using require_all_device_expr = internal::require_all_t<is_device_expr_v<Types>...>; + +template <typename T> +constexpr GpuOp trans_op = internal::device_expr_traits<std::decay_t<T>>::op; + +///@} + +namespace internal { +template <typename T> +struct is_complex : Eigen::internal::bool_constant<false> {}; + +template <typename T> +struct is_complex<std::complex<T>> : Eigen::internal::bool_constant<true> {}; +} // namespace internal + +/** + * Detect if a type is a `std::complex` specialization. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_complex : internal::is_complex<std::decay_t<T>> {}; + +/** + * True iff a type is a `std::complex` specialization. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +constexpr bool is_complex_v = is_complex<T>::value; + +/** + * Detect if a type is an integral, floating-point, or `std::complex` host scalar. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +struct is_host_scalar : Eigen::internal::bool_constant<std::is_floating_point<std::decay_t<T>>::value || + std::is_integral<std::decay_t<T>>::value || is_complex_v<T>> {}; + +/** + * Require a type is an integral, floating-point, or `std::complex` host scalar. + * @tparam T The type to test. + * @ingroup gpu_type_traits + */ +template <typename T> +using require_host_scalar = internal::require_t<is_host_scalar<T>>; + +///@} + +} // namespace gpu +} // namespace Eigen + +#endif // EIGEN_GPU_TYPE_TRAITS_H
diff --git a/unsupported/test/GPU/CMakeLists.txt b/unsupported/test/GPU/CMakeLists.txt index c5375e8..3568e33 100644 --- a/unsupported/test/GPU/CMakeLists.txt +++ b/unsupported/test/GPU/CMakeLists.txt
@@ -73,6 +73,11 @@ endforeach() endfunction() +# Compile-time GPU type-trait surface (static_assert only; links like device_matrix +# because it pulls the full <unsupported/Eigen/GPU> umbrella). +ei_add_gpu_test(device_traits + EXTRA_LIBS CUDA::cublas CUDA::cusolver CUDA::npps CUDA::nppc) + # DeviceMatrix core: CUDA runtime + cuBLAS + cuSOLVER (for BLAS-1 ops via GpuContext). ei_add_gpu_test(device_matrix EXTRA_LIBS CUDA::cublas CUDA::cusolver CUDA::npps CUDA::nppc)
diff --git a/unsupported/test/GPU/device_traits.cpp b/unsupported/test/GPU/device_traits.cpp new file mode 100644 index 0000000..f2d4e1a --- /dev/null +++ b/unsupported/test/GPU/device_traits.cpp
@@ -0,0 +1,145 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// 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/. +// SPDX-FileCopyrightText: The Eigen Authors +// SPDX-License-Identifier: MPL-2.0 + +// Compile-time tests for the unsupported GPU type-trait surface: name-keyed +// predicates plus the device_expr_traits query aliases (scalar_type_t, is_device_expr_v, trans_op). + +#define EIGEN_USE_GPU +#include "main.h" +#include <type_traits> +#include <unsupported/Eigen/GPU> + +using namespace Eigen; + +namespace { +using TraitDeviceMatrix = gpu::DeviceMatrix<double>; +using TraitAdjointView = gpu::AdjointView<double>; +using TraitTransposeView = gpu::TransposeView<double>; +using TraitScaledMatrix = gpu::Scaled<TraitDeviceMatrix>; +using TraitGemmExpr = gpu::GemmExpr<TraitDeviceMatrix, TraitDeviceMatrix>; +using TraitTriangularView = gpu::TriangularView<double, Lower>; +using TraitTrsmExpr = gpu::TrsmExpr<double, Lower>; +using TraitSelfAdjointView = gpu::SelfAdjointView<double, Lower>; +using TraitConstSelfAdjointView = gpu::ConstSelfAdjointView<double, Lower>; +using TraitSymmExpr = gpu::SymmExpr<double, Lower>; +using TraitSyrkExpr = gpu::SyrkExpr<double, Lower>; +using TraitLltSolveExpr = gpu::LltSolveExpr<double, Lower>; +using TraitLuSolveExpr = gpu::LuSolveExpr<double>; +using TraitLltView = gpu::LLTView<double, Lower>; +using TraitLuView = gpu::LUView<double>; +using TraitDeviceAddExpr = gpu::DeviceAddExpr<double>; +using TraitDeviceScaledDevice = gpu::DeviceScaledDevice<double>; +using TraitDeviceScalar = gpu::DeviceScalar<double>; +using TraitDeviceBuffer = gpu::internal::DeviceBuffer; + +#define EIGEN_GPU_STATIC_ASSERT_TRAIT(Trait, Require, RequireAll, Type, NegativeType) \ + static_assert(gpu::Trait<Type>::value, #Trait " should accept the exact type"); \ + static_assert(gpu::Trait<const Type&>::value, #Trait " should decay const references"); \ + static_assert(gpu::Trait##_v<Type&&>, #Trait "_v should decay rvalue references"); \ + static_assert(!gpu::Trait<NegativeType>::value, #Trait " should reject unrelated types"); \ + static_assert(std::is_same<gpu::Require<Type>, int>::value, #Require " should compile"); \ + static_assert(std::is_same<gpu::RequireAll<Type, const Type&>, int>::value, #RequireAll " should compile") + +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_device_matrix, require_device_matrix, require_all_device_matrix, TraitDeviceMatrix, + TraitAdjointView); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_adjoint_view, require_adjoint_view, require_all_adjoint_view, TraitAdjointView, + TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_transpose_view, require_transpose_view, require_all_transpose_view, TraitTransposeView, + TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_gemm_expr, require_gemm_expr, require_all_gemm_expr, TraitGemmExpr, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_triangular_view, require_triangular_view, require_all_triangular_view, + TraitTriangularView, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_trsm_expr, require_trsm_expr, require_all_trsm_expr, TraitTrsmExpr, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_selfadjoint_view, require_selfadjoint_view, require_all_selfadjoint_view, + TraitSelfAdjointView, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_const_selfadjoint_view, require_const_selfadjoint_view, + require_all_const_selfadjoint_view, TraitConstSelfAdjointView, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_symm_expr, require_symm_expr, require_all_symm_expr, TraitSymmExpr, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_syrk_expr, require_syrk_expr, require_all_syrk_expr, TraitSyrkExpr, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_llt_solve_expr, require_llt_solve_expr, require_all_llt_solve_expr, TraitLltSolveExpr, + TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_lu_solve_expr, require_lu_solve_expr, require_all_lu_solve_expr, TraitLuSolveExpr, + TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_llt_view, require_llt_view, require_all_llt_view, TraitLltView, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_lu_view, require_lu_view, require_all_lu_view, TraitLuView, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_device_add_expr, require_device_add_expr, require_all_device_add_expr, + TraitDeviceAddExpr, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_device_scaled_device, require_device_scaled_device, require_all_device_scaled_device, + TraitDeviceScaledDevice, TraitDeviceMatrix); +EIGEN_GPU_STATIC_ASSERT_TRAIT(is_device_scalar, require_device_scalar, require_all_device_scalar, TraitDeviceScalar, + TraitDeviceMatrix); + +#undef EIGEN_GPU_STATIC_ASSERT_TRAIT + +// is_device_buffer has no require_all_ variant, so it is checked directly. +static_assert(gpu::is_device_buffer_v<TraitDeviceBuffer>, "internal::DeviceBuffer is a device buffer"); +static_assert(gpu::is_device_buffer_v<const TraitDeviceBuffer&>, "is_device_buffer_v should decay cv/ref"); +static_assert(!gpu::is_device_buffer_v<TraitDeviceMatrix>, "a DeviceMatrix is not a DeviceBuffer"); +static_assert(std::is_same<gpu::require_device_buffer<TraitDeviceBuffer>, int>::value, + "require_device_buffer should compile"); + +// is_scaled is name-keyed; is_scaled_leaf / is_scaled_gemm compose it with a +// predicate over Scaled::Inner. None of the three has a require_all_ variant. +static_assert(gpu::is_scaled_v<TraitScaledMatrix>, "Scaled is a scaled node"); +static_assert(gpu::is_scaled_v<const TraitScaledMatrix&>, "is_scaled_v should decay cv/ref"); +static_assert(!gpu::is_scaled_v<TraitDeviceMatrix>, "a DeviceMatrix is not a scaled node"); +static_assert(std::is_same<gpu::require_scaled<TraitScaledMatrix>, int>::value, "require_scaled should compile"); + +// Scaled names its operand, which is what the two composed predicates read. +static_assert(std::is_same<TraitScaledMatrix::Inner, TraitDeviceMatrix>::value, "Scaled exposes its inner type"); +static_assert(std::is_same<gpu::Scaled<const TraitDeviceMatrix&>::Inner, TraitDeviceMatrix>::value, + "Scaled::Inner decays the operand"); + +static_assert(gpu::is_scaled_leaf_v<TraitScaledMatrix>, "Scaled<DeviceMatrix> is a scaled leaf"); +static_assert(gpu::is_scaled_leaf_v<TraitScaledMatrix&&>, "is_scaled_leaf_v should decay refs"); +static_assert(gpu::is_scaled_leaf_v<gpu::Scaled<const TraitDeviceMatrix&>>, + "a scaled leaf is still a leaf through Scaled::Inner's decay"); +static_assert(!gpu::is_scaled_leaf_v<gpu::Scaled<TraitAdjointView>>, "Scaled over a view is not a scaled leaf"); +static_assert(!gpu::is_scaled_leaf_v<gpu::Scaled<TraitTransposeView>>, "Scaled over a view is not a scaled leaf"); +static_assert(!gpu::is_scaled_leaf_v<gpu::Scaled<TraitGemmExpr>>, "Scaled over a composite is not a scaled leaf"); +static_assert(!gpu::is_scaled_leaf_v<TraitDeviceMatrix>, "a bare leaf is not a scaled leaf"); + +static_assert(gpu::is_scaled_gemm_v<gpu::Scaled<TraitGemmExpr>>, "Scaled<GemmExpr> is a scaled product"); +static_assert(!gpu::is_scaled_gemm_v<TraitGemmExpr>, "a bare GemmExpr is not a scaled product"); +static_assert(!gpu::is_scaled_gemm_v<TraitScaledMatrix>, "Scaled<leaf> is not a scaled product"); +static_assert(!gpu::is_scaled_gemm_v<gpu::Scaled<TraitAdjointView>>, "Scaled<view> is not a scaled product"); + +// The gate short-circuits: a non-Scaled operand must answer false, not fail to +// compile on the missing ::Inner member. +static_assert(!gpu::is_scaled_leaf_v<TraitAdjointView>, "a view has no ::Inner and is not a scaled leaf"); +static_assert(!gpu::is_scaled_gemm_v<TraitDeviceScalar>, "a device scalar has no ::Inner and is not a scaled product"); + +static_assert(gpu::is_gemm_like_v<TraitGemmExpr>, "a GemmExpr is gemm-like"); +static_assert(gpu::is_gemm_like_v<gpu::Scaled<TraitGemmExpr>>, "Scaled<GemmExpr> is gemm-like"); +static_assert(!gpu::is_gemm_like_v<TraitDeviceMatrix>, "a leaf is not gemm-like"); +static_assert(!gpu::is_gemm_like_v<TraitDeviceAddExpr>, "a sum is not gemm-like"); + +// A factorization handle of either kind satisfies the combined operator gate. +static_assert(gpu::is_factor_expr_v<TraitLuView>, "LUView is a factorization handle"); +static_assert(gpu::is_factor_expr_v<TraitLltView>, "LLTView is a factorization handle"); +static_assert(!gpu::is_factor_expr_v<TraitDeviceMatrix>, "a leaf is not a factorization handle"); + +// Query aliases over device_expr_traits (specializations from DeviceExpr.h). +static_assert(gpu::is_device_expr_v<TraitDeviceMatrix>, "a leaf is a device-expression operand"); +static_assert(gpu::is_device_expr_v<const TraitAdjointView&>, "an adjoint view is a device-expression operand"); +static_assert(gpu::is_device_expr_v<TraitTransposeView&&>, "a transpose view is a device-expression operand"); +static_assert(gpu::is_device_expr_v<TraitScaledMatrix>, "a scaled leaf is a device-expression operand"); +static_assert(!gpu::is_device_expr_v<TraitGemmExpr>, "a GemmExpr is dispatched, not an operand"); +static_assert(std::is_same<gpu::scalar_type_t<const TraitScaledMatrix&>, double>::value, + "scalar_type_t decays cv/ref and folds the operand scalar"); +static_assert(std::is_same<gpu::scalar_type_t<TraitAdjointView>, double>::value, "scalar_type_t reads the view scalar"); +static_assert(gpu::trans_op<TraitDeviceMatrix> == gpu::GpuOp::NoTrans, "a leaf is NoTrans"); +static_assert(gpu::trans_op<TraitAdjointView> == gpu::GpuOp::ConjTrans, "an adjoint view is ConjTrans"); +static_assert(gpu::trans_op<TraitTransposeView> == gpu::GpuOp::Trans, "a transpose view is Trans"); +static_assert(std::is_same<gpu::require_all_device_expr<TraitDeviceMatrix, const TraitAdjointView&, TraitScaledMatrix>, + int>::value, + "require_all_device_expr should compile for device-expression operands"); +} // namespace + +EIGEN_DECLARE_TEST(device_traits) {}