-
Notifications
You must be signed in to change notification settings - Fork 577
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tpetra: Replace Tpetra_DefaultNode CMake option with Tpetra_DefaultExecutionSpace #56
Comments
This blocks Issue #57. |
This change I believe fixes the logic where TriBITS was enabling packages listed in TEST_[REQUIRED|OPTIONAL]_PACKAGES even if tests and examples were not enabled. See trilinos/Trilinos#56. This fails a bunch of the TriBITS tests because it now prints what type of dependnecy ('lib' or 'test/example') the package is. New tests targeted for this use case need to be created and the existing failing tests need to be fixed.
Y'all check this out (I came up with it this morning): // mfh 29 Nov 2017
// Demonstrate aliases for backwards compatibility
// of Tpetra objects' template parameters.
// Successfully builds with GCC 7.2 (not a typo, there's no "4").
#include <iostream>
#include <type_traits>
// Stub "Node" type.
template<class KokkosDeviceType>
struct WrapperNode {
// Kokkos-style, tag this class as a node.
using node = WrapperNode<KokkosDeviceType>;
};
// Stub Kokkos execution spaces.
struct Cuda {
// Kokkos-style, tag this class as an execution space.
using execution_space = Cuda;
};
struct OpenMP {
using execution_space = OpenMP;
};
struct Serial {
using execution_space = Serial;
};
// Default "Tpetra" template parameters.
typedef double default_scalar_type;
typedef int default_local_ordinal_type;
typedef long long default_global_ordinal_type;
typedef WrapperNode<Serial> default_node_type;
// Was KOKKOS_IMPL_IS_CONCEPT
#define TPETRA_IMPL_IS_CONCEPT( CONCEPT ) \
template< typename T > struct is_ ## CONCEPT { \
private: \
template< typename , typename = std::true_type > struct have : std::false_type {}; \
template< typename U > struct have<U,typename std::is_same<U,typename U:: CONCEPT >::type> : std::true_type {}; \
public: \
enum { value = is_ ## CONCEPT::template have<T>::value }; \
};
// Define "is_node" concept.
// is_node<T>::value is true if and only if T is a WrapperNode<T> for some T.
TPETRA_IMPL_IS_CONCEPT( node )
// Define "is_execution_space" concept (Kokkos already has this).
TPETRA_IMPL_IS_CONCEPT( execution_space )
namespace Impl {
// The actual implementation of Vector.
// The four template parameters as given are the canonical ones,
// in their canonical order.
// This is how Tpetra declares its objects like Tpetra::Vector or Tpetra::CrsMatrix now.
template<class S = default_scalar_type,
class LO = default_local_ordinal_type,
class GO = default_global_ordinal_type,
class NT = default_node_type>
class Vector {
public:
static_assert (std::is_integral<LO>::value,
"LO template parameter must be an integer.");
static_assert (std::is_integral<GO>::value,
"GO template parameter must be an integer.");
static_assert (is_node<NT>::value,
"NT template parameter must be a WrapperNode<T> for some T.");
void print (std::ostream& out) const {
out << "I am a Vector!" << std::endl;
}
};
// Implementation detail.
// Tpetra will use VectorAlias to get the Impl::Vector specialization
// corresponding to the template parameters of Vector.
// Args are exactly the template parameters of Vector.
// (We may need to allow 5 for backwards compatibility; the last must be bool.)
template<class ... Args>
struct VectorAlias {
static_assert (sizeof... (Args) < 5,
"Vector must have no more than 4 template parameters");
};
// Specialization for zero template parameters.
template<>
struct VectorAlias<> {
private:
using scalar_type = default_scalar_type;
using local_ordinal_type = default_local_ordinal_type;
using global_ordinal_type = default_global_ordinal_type;
using node_type = default_node_type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 1 template parameter; must be Scalar.
template<class Scalar>
struct VectorAlias<Scalar> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using local_ordinal_type = default_local_ordinal_type;
using global_ordinal_type = default_global_ordinal_type;
using node_type = default_node_type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Map from NT to the corresponding WrapperNode type.
// NT may be a WrapperNode, a Kokkos::Device,
// or a Kokkos execution or memory space.
template<class NT>
struct NodeType {
using type =
typename std::conditional<
is_node<NT>::value,
NT,
typename std::conditional<
is_execution_space<NT>::value,
WrapperNode<NT>,
NT
>::type
>::type;
};
// Specialization for 2 template parameters; first is always Scalar.
template<class Scalar, class Second>
struct VectorAlias<Scalar, Second> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using second_type = typename std::decay<Second>::type;
// If Second is an integer, assume that it is the local ordinal type,
// for backwards compatibility. Otherwise, assume that it is the node /
// device / space type.
using local_ordinal_type =
typename std::conditional<
std::is_integral<second_type>::value,
second_type,
int>::type;
using global_ordinal_type = default_global_ordinal_type;
using node_type =
typename std::conditional<
std::is_integral<second_type>::value,
default_node_type,
typename NodeType<second_type>::type
>::type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 3 template parameters; first is always Scalar.
template<class Scalar, class Second, class Third>
struct VectorAlias<Scalar, Second, Third> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using second_type = typename std::decay<Second>::type;
using third_type = typename std::decay<Third>::type;
// Second must be an integer.
static_assert (std::is_integral<second_type>::value,
"If you give Tpetra objects three template parameters, "
"the second (an index type) must be an integer.");
// Third could be an integer, a Node, or a
// Kokkos Device / execution space / memory space.
using global_ordinal_type =
typename std::conditional<
std::is_integral<third_type>::value,
third_type,
second_type>::type;
// If the user only gives me one index type,
// assume that they meant the global index type,
// and make the local index type the default.
using local_ordinal_type =
typename std::conditional<
std::is_integral<third_type>::value,
second_type,
default_local_ordinal_type>::type;
// If Third is an integer, use the default node_type.
// Otherwise, use Third to find the node_type.
using node_type =
typename std::conditional<
std::is_integral<third_type>::value,
default_node_type,
typename NodeType<third_type>::type
>::type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 4 template parameters; first is always Scalar.
template<class Scalar, class LO, class GO, class NT>
struct VectorAlias<Scalar, LO, GO, NT> {
private:
static_assert (std::is_integral<LO>::value,
"If you give Tpetra objects four template parameters, "
"the second (the local index type) must be an integer.");
static_assert (std::is_integral<GO>::value,
"If you give Tpetra objects four template parameters, "
"the third (the global index type) must be an integer.");
using scalar_type = typename std::decay<Scalar>::type;
using local_ordinal_type = LO;
using global_ordinal_type = GO;
using node_type = typename NodeType<NT>::type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
} // namespace Impl
// This is the Vector class that users will see.
// Users put their template parameters into this class.
// Tpetra aliases this to the matching Impl::Vector specialization.
template<class ... Args>
using Vector = typename Impl::VectorAlias<Args...>::type;
int main () {
// Test is_node concept.
static_assert (is_node<default_node_type>::value, "Oops, is_node is broken");
static_assert (! is_node<Serial>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<Serial>>::value, "Oops, is_node is broken");
static_assert (! is_node<OpenMP>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<OpenMP>>::value, "Oops, is_node is broken");
static_assert (! is_node<Cuda>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<Cuda>>::value, "Oops, is_node is broken");
static_assert (! is_node<int>::value, "Oops, is_node is broken");
// Check that the Vector type alias behaves as expected.
static_assert (std::is_same<
Vector<>,
Impl::Vector<default_scalar_type, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops 0 arguments");
static_assert (std::is_same<
Vector<float>,
Impl::Vector<float, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops 1 argument");
static_assert (std::is_same<
Vector<float, short>,
Impl::Vector<float, short, default_global_ordinal_type, default_node_type>
>::value,
"Oops 2 arguments");
static_assert (std::is_same<
Vector<float, short, long>,
Impl::Vector<float, short, long, default_node_type>
>::value,
"Oops 3 arguments");
static_assert (std::is_same<
Vector<float, short, long, WrapperNode<Cuda>>,
Impl::Vector<float, short, long, WrapperNode<Cuda>>
>::value,
"Oops 4 arguments");
// Check that we're not instantiating redundant types.
static_assert (std::is_same<
Vector<>,
Vector<default_scalar_type, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, short>,
Vector<float, short, default_global_ordinal_type, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, default_global_ordinal_type, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, long, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, short, long>,
Vector<float, short, long, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, long, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
// Check that execution spaces work in place of Nodes.
static_assert (std::is_same<
Vector<float, long, Cuda>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
// Uncomment the line below to trigger the static_assert
// that forbids more than 4 template parameters.
// Vector<float, short, long, WrapperNode<Cuda>, double> v2;
// Generate some output, so stuff actually compiles.
Impl::Vector<double, int, long, WrapperNode<Serial>> v;
v.print (std::cout);
return 0;
} |
Hm, I think I can generalize this for all Tpetra classes that take the same four template parameters, using template templates. |
I AM LORD OF THE TEMPLATES #include <iostream>
#include <type_traits>
// Stub "Node" type.
template<class KokkosDeviceType>
struct WrapperNode {
// Kokkos-style, tag this class as a node.
using node = WrapperNode<KokkosDeviceType>;
};
// Stub Kokkos execution spaces.
struct Cuda {
// Kokkos-style, tag this class as an execution space.
using execution_space = Cuda;
};
struct OpenMP {
using execution_space = OpenMP;
};
struct Serial {
using execution_space = Serial;
};
// Default "Tpetra" template parameters.
typedef double default_scalar_type;
typedef int default_local_ordinal_type;
typedef long long default_global_ordinal_type;
typedef WrapperNode<Serial> default_node_type;
// Was KOKKOS_IMPL_IS_CONCEPT
#define TPETRA_IMPL_IS_CONCEPT( CONCEPT ) \
template< typename T > struct is_ ## CONCEPT { \
private: \
template< typename , typename = std::true_type > struct have : std::false_type {}; \
template< typename U > struct have<U,typename std::is_same<U,typename U:: CONCEPT >::type> : std::true_type {}; \
public: \
enum { value = is_ ## CONCEPT::template have<T>::value }; \
};
// Define "is_node" concept.
// is_node<T>::value is true if and only if T is a WrapperNode<T> for some T.
TPETRA_IMPL_IS_CONCEPT( node )
// Define "is_execution_space" concept (Kokkos already has this).
TPETRA_IMPL_IS_CONCEPT( execution_space )
namespace Impl {
// The actual implementation of Vector.
// The four template parameters as given are the canonical ones,
// in their canonical order.
// This is how Tpetra declares its objects like Tpetra::Vector or Tpetra::CrsMatrix now.
template<class S = default_scalar_type,
class LO = default_local_ordinal_type,
class GO = default_global_ordinal_type,
class NT = default_node_type>
class Vector {
public:
typedef S scalar_type;
typedef LO local_ordinal_type;
typedef GO global_ordinal_type;
typedef NT node_type;
static_assert (std::is_integral<LO>::value,
"LO template parameter must be an integer.");
static_assert (std::is_integral<GO>::value,
"GO template parameter must be an integer.");
static_assert (is_node<NT>::value,
"NT template parameter must be a WrapperNode<T> for some T.");
void print (std::ostream& out) const {
out << "I am a Vector!" << std::endl;
}
};
// Implementation detail.
// Tpetra will use FourArgAlias to get the Impl::Vector specialization
// corresponding to the template parameters of Vector.
// First template parameter will be Impl::Vector.
// Remaining template parameter(s) (if any) Args
// are exactly the template parameters of Vector.
// (We may need to allow 5 for backwards compatibility; the last must be bool.)
template<template <class S, class LO, class GO, class NT> class Object,
class ... Args>
struct FourArgAlias {
static_assert (sizeof... (Args) < 5,
"This class must have no more than 4 template parameters");
};
// Specialization for zero template parameters.
template<template <class S, class LO, class GO, class NT> class Object>
struct FourArgAlias<Object> {
private:
using scalar_type = default_scalar_type;
using local_ordinal_type = default_local_ordinal_type;
using global_ordinal_type = default_global_ordinal_type;
using node_type = default_node_type;
public:
using type = Object<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 1 template parameter; must be Scalar.
template<template <class S, class LO, class GO, class NT> class Object,
class Scalar>
struct FourArgAlias<Object, Scalar> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using local_ordinal_type = default_local_ordinal_type;
using global_ordinal_type = default_global_ordinal_type;
using node_type = default_node_type;
public:
using type = Object<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Map from NT to the corresponding WrapperNode type.
// NT may be a WrapperNode, a Kokkos::Device,
// or a Kokkos execution or memory space.
template<class NT>
struct NodeType {
using type =
typename std::conditional<
is_node<NT>::value,
NT,
typename std::conditional<
is_execution_space<NT>::value,
WrapperNode<NT>,
NT
>::type
>::type;
};
// Specialization for 2 template parameters; first is always Scalar.
template<template <class S, class LO, class GO, class NT> class Object,
class Scalar,
class Second>
struct FourArgAlias<Object, Scalar, Second> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using second_type = typename std::decay<Second>::type;
// If Second is an integer, assume that it is the local ordinal type,
// for backwards compatibility. Otherwise, assume that it is the node /
// device / space type.
using local_ordinal_type =
typename std::conditional<
std::is_integral<second_type>::value,
second_type,
int>::type;
using global_ordinal_type = default_global_ordinal_type;
using node_type =
typename std::conditional<
std::is_integral<second_type>::value,
default_node_type,
typename NodeType<second_type>::type
>::type;
public:
using type = Object<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 3 template parameters; first is always Scalar.
template<template <class S, class LO, class GO, class NT> class Object,
class Scalar,
class Second,
class Third>
struct FourArgAlias<Object, Scalar, Second, Third> {
private:
using scalar_type = typename std::decay<Scalar>::type;
using second_type = typename std::decay<Second>::type;
using third_type = typename std::decay<Third>::type;
// Second must be an integer.
static_assert (std::is_integral<second_type>::value,
"If you give Tpetra objects three template parameters, "
"the second (an index type) must be an integer.");
// Third could be an integer, a Node, or a
// Kokkos Device / execution space / memory space.
using global_ordinal_type =
typename std::conditional<
std::is_integral<third_type>::value,
third_type,
second_type>::type;
// If the user only gives me one index type,
// assume that they meant the global index type,
// and make the local index type the default.
using local_ordinal_type =
typename std::conditional<
std::is_integral<third_type>::value,
second_type,
default_local_ordinal_type>::type;
// If Third is an integer, use the default node_type.
// Otherwise, use Third to find the node_type.
using node_type =
typename std::conditional<
std::is_integral<third_type>::value,
default_node_type,
typename NodeType<third_type>::type
>::type;
public:
using type = Object<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
// Specialization for 4 template parameters; first is always Scalar.
template<template <class S, class LO, class GO, class NT> class Object,
class Scalar,
class Second,
class Third,
class Fourth>
struct FourArgAlias<Object, Scalar, Second, Third, Fourth> {
private:
static_assert (std::is_integral<Second>::value,
"If you give Tpetra objects four template parameters, "
"the second (the local index type) must be an integer.");
static_assert (std::is_integral<Third>::value,
"If you give Tpetra objects four template parameters, "
"the third (the global index type) must be an integer.");
using scalar_type = typename std::decay<Scalar>::type;
using local_ordinal_type = Second;
using global_ordinal_type = Third;
using node_type = typename NodeType<Fourth>::type;
public:
using type = Impl::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type>;
};
} // namespace Impl
// This is the Vector class that users will see.
// Users put their template parameters into this class.
// Tpetra aliases this to the matching Impl::Vector specialization.
//template<class ... Args>
//using Vector = typename Impl::VectorAlias<Args...>::type;
template<class ... Args>
using Vector = typename Impl::FourArgAlias<Impl::Vector, Args...>::type;
int main () {
// Test is_node concept.
static_assert (is_node<default_node_type>::value, "Oops, is_node is broken");
static_assert (! is_node<Serial>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<Serial>>::value, "Oops, is_node is broken");
static_assert (! is_node<OpenMP>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<OpenMP>>::value, "Oops, is_node is broken");
static_assert (! is_node<Cuda>::value, "Oops, is_node is broken");
static_assert (is_node<WrapperNode<Cuda>>::value, "Oops, is_node is broken");
static_assert (! is_node<int>::value, "Oops, is_node is broken");
// Check that the Vector type alias behaves as expected.
static_assert (std::is_same<
Vector<>,
Impl::Vector<default_scalar_type, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops 0 arguments");
static_assert (std::is_same<
Vector<float>,
Impl::Vector<float, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops 1 argument");
static_assert (std::is_same<
Vector<float, short>,
Impl::Vector<float, short, default_global_ordinal_type, default_node_type>
>::value,
"Oops 2 arguments");
static_assert (std::is_same<
Vector<float, short, long>,
Impl::Vector<float, short, long, default_node_type>
>::value,
"Oops 3 arguments");
static_assert (std::is_same<
Vector<float, short, long, WrapperNode<Cuda>>,
Impl::Vector<float, short, long, WrapperNode<Cuda>>
>::value,
"Oops 4 arguments");
// Check that we're not instantiating redundant types.
static_assert (std::is_same<
Vector<>,
Vector<default_scalar_type, default_local_ordinal_type, default_global_ordinal_type, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, short>,
Vector<float, short, default_global_ordinal_type, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, default_global_ordinal_type, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, long, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, short, long>,
Vector<float, short, long, default_node_type>
>::value,
"Oops not the same type"
);
static_assert (std::is_same<
Vector<float, long, WrapperNode<Cuda>>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
// Check that execution spaces work in place of Nodes.
static_assert (std::is_same<
Vector<float, long, Cuda>,
Vector<float, default_local_ordinal_type, long, WrapperNode<Cuda>>
>::value,
"Oops not the same type"
);
// Uncomment the line below to trigger the static_assert
// that forbids more than 4 template parameters.
// Vector<float, short, long, WrapperNode<Cuda>, double> v2;
// Generate some output, so stuff actually compiles.
Impl::Vector<double, int, long, WrapperNode<Serial>> v;
v.print (std::cout);
return 0;
} |
This issue has had no activity for 365 days and is marked for closure. It will be closed after an additional 30 days of inactivity. |
This issue was closed due to inactivity for 395 days. |
@trilinos/tpetra @rppawlo @crtrott
Tpetra will get rid of Node altogether at some point, and tie itself completely to Kokkos' execution and memory spaces. It would make sense to deprecate the Tpetra_DefaultNode CMake option and replace it with Tpetra_DefaultExecutionSpace.
The text was updated successfully, but these errors were encountered: