diff --git a/common/src/KokkosFFT_traits.hpp b/common/src/KokkosFFT_traits.hpp index ec89df49..312797bd 100644 --- a/common/src/KokkosFFT_traits.hpp +++ b/common/src/KokkosFFT_traits.hpp @@ -9,6 +9,9 @@ namespace KokkosFFT { namespace Impl { + +// Traits for unary operation + template <typename T> struct base_floating_point { using value_type = T; @@ -60,15 +63,15 @@ struct is_admissible_value_type< template <typename T> struct is_admissible_value_type< - T, std::enable_if_t<Kokkos::is_view<T>::value && + T, std::enable_if_t<Kokkos::is_view_v<T> && (is_real_v<typename T::non_const_value_type> || is_complex_v<typename T::non_const_value_type>)>> : std::true_type {}; /// \brief Helper to check if a type is an acceptable value type /// (float/double/Kokkos::complex<float>/Kokkos::complex<double>) for Kokkos-FFT -/// When applied to Kokkos::View, then check if a value type is an -/// acceptable real/complex type. +/// When applied to Kokkos::View, then check if a value type is an acceptable +/// real/complex type. template <typename T> inline constexpr bool is_admissible_value_type_v = is_admissible_value_type<T>::value; @@ -80,7 +83,7 @@ template <typename ViewType> struct is_layout_left_or_right< ViewType, std::enable_if_t< - Kokkos::is_view<ViewType>::value && + Kokkos::is_view_v<ViewType> && (std::is_same_v<typename ViewType::array_layout, Kokkos::LayoutLeft> || std::is_same_v<typename ViewType::array_layout, Kokkos::LayoutRight>)>> : std::true_type {}; @@ -96,7 +99,7 @@ struct is_admissible_view : std::false_type {}; template <typename ViewType> struct is_admissible_view< - ViewType, std::enable_if_t<Kokkos::is_view<ViewType>::value && + ViewType, std::enable_if_t<Kokkos::is_view_v<ViewType> && is_layout_left_or_right_v<ViewType> && is_admissible_value_type_v<ViewType>>> : std::true_type {}; @@ -107,6 +110,111 @@ template <typename ViewType> inline constexpr bool is_admissible_view_v = is_admissible_view<ViewType>::value; +template <typename ExecutionSpace, typename ViewType, typename Enable = void> +struct is_operatable_view : std::false_type {}; + +template <typename ExecutionSpace, typename ViewType> +struct is_operatable_view< + ExecutionSpace, ViewType, + std::enable_if_t< + Kokkos::is_execution_space_v<ExecutionSpace> && + is_admissible_view_v<ViewType> && + Kokkos::SpaceAccessibility< + ExecutionSpace, typename ViewType::memory_space>::accessible>> + : std::true_type {}; + +/// \brief Helper to check if a View is an acceptable View for Kokkos-FFT and +/// memory space is accessible from the ExecutionSpace +template <typename ExecutionSpace, typename ViewType> +inline constexpr bool is_operatable_view_v = + is_operatable_view<ExecutionSpace, ViewType>::value; + +// Traits for binary operations +template <typename T1, typename T2, typename Enable = void> +struct have_same_base_floating_point_type : std::false_type {}; + +template <typename T1, typename T2> +struct have_same_base_floating_point_type< + T1, T2, + std::enable_if_t<!Kokkos::is_view_v<T1> && !Kokkos::is_view_v<T2> && + std::is_same_v<base_floating_point_type<T1>, + base_floating_point_type<T2>>>> + : std::true_type {}; + +template <typename InViewType, typename OutViewType> +struct have_same_base_floating_point_type< + InViewType, OutViewType, + std::enable_if_t< + Kokkos::is_view_v<InViewType> && Kokkos::is_view_v<OutViewType> && + std::is_same_v< + base_floating_point_type<typename InViewType::non_const_value_type>, + base_floating_point_type< + typename OutViewType::non_const_value_type>>>> + : std::true_type {}; + +/// \brief Helper to check if two value have the same base floating point type. +/// When applied to Kokkos::View, then check if values of views have the same +/// base floating point type. +template <typename T1, typename T2> +inline constexpr bool have_same_base_floating_point_type_v = + have_same_base_floating_point_type<T1, T2>::value; + +template <typename InViewType, typename OutViewType, typename Enable = void> +struct have_same_layout : std::false_type {}; + +template <typename InViewType, typename OutViewType> +struct have_same_layout< + InViewType, OutViewType, + std::enable_if_t<Kokkos::is_view_v<InViewType> && + Kokkos::is_view_v<OutViewType> && + std::is_same_v<typename InViewType::array_layout, + typename OutViewType::array_layout>>> + : std::true_type {}; + +/// \brief Helper to check if two views have the same layout type. +template <typename InViewType, typename OutViewType> +inline constexpr bool have_same_layout_v = + have_same_layout<InViewType, OutViewType>::value; + +template <typename InViewType, typename OutViewType, typename Enable = void> +struct have_same_rank : std::false_type {}; + +template <typename InViewType, typename OutViewType> +struct have_same_rank< + InViewType, OutViewType, + std::enable_if_t<Kokkos::is_view_v<InViewType> && + Kokkos::is_view_v<OutViewType> && + InViewType::rank() == OutViewType::rank()>> + : std::true_type {}; + +/// \brief Helper to check if two views have the same rank. +template <typename InViewType, typename OutViewType> +inline constexpr bool have_same_rank_v = + have_same_rank<InViewType, OutViewType>::value; + +template <typename ExecutionSpace, typename InViewType, typename OutViewType, + typename Enable = void> +struct are_operatable_views : std::false_type {}; + +template <typename ExecutionSpace, typename InViewType, typename OutViewType> +struct are_operatable_views< + ExecutionSpace, InViewType, OutViewType, + std::enable_if_t< + is_operatable_view_v<ExecutionSpace, InViewType> && + is_operatable_view_v<ExecutionSpace, OutViewType> && + have_same_base_floating_point_type_v<InViewType, OutViewType> && + have_same_layout_v<InViewType, OutViewType> && + have_same_rank_v<InViewType, OutViewType>>> : std::true_type {}; + +/// \brief Helper to check if Views are acceptable View for Kokkos-FFT and +/// memory space are accessible from the ExecutionSpace. +/// In addition, precisions, layout and rank are checked to be identical. +template <typename ExecutionSpace, typename InViewType, typename OutViewType> +inline constexpr bool are_operatable_views_v = + are_operatable_views<ExecutionSpace, InViewType, OutViewType>::value; + +// Other traits + /// \brief Helper to define a managable View type from the original view type template <typename T> struct managable_view_type { diff --git a/common/unit_test/Test_Traits.cpp b/common/unit_test/Test_Traits.cpp index 01eb2e02..8d09753d 100644 --- a/common/unit_test/Test_Traits.cpp +++ b/common/unit_test/Test_Traits.cpp @@ -6,6 +6,16 @@ #include "KokkosFFT_traits.hpp" #include "Test_Utils.hpp" +// All the tests in this file are compile time tests, so we skip all the tests +// by GTEST_SKIP(). gtest is used for type parameterization. + +// Define the types to combine +using base_real_types = std::tuple<float, double, long double>; + +// Define the layouts to combine +using base_layout_types = + std::tuple<Kokkos::LayoutLeft, Kokkos::LayoutRight, Kokkos::LayoutStride>; + using real_types = ::testing::Types<float, double, long double>; using view_types = ::testing::Types<std::pair<float, Kokkos::LayoutLeft>, @@ -18,10 +28,25 @@ using view_types = std::pair<long double, Kokkos::LayoutRight>, std::pair<long double, Kokkos::LayoutStride>>; +// Define all the combinations +using paired_value_types = + tuple_to_types_t<cartesian_product_t<base_real_types, base_real_types>>; + +using paired_layout_types = + tuple_to_types_t<cartesian_product_t<base_layout_types, base_layout_types>>; + +using paired_view_types = + tuple_to_types_t<cartesian_product_t<base_real_types, base_layout_types, + base_real_types, base_layout_types>>; + template <typename T> struct RealAndComplexTypes : public ::testing::Test { using real_type = T; using complex_type = Kokkos::complex<T>; + + virtual void SetUp() { + GTEST_SKIP() << "Skipping all tests for this fixture"; + } }; template <typename T> @@ -29,10 +54,48 @@ struct RealAndComplexViewTypes : public ::testing::Test { using real_type = typename T::first_type; using complex_type = Kokkos::complex<real_type>; using layout_type = typename T::second_type; + virtual void SetUp() { + GTEST_SKIP() << "Skipping all tests for this fixture"; + } +}; + +template <typename T> +struct PairedValueTypes : public ::testing::Test { + using real_type1 = typename std::tuple_element_t<0, T>; + using real_type2 = typename std::tuple_element_t<1, T>; + + virtual void SetUp() { + GTEST_SKIP() << "Skipping all tests for this fixture"; + } +}; + +template <typename T> +struct PairedLayoutTypes : public ::testing::Test { + using layout_type1 = typename std::tuple_element_t<0, T>; + using layout_type2 = typename std::tuple_element_t<1, T>; + + virtual void SetUp() { + GTEST_SKIP() << "Skipping all tests for this fixture"; + } +}; + +template <typename T> +struct PairedViewTypes : public ::testing::Test { + using real_type1 = typename std::tuple_element_t<0, T>; + using layout_type1 = typename std::tuple_element_t<1, T>; + using real_type2 = typename std::tuple_element_t<2, T>; + using layout_type2 = typename std::tuple_element_t<3, T>; + + virtual void SetUp() { + GTEST_SKIP() << "Skipping all tests for this fixture"; + } }; TYPED_TEST_SUITE(RealAndComplexTypes, real_types); TYPED_TEST_SUITE(RealAndComplexViewTypes, view_types); +TYPED_TEST_SUITE(PairedValueTypes, paired_value_types); +TYPED_TEST_SUITE(PairedLayoutTypes, paired_layout_types); +TYPED_TEST_SUITE(PairedViewTypes, paired_view_types); // Tests for real type deduction template <typename RealType, typename ComplexType> @@ -42,8 +105,11 @@ void test_get_real_type() { using real_type_from_ComplexType = KokkosFFT::Impl::base_floating_point_type<ComplexType>; + // base floating point type of RealType is RealType static_assert(std::is_same_v<real_type_from_RealType, RealType>, "Real type not deduced correctly from real type"); + + // base floating point type of Kokkos::complex<RealType> is RealType static_assert(std::is_same_v<real_type_from_ComplexType, RealType>, "Real type not deduced correctly from complex type"); } @@ -51,26 +117,32 @@ void test_get_real_type() { // Tests for admissible real types (float or double) template <typename T> void test_admissible_real_type() { + // Tests that a real type is float or double if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) { + // T is float or double static_assert(KokkosFFT::Impl::is_real_v<T>, "Real type must be float or double"); } else { + // T is not float or double static_assert(!KokkosFFT::Impl::is_real_v<T>, - "Real type must be float or double"); + "Real type must not be float or double"); } } template <typename T> void test_admissible_complex_type() { using real_type = KokkosFFT::Impl::base_floating_point_type<T>; + // Tests that a base floating point type of complex value is float or double if constexpr (std::is_same_v<real_type, float> || std::is_same_v<real_type, double>) { + // T is Kokkos::complex<float> or Kokkos::complex<double> static_assert(KokkosFFT::Impl::is_complex_v<T>, "Complex type must be Kokkos::complex<float> or " "Kokkos::complex<double>"); } else { + // T is not Kokkos::complex<float> or Kokkos::complex<double> static_assert(!KokkosFFT::Impl::is_complex_v<T>, - "Complex type must be Kokkos::complex<float> or " + "Complex type must not be Kokkos::complex<float> or " "Kokkos::complex<double>"); } } @@ -99,26 +171,38 @@ template <typename T, typename LayoutType> void test_admissible_value_type() { using ViewType = Kokkos::View<T*, LayoutType>; using real_type = KokkosFFT::Impl::base_floating_point_type<T>; + // Tests that a Value or View has a addmissible value type if constexpr (std::is_same_v<real_type, float> || std::is_same_v<real_type, double>) { + // Base floating point type of a Value is float or double + static_assert(KokkosFFT::Impl::is_admissible_value_type_v<T>, + "Base value type must be float or double"); + + // Base floating point type of a View is float or double static_assert(KokkosFFT::Impl::is_admissible_value_type_v<ViewType>, - "Real type must be float or double"); + "Base value type of a View must be float or double"); } else { + // Base floating point type of a Value is not float or double + static_assert(!KokkosFFT::Impl::is_admissible_value_type_v<T>, + "Base value type of a View must not be float or double"); + + // Base floating point type of a View is not float or double static_assert(!KokkosFFT::Impl::is_admissible_value_type_v<ViewType>, - "Real type must be float or double"); + "Base value type of a View must not be float or double"); } } template <typename T, typename LayoutType> void test_admissible_layout_type() { using ViewType = Kokkos::View<T*, LayoutType>; + // Tests that the View has a layout in LayoutLeft or LayoutRight if constexpr (std::is_same_v<LayoutType, Kokkos::LayoutLeft> || std::is_same_v<LayoutType, Kokkos::LayoutRight>) { static_assert(KokkosFFT::Impl::is_layout_left_or_right_v<ViewType>, "View Layout must be either LayoutLeft or LayoutRight."); } else { static_assert(!KokkosFFT::Impl::is_layout_left_or_right_v<ViewType>, - "View Layout must be either LayoutLeft or LayoutRight."); + "View Layout must not be either LayoutLeft or LayoutRight."); } } @@ -126,19 +210,78 @@ template <typename T, typename LayoutType> void test_admissible_view_type() { using ViewType = Kokkos::View<T*, LayoutType>; using real_type = KokkosFFT::Impl::base_floating_point_type<T>; - if constexpr ( - (std::is_same_v<real_type, float> || std::is_same_v<real_type, double>)&&( - std::is_same_v<LayoutType, Kokkos::LayoutLeft> || - std::is_same_v<LayoutType, Kokkos::LayoutRight>)) { - static_assert(KokkosFFT::Impl::is_admissible_view_v<ViewType>, - "View value type must be float, double, " - "Kokkos::Complex<float>, Kokkos::Complex<double>. Layout " - "must be either LayoutLeft or LayoutRight."); + + // Tests that the View has a base floating point type in float or double + if constexpr ((std::is_same_v<real_type, float> || + std::is_same_v<real_type, double>)) { + // Tests that the View has a layout in LayoutLeft or LayoutRight + if constexpr (std::is_same_v<LayoutType, Kokkos::LayoutLeft> || + std::is_same_v<LayoutType, Kokkos::LayoutRight>) { + static_assert(KokkosFFT::Impl::is_admissible_view_v<ViewType>, + "View value type must be float, double, " + "Kokkos::Complex<float>, Kokkos::Complex<double>. Layout " + "must be either LayoutLeft or LayoutRight."); + } else { + // View is not admissible because layout is not in LayoutLeft or + // LayoutRight + static_assert(!KokkosFFT::Impl::is_admissible_view_v<ViewType>, + "Layout must be either LayoutLeft or LayoutRight."); + } } else { + // View is not admissible because the base floating point type is not in + // float or double static_assert(!KokkosFFT::Impl::is_admissible_view_v<ViewType>, - "View value type must be float, double, " - "Kokkos::Complex<float>, Kokkos::Complex<double>. Layout " - "must be either LayoutLeft or LayoutRight."); + "Base value type must be float or double"); + } +} + +// \brief Test if a View is operatable +// \tparam ExecutionSpace1 Execution space for the device +// \tparam ExecutionSpace2 Execution space for the View memory space +// \tparam T Value type of the View +// \tparam LayoutType Layout type of the View +template <typename ExecutionSpace1, typename ExecutionSpace2, typename T, + typename LayoutType> +void test_operatable_view_type() { + using ViewType = Kokkos::View<T*, LayoutType, ExecutionSpace2>; + using real_type = KokkosFFT::Impl::base_floating_point_type<T>; + + // Tests that a View is accessible from the ExecutionSpace + if constexpr (Kokkos::SpaceAccessibility< + ExecutionSpace1, + typename ViewType::memory_space>::accessible) { + // Tests that the View has a base floating point type in float or double + if constexpr ((std::is_same_v<real_type, float> || + std::is_same_v<real_type, double>)) { + // Tests that the View has a layout in LayoutLeft or LayoutRight + if constexpr (std::is_same_v<LayoutType, Kokkos::LayoutLeft> || + std::is_same_v<LayoutType, Kokkos::LayoutRight>) { + // View is operatable + static_assert( + KokkosFFT::Impl::is_operatable_view_v<ExecutionSpace1, ViewType>, + "View value type must be float, double, " + "Kokkos::Complex<float>, or Kokkos::Complex<double>. Layout " + "must be either LayoutLeft or LayoutRight."); + } else { + // View is not operatable because layout is not in LayoutLeft or + // LayoutRight + static_assert( + !KokkosFFT::Impl::is_operatable_view_v<ExecutionSpace1, ViewType>, + "Layout must be either LayoutLeft or LayoutRight."); + } + } else { + // View is not operatable because the base floating point type is not in + // float or double + static_assert( + !KokkosFFT::Impl::is_operatable_view_v<ExecutionSpace1, ViewType>, + "Base value type must be float or double"); + } + } else { + // View is not operatable because it is not accessible from + // ExecutionSpace + static_assert( + !KokkosFFT::Impl::is_operatable_view_v<ExecutionSpace1, ViewType>, + "ExecutionSpace cannot access data in ViewType"); } } @@ -168,3 +311,330 @@ TYPED_TEST(RealAndComplexViewTypes, admissible_view_type) { test_admissible_view_type<real_type, layout_type>(); test_admissible_view_type<complex_type, layout_type>(); } + +TYPED_TEST(RealAndComplexViewTypes, operatable_view_type) { + using real_type = typename TestFixture::real_type; + using complex_type = typename TestFixture::complex_type; + using layout_type = typename TestFixture::layout_type; + using host_space = Kokkos::DefaultHostExecutionSpace; + using device_space = Kokkos::DefaultExecutionSpace; + + test_operatable_view_type<host_space, host_space, real_type, layout_type>(); + test_operatable_view_type<host_space, device_space, real_type, layout_type>(); + test_operatable_view_type<device_space, host_space, real_type, layout_type>(); + test_operatable_view_type<device_space, device_space, real_type, + layout_type>(); + + test_operatable_view_type<host_space, host_space, complex_type, + layout_type>(); + test_operatable_view_type<host_space, device_space, complex_type, + layout_type>(); + test_operatable_view_type<device_space, host_space, complex_type, + layout_type>(); + test_operatable_view_type<device_space, device_space, complex_type, + layout_type>(); +} + +// Tests for multiple Views +template <typename RealType1, typename RealType2> +void test_have_same_base_floating_point_type() { + using real_type1 = RealType1; + using real_type2 = RealType2; + using complex_type1 = Kokkos::complex<real_type1>; + using complex_type2 = Kokkos::complex<real_type2>; + + using RealViewType1 = Kokkos::View<real_type1*>; + using ComplexViewType1 = Kokkos::View<complex_type1*>; + using RealViewType2 = Kokkos::View<real_type2*>; + using ComplexViewType2 = Kokkos::View<complex_type2*>; + + // Tests that Values or Views have the same base floating point type + if constexpr (std::is_same_v<real_type1, real_type2>) { + // Values must have the same base floating point type + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<real_type1, + complex_type1>, + "Values must have the same base floating point type"); + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<complex_type1, + real_type2>, + "Values must have the same base floating point type"); + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<complex_type1, + complex_type2>, + "Values must have the same base floating point type"); + + // Views must have the same base floating point type + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<RealViewType1, + RealViewType2>, + "ViewTypes must have the same base floating point type"); + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<RealViewType1, + ComplexViewType2>, + "ViewTypes must have the same base floating point type"); + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<ComplexViewType1, + RealViewType2>, + "ViewTypes must have the same base floating point type"); + static_assert( + KokkosFFT::Impl::have_same_base_floating_point_type_v<ComplexViewType1, + ComplexViewType2>, + "ViewTypes must have the same base floating point type"); + } else { + // Values must not have the same base floating point type + static_assert( + !KokkosFFT::Impl::have_same_base_floating_point_type_v<complex_type1, + real_type2>, + "Values must not have the same base floating point type"); + static_assert( + !KokkosFFT::Impl::have_same_base_floating_point_type_v<complex_type1, + complex_type2>, + "Values must not have the same base floating point type"); + + // Views must not have the same base floating point type + static_assert( + !KokkosFFT::Impl::have_same_base_floating_point_type_v<RealViewType1, + RealViewType2>, + "ViewTypes must not have the same base floating point type"); + static_assert(!KokkosFFT::Impl::have_same_base_floating_point_type_v< + RealViewType1, ComplexViewType2>, + "ViewTypes must not have the same base floating point type"); + static_assert( + !KokkosFFT::Impl::have_same_base_floating_point_type_v<ComplexViewType1, + RealViewType2>, + "ViewTypes must not have the same base floating point type"); + static_assert(!KokkosFFT::Impl::have_same_base_floating_point_type_v< + ComplexViewType1, ComplexViewType2>, + "ViewTypes must not have the same base floating point type"); + } +} + +template <typename LayoutType1, typename LayoutType2> +void test_have_same_layout() { + using RealType = double; + using ViewType1 = Kokkos::View<RealType*, LayoutType1>; + using ViewType2 = Kokkos::View<RealType*, LayoutType2>; + + // Tests that Views have the same layout + if constexpr (std::is_same_v<LayoutType1, LayoutType2>) { + // Views must have the same layout + static_assert(KokkosFFT::Impl::have_same_layout_v<ViewType1, ViewType2>, + "ViewTypes must have the same layout"); + } else { + // Views must not have the same layout + static_assert(!KokkosFFT::Impl::have_same_layout_v<ViewType1, ViewType2>, + "ViewTypes must not have the same layout"); + } +} + +template <typename LayoutType1, typename LayoutType2> +void test_have_same_rank() { + using RealType = double; + using DynamicRank1ViewType = Kokkos::View<RealType*, LayoutType1>; + using DynamicRank2ViewType = Kokkos::View<RealType**, LayoutType1>; + using StaticRank1ViewType = Kokkos::View<RealType[3], LayoutType2>; + using StaticRank2ViewType = Kokkos::View<RealType[2][5], LayoutType2>; + using DynamicStaticRank2ViewType = Kokkos::View<RealType* [5], LayoutType1>; + + // Views must have the same rank + static_assert(KokkosFFT::Impl::have_same_rank_v<DynamicRank1ViewType, + StaticRank1ViewType>, + "ViewTypes must have the same rank"); + static_assert(KokkosFFT::Impl::have_same_rank_v<DynamicRank2ViewType, + StaticRank2ViewType>, + "ViewTypes must have the same rank"); + static_assert(KokkosFFT::Impl::have_same_rank_v<DynamicRank2ViewType, + DynamicStaticRank2ViewType>, + "ViewTypes must have the same rank"); + + // Views must not have the same rank + static_assert(!KokkosFFT::Impl::have_same_rank_v<DynamicRank1ViewType, + DynamicRank2ViewType>, + "ViewTypes must not have the same rank"); + static_assert(!KokkosFFT::Impl::have_same_rank_v<DynamicRank1ViewType, + StaticRank2ViewType>, + "ViewTypes must not have the same rank"); + static_assert(!KokkosFFT::Impl::have_same_rank_v<DynamicRank1ViewType, + DynamicStaticRank2ViewType>, + "ViewTypes must not have the same rank"); + static_assert(!KokkosFFT::Impl::have_same_rank_v<StaticRank1ViewType, + DynamicRank2ViewType>, + "ViewTypes must not have the same rank"); + static_assert(!KokkosFFT::Impl::have_same_rank_v<StaticRank1ViewType, + StaticRank2ViewType>, + "ViewTypes must not have the same rank"); + static_assert(!KokkosFFT::Impl::have_same_rank_v<StaticRank1ViewType, + DynamicStaticRank2ViewType>, + "ViewTypes must not have the same rank"); +} + +// \brief Test if two Views are operatable +// \tparam ExecutionSpace1 Execution space for the device +// \tparam ExecutionSpace2 Execution space for the View memory space +// \tparam RealType1 Base Real Value type of the View1 +// \tparam LayoutType1 Layout type of the View1 +// \tparam RealType2 Base Real Value type of the View2 +// \tparam LayoutType2 Layout type of the View2 +template <typename ExecutionSpace1, typename ExecutionSpace2, + typename RealType1, typename LayoutType1, typename RealType2, + typename LayoutType2> +void test_are_operatable_views() { + using real_type1 = RealType1; + using real_type2 = RealType2; + using complex_type1 = Kokkos::complex<real_type1>; + using complex_type2 = Kokkos::complex<real_type2>; + + using RealViewType1 = Kokkos::View<real_type1*, LayoutType1, ExecutionSpace2>; + using ComplexViewType1 = + Kokkos::View<complex_type1*, LayoutType1, ExecutionSpace2>; + using RealViewType2 = Kokkos::View<real_type2*, LayoutType2, ExecutionSpace2>; + using ComplexViewType2 = + Kokkos::View<complex_type2*, LayoutType2, ExecutionSpace2>; + using RealViewType3 = + Kokkos::View<real_type2**, LayoutType2, ExecutionSpace2>; + using ComplexViewType3 = + Kokkos::View<complex_type2* [3], LayoutType2, ExecutionSpace2>; + + // Tests that the Views are accessible from the ExecutionSpace + if constexpr (Kokkos::SpaceAccessibility< + ExecutionSpace1, + typename RealViewType1::memory_space>::accessible) { + // Tests that the Views have the same base floating point type in float or + // double + if constexpr (std::is_same_v<RealType1, RealType2> && + (std::is_same_v<RealType1, float> || + std::is_same_v<RealType1, double>)) { + // Tests that the Views have the same layout in LayoutLeft or LayoutRight + if constexpr (std::is_same_v<LayoutType1, LayoutType2> && + (std::is_same_v<LayoutType1, Kokkos::LayoutLeft> || + std::is_same_v<LayoutType1, Kokkos::LayoutRight>)) { + // Tests that the Views are operatable if they have the same rank + static_assert(KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, RealViewType2>, + "InViewType and OutViewType must have the same rank"); + static_assert(KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, ComplexViewType2>, + "InViewType and OutViewType must have the same rank"); + static_assert(KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, RealViewType2>, + "InViewType and OutViewType must have the same rank"); + static_assert(KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, ComplexViewType2>, + "InViewType and OutViewType must have the same rank"); + + // Tests that the Views are not operatable if the ranks are not the same + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, RealViewType3>, + "InViewType and OutViewType must have the same rank"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, ComplexViewType3>, + "InViewType and OutViewType must have the same rank"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, RealViewType3>, + "InViewType and OutViewType must have the same rank"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, ComplexViewType3>, + "InViewType and OutViewType must have the same rank"); + } else { + // Views are not operatable because they do not have the same layout in + // LayoutLeft or LayoutRight + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, RealViewType2>, + "Layouts are not identical or one of them is not " + "LayoutLeft or LayoutRight"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, ComplexViewType2>, + "Layouts are not identical or one of them is not " + "LayoutLeft or LayoutRight"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, RealViewType2>, + "Layouts are not identical or one of them is not " + "LayoutLeft or LayoutRight"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, ComplexViewType2>, + "Layouts are not identical or one of them is not " + "LayoutLeft or LayoutRight"); + } + } else { + // Views are not operatable because they do not have the same base + // floating point type in float or double + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, RealViewType2>, + "Base value types are not identical or one of them is not " + "float or double"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, RealViewType1, ComplexViewType2>, + "Base value types are not identical or one of them is not " + "float or double"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, RealViewType2>, + "Base value types are not identical or one of them is not " + "float or double"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, ComplexViewType2>, + "Base value types are not identical or one of them is not " + "float or double"); + } + } else { + // Views are not operatable because they are not accessible from + // ExecutionSpace + static_assert( + !KokkosFFT::Impl::are_operatable_views_v<ExecutionSpace1, RealViewType1, + RealViewType2>, + "Either InViewType or OutViewType is not accessible from " + "ExecutionSpace"); + static_assert( + !KokkosFFT::Impl::are_operatable_views_v<ExecutionSpace1, RealViewType1, + ComplexViewType2>, + "Either InViewType or OutViewType is not accessible from " + "ExecutionSpace"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, RealViewType2>, + "Either InViewType or OutViewType is not accessible from " + "ExecutionSpace"); + static_assert(!KokkosFFT::Impl::are_operatable_views_v< + ExecutionSpace1, ComplexViewType1, ComplexViewType2>, + "Either InViewType or OutViewType is not accessible from " + "ExecutionSpace"); + } +} + +TYPED_TEST(PairedValueTypes, have_same_base_floating_point_type) { + using real_type1 = typename TestFixture::real_type1; + using real_type2 = typename TestFixture::real_type2; + + test_have_same_base_floating_point_type<real_type1, real_type2>(); +} + +TYPED_TEST(PairedLayoutTypes, have_same_layout) { + using layout_type1 = typename TestFixture::layout_type1; + using layout_type2 = typename TestFixture::layout_type2; + + test_have_same_layout<layout_type1, layout_type2>(); +} + +TYPED_TEST(PairedLayoutTypes, have_same_rank) { + using layout_type1 = typename TestFixture::layout_type1; + using layout_type2 = typename TestFixture::layout_type2; + + test_have_same_rank<layout_type1, layout_type2>(); +} + +TYPED_TEST(PairedViewTypes, are_operatable_views) { + using real_type1 = typename TestFixture::real_type1; + using layout_type1 = typename TestFixture::layout_type1; + using real_type2 = typename TestFixture::real_type2; + using layout_type2 = typename TestFixture::layout_type2; + using host_space = Kokkos::DefaultHostExecutionSpace; + using device_space = Kokkos::DefaultExecutionSpace; + + test_are_operatable_views<host_space, host_space, real_type1, layout_type1, + real_type2, layout_type2>(); + test_are_operatable_views<host_space, device_space, real_type1, layout_type1, + real_type2, layout_type2>(); + test_are_operatable_views<device_space, host_space, real_type1, layout_type1, + real_type2, layout_type2>(); + test_are_operatable_views<device_space, device_space, real_type1, + layout_type1, real_type2, layout_type2>(); +} diff --git a/common/unit_test/Test_Utils.hpp b/common/unit_test/Test_Utils.hpp index d30aa9b0..28fe30ca 100644 --- a/common/unit_test/Test_Utils.hpp +++ b/common/unit_test/Test_Utils.hpp @@ -5,6 +5,9 @@ #ifndef TEST_UTILS_HPP #define TEST_UTILS_HPP +#include <gtest/gtest.h> +#include <tuple> +#include <type_traits> #include "Test_Types.hpp" template <typename AViewType, typename BViewType> @@ -54,4 +57,95 @@ void display(std::string name, std::vector<T>& values) { } } -#endif \ No newline at end of file +/// Transform a sequence S to a tuple: +/// - a std::integer_sequence<T, Ints...> to a +/// std::tuple<std::integral_constant<T, Ints>...> +/// - a std::pair<T, U> to a std::tuple<T, U> +/// - identity otherwise (std::tuple) +template <class S> +struct to_tuple { + using type = S; +}; + +template <class T, T... Ints> +struct to_tuple<std::integer_sequence<T, Ints...>> { + using type = std::tuple<std::integral_constant<T, Ints>...>; +}; + +template <class T, class U> +struct to_tuple<std::pair<T, U>> { + using type = std::tuple<T, U>; +}; + +template <class S> +using to_tuple_t = typename to_tuple<S>::type; + +template <class TupleOfTuples, class Tuple> +struct for_each_tuple_cat; + +template <class... Tuples, class Tuple> +struct for_each_tuple_cat<std::tuple<Tuples...>, Tuple> { + using type = std::tuple<decltype(std::tuple_cat(std::declval<Tuples>(), + std::declval<Tuple>()))...>; +}; + +/// Construct a tuple of tuples that is the result of the concatenation of the +/// tuples in TupleOfTuples with Tuple. +template <class TupleOfTuples, class Tuple> +using for_each_tuple_cat_t = + typename for_each_tuple_cat<TupleOfTuples, Tuple>::type; + +static_assert( + std::is_same_v<for_each_tuple_cat_t<std::tuple<std::tuple<double, double>, + std::tuple<int, double>>, + std::tuple<int>>, + std::tuple<std::tuple<double, double, int>, + std::tuple<int, double, int>>>); + +static_assert( + std::is_same_v<for_each_tuple_cat_t<std::tuple<std::tuple<double, double>>, + std::tuple<int>>, + std::tuple<std::tuple<double, double, int>>>); + +template <class InTupleOfTuples, class OutTupleOfTuples> +struct cartesian_product_impl; + +template <class... HeadArgs, class... TailTuples, class OutTupleOfTuples> +struct cartesian_product_impl< + std::tuple<std::tuple<HeadArgs...>, TailTuples...>, OutTupleOfTuples> + : cartesian_product_impl< + std::tuple<TailTuples...>, + decltype(std::tuple_cat( + std::declval<for_each_tuple_cat_t<OutTupleOfTuples, + std::tuple<HeadArgs>>>()...))> { +}; + +template <class OutTupleOfTuples> +struct cartesian_product_impl<std::tuple<>, OutTupleOfTuples> { + using type = OutTupleOfTuples; +}; + +/// Generate a std::tuple cartesian product from multiple tuple-like structures +/// (std::tuple, std::integer_sequence and std::pair) Do not rely on the +/// ordering result. +template <class... InTuplesLike> +using cartesian_product_t = + typename cartesian_product_impl<std::tuple<to_tuple_t<InTuplesLike>...>, + std::tuple<std::tuple<>>>::type; + +/// Transform a std::tuple<Args...> to a testing::Types<Args...>, identity +/// otherwise +template <class T> +struct tuple_to_types { + using type = T; +}; + +template <class... Args> +struct tuple_to_types<std::tuple<Args...>> { + using type = testing::Types<Args...>; +}; + +template <class T> +using tuple_to_types_t = typename tuple_to_types<T>::type; + +#endif