diff --git a/include/alp/matrix.hpp b/include/alp/matrix.hpp index e3e451c83..e8db7cd40 100644 --- a/include/alp/matrix.hpp +++ b/include/alp/matrix.hpp @@ -52,6 +52,17 @@ namespace alp { enum Backend backend = config::default_backend > class Matrix; + /** Specializations of ALP backend-agnostic type traits */ + template< typename T, typename Structure, enum Density density, typename View, typename ImfR, typename ImfC, enum Backend backend > + struct inspect_structure< Matrix< T, Structure, density, View, ImfR, ImfC, backend > > { + typedef Structure type; + }; + + template< typename T, typename Structure, enum Density density, typename View, typename ImfR, typename ImfC, enum Backend backend > + struct internal::inspect_view< Matrix< T, Structure, density, View, ImfR, ImfC, backend > > { + typedef View type; + }; + } // namespace alp #endif diff --git a/include/alp/reference/matrix.hpp b/include/alp/reference/matrix.hpp index f60b8fbf5..49bba345e 100644 --- a/include/alp/reference/matrix.hpp +++ b/include/alp/reference/matrix.hpp @@ -346,15 +346,18 @@ namespace alp { size_t ncols( const Matrix< T, reference > & m ) noexcept { return m.n; } + + /** + * Identifies any reference internal matrix is an internal container. + */ + template< typename T > + struct is_container< internal::Matrix< T, reference > > : std::true_type {}; + } // namespace internal - /** - * @brief A reference Matrix is an ALP object. - */ - template< typename T > - struct is_container< internal::Matrix< T, reference > > { - static const constexpr bool value = true; - }; + /** Identifies any reference implementation of ALP matrix as an ALP matrix. */ + template< typename T, typename Structure, enum Density density, typename View, typename ImfR, typename ImfC > + struct is_matrix< Matrix< T, Structure, density, View, ImfR, ImfC, reference > > : std::true_type {}; // Matrix-related implementation @@ -903,7 +906,7 @@ namespace alp { template < bool d > struct view_type< view::transpose, d > { - using type = Matrix< T, structures::Square, Density::Dense, View, ImfR, ImfC, reference >; + using type = Matrix< T, structures::Square, Density::Dense, view::Transpose< self_type >, ImfR, ImfC, reference >; }; /** Constructor for an original matrix. */ diff --git a/include/alp/reference/scalar.hpp b/include/alp/reference/scalar.hpp index f11c031d5..6b67bdb50 100644 --- a/include/alp/reference/scalar.hpp +++ b/include/alp/reference/scalar.hpp @@ -181,10 +181,7 @@ namespace alp { /** Identifies any reference scalar as an ALP scalar. */ template< typename T, typename Structure > - struct is_container< Scalar< T, Structure, reference > > { - /** A scalar is an ALP object. */ - static const constexpr bool value = true; - }; + struct is_scalar< Scalar< T, Structure, reference > > : std::true_type {}; namespace internal { template< typename T, typename Structure > diff --git a/include/alp/reference/vector.hpp b/include/alp/reference/vector.hpp index 835233ad9..584e228a8 100644 --- a/include/alp/reference/vector.hpp +++ b/include/alp/reference/vector.hpp @@ -269,14 +269,12 @@ namespace alp { // } }; - } // end namespace ``alp::internal'' - /** Identifies any reference vector as an ALP vector. */ - template< typename T > - struct is_container< internal::Vector< T, reference > > { - /** A reference_vector is an ALP object. */ - static const constexpr bool value = true; - }; + /** Identifies any reference internal vector as an internal container. */ + template< typename T > + struct is_container< internal::Vector< T, reference > > : std::true_type {}; + + } // end namespace ``alp::internal'' namespace internal { @@ -451,12 +449,9 @@ namespace alp { }; // class Vector with physical container - /** Identifies any reference vector as an ALP vector. */ + /** Identifies any reference ALP vector as an ALP vector. */ template< typename T, typename Structure, typename View, typename Imf > - struct is_container< Vector< T, Structure, Density::Dense, View, Imf, reference > > { - /** A reference_vector is an ALP object. */ - static const constexpr bool value = true; - }; + struct is_vector< Vector< T, Structure, Density::Dense, View, Imf, reference > > : std::true_type {}; /** * @brief Generate an original view of the input Vector. The function guarantees diff --git a/include/alp/scalar.hpp b/include/alp/scalar.hpp index 482c7907c..8aac811e6 100644 --- a/include/alp/scalar.hpp +++ b/include/alp/scalar.hpp @@ -33,6 +33,12 @@ namespace alp { template< typename T, typename Structure = structures::General, enum Backend backend = config::default_backend > class Scalar; + /** Specializations of ALP backend-agnostic type traits */ + template< typename T, typename Structure, enum Backend backend > + struct inspect_structure< Scalar< T, Structure, backend > > { + typedef Structure type; + }; + } #endif diff --git a/include/alp/type_traits.hpp b/include/alp/type_traits.hpp index 1973dcae3..94d457361 100644 --- a/include/alp/type_traits.hpp +++ b/include/alp/type_traits.hpp @@ -23,25 +23,78 @@ #ifndef _H_ALP_TYPE_TRAITS #define _H_ALP_TYPE_TRAITS +#include +#include + namespace alp { /** - * Used to inspect whether a given type is a GraphBLAS container. + * Used to inspect whether a given type is an ALP scalar. + * + * @tparam T The type to inspect. + * + * \note An arbitrary type is not an ALP scalar. + * + */ + template< typename T > + struct is_scalar : std::false_type {}; + + /** + * Used to inspect whether a given type is an ALP vector. + * + * @tparam T The type to inspect. + * + * \note An arbitrary type is not an ALP vector. + * + */ + template< typename T > + struct is_vector : std::false_type {}; + + /** + * Used to inspect whether a given type is an ALP matrix. * * @tparam T The type to inspect. * - * There are only two GraphBLAS containers: + * \note An arbitrary type is not an ALP matrix. + * + */ + template< typename T > + struct is_matrix : std::false_type {}; + + /** + * Used to inspect whether a given type is an ALP container. + * + * @tparam T The type to inspect. + * + * There are only three ALP containers: + * -# alp::Scalar, * -# alp::Vector, and * -# alp::Matrix. */ template< typename T > - struct is_container { - /** Base case: an arbitrary type is not a GraphBLAS object. */ - static const constexpr bool value = false; - }; + struct is_container : std::integral_constant< + bool, + is_scalar< T >::value || is_vector< T >::value || is_matrix< T >::value + > {}; + + namespace internal { + + /** + * Used to inspect whether a given type is an internal container. + * + * @tparam T The type to inspect. + * + * There are only two internal containers: + * -# alp::internal::Vector, and + * -# alp::internal::Matrix. + */ + template< typename T > + struct is_container : std::false_type {}; + + } // namespace internal /** - * Used to inspect whether a given type is a GraphBLAS semiring. + * Used to inspect whether a given type is an ALP semiring. * * @tparam T The type to inspect. */ @@ -52,7 +105,7 @@ namespace alp { }; /** - * Used to inspect whether a given type is a GraphBLAS monoid. + * Used to inspect whether a given type is an ALP monoid. * * @tparam T The type to inspect. */ @@ -63,7 +116,7 @@ namespace alp { }; /** - * Used to inspect whether a given type is a GraphBLAS operator. + * Used to inspect whether a given type is an ALP operator. * * @tparam T The type to inspect. */ @@ -74,11 +127,11 @@ namespace alp { }; /** - * Used to inspect whether a given type is a GraphBLAS object. + * Used to inspect whether a given type is an ALP object. * * @tparam T The type to inspect. * - * A GraphBLAS object is either a container, a semiring, a monoid, or an + * A ALP object is either a container, a semiring, a monoid, or an * operator. * * @see #is_monoid @@ -88,7 +141,7 @@ namespace alp { */ template< typename T > struct is_object { - /** A GraphBLAS object is either a container, a semiring, a monoid, or an operator. */ + /** A ALP object is either a container, a semiring, a monoid, or an operator. */ static const constexpr bool value = is_container< T >::value || is_semiring< T >::value || is_monoid< T >::value || @@ -148,6 +201,117 @@ namespace alp { } // end namespace alp::internal + /** + * Used to get a structure type of the given ALP container + * + * @tparam T The ALP container to inspect. + * + */ + template< typename Container > + struct inspect_structure {}; + + namespace internal { + + /** + * Used to get a View type of the given ALP container + * + * @tparam T The ALP container to inspect. + * + */ + template< typename Container > + struct inspect_view {}; + + /** + * Inspects whether a view corresponds to a storage-based ALP container. + * + * ALP containers can either be storage-based or functor-based. + * + * @tparam T The view to inspect. + * + * \note A Matrix is storage-based if it has + * - an original view over void, or + * - any type of view over another storage-based matrix. + * + */ + template< typename View > + struct is_view_over_storage : is_view_over_storage< + typename inspect_view< typename View::applied_to >::type + > {}; + + /** Original view over void is by definition storage based ALP container. */ + template<> + struct is_view_over_storage< view::Original< void > > : std::true_type {}; + + /** Functor views are not storage-based ALP containers. */ + template< typename LambdaType > + struct is_view_over_storage< view::Functor< LambdaType > > : std::false_type {}; + + /** + * A helper type trait for \a is_view_over_functor. + * Needed to expose the type the provided view is applied to. + * + * @tparam View The view to inspect. + * @tparam AppliedTo The type that View is applied to. + * + * @see is_view_over_functor + * + */ + template< typename View, typename AppliedTo > + struct is_view_over_functor_helper : is_view_over_functor_helper< + /** The view of the ALP container this view is applied to */ + typename inspect_view< typename View::applied_to >::type, + /** What the above view is applied to */ + typename inspect_view< typename View::applied_to >::type::applied_to + > {}; + + /** Functor view over a lambda type is by definition functor-based ALP container. */ + template< typename AppliedTo > + struct is_view_over_functor_helper< view::Functor< AppliedTo >, AppliedTo > : std::true_type {}; + + template< typename AppliedTo > + struct is_view_over_functor_helper< view::Original< void >, AppliedTo > : std::false_type {}; + + /** + * Inspects whether a view corresponds to a functor-based ALP container. + * + * ALP containers can either be storage-based or functor-based. + * + * @tparam View The view to inspect. + * + * \note A Matrix is functor-based if it has + * - a functor view over a lambda type, or + * - any type of view over another functor-based matrix. + * + * @see is_view_over_functor_helper + * + */ + template< typename View > + struct is_view_over_functor : is_view_over_functor_helper< View, typename View::applied_to > {}; + + /** + * Inspects whether a provided view is associated with an ALP container + * that allocates the container data-related memory (either the storage + * or the functor), or, in other words, + * whether it is a view over another ALP container. + * + * @tparam T The view type to inspect. + * + * The value is true if the provided view corresponds to an ALP container that + * - allocates memory for container storage, or + * - allocates memory for a functor + * The value is false otherwise, i.e., if the provided view type corresponds + * to a view over another ALP container, and, therefore, does not need to + * allocate memory for storage/functor. + * + */ + template< typename View > + struct requires_allocation : std::integral_constant< + bool, + std::is_same< view::Original< void >, View >::value || + std::is_same< view::Functor< typename View::applied_to >, View >::value + > {}; + } // namespace internal + } // namespace alp #endif diff --git a/include/alp/vector.hpp b/include/alp/vector.hpp index 24e2c086d..a3c8a7986 100644 --- a/include/alp/vector.hpp +++ b/include/alp/vector.hpp @@ -52,6 +52,18 @@ namespace alp { > class Vector; + /** Specializations of ALP backend-agnostic type traits */ + template< typename T, typename Structure, enum Density density, typename View, typename Imf, enum Backend backend > + struct inspect_structure< Vector< T, Structure, density, View, Imf, backend > > { + typedef Structure type; + }; + + template< typename T, typename Structure, enum Density density, typename View, typename Imf, enum Backend backend > + struct internal::inspect_view< Vector< T, Structure, density, View, Imf, backend > > { + typedef View type; + }; + + } #endif diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 706260c0a..00f5f9ac5 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -34,6 +34,10 @@ add_grb_executables( add15m add15m.cpp # BACKENDS alp_reference #) +add_grb_executables( alp_type_traits alp_type_traits.cpp + BACKENDS alp_reference +) + add_grb_executables( argmax argmax.cpp BACKENDS reference reference_omp bsp1d hybrid ) diff --git a/tests/unit/alp_type_traits.cpp b/tests/unit/alp_type_traits.cpp new file mode 100644 index 000000000..ce2ae5d1c --- /dev/null +++ b/tests/unit/alp_type_traits.cpp @@ -0,0 +1,146 @@ + +/* + * Copyright 2021 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include + +using namespace alp; + +template< typename MatrixType > +void ask_questions( const MatrixType & M, std::string name ) { + + using M_type = typename std::remove_const< typename std::remove_reference< decltype( M ) >::type >::type; + using M_view_type = typename internal::inspect_view< M_type >::type; + + std::cout << name << "( " << alp::nrows( M ) << ", " << alp::ncols( M ) << " )" << std::endl; + std::cout << "Is " << name << ":" << std::endl; + std::cout << "\tan ALP Matrix? " << alp::is_matrix< M_type >::value << std::endl; + std::cout << "\tan ALP Vector? " << alp::is_vector< M_type >::value << std::endl; + std::cout << "\ta structured Matrix? " << alp::is_structured_matrix< M_type >::value << std::endl; + std::cout << "\ta storage-based Matrix? " << alp::internal::is_view_over_storage< M_view_type >::value << std::endl; + std::cout << "\ta functor-based Matrix? " << alp::internal::is_view_over_functor< M_view_type >::value << std::endl; + std::cout << "\ta view over another Matrix? " << !alp::internal::requires_allocation< M_view_type >::value << std::endl; + //std::cout << name << " has the following static properties:" << std::endl; + //std::cout << "\tstructure: " << typeid(typename alp::inspect_structure< M_type >::type).name() << std::endl; + //std::cout << "\tview type: " << typeid(typename alp::inspect_view< M_type >::type).name() << std::endl; + //std::cout << "\tApplied to: " << typeid(typename alp::inspect_view< M_type >::type::applied_to).name() << std::endl; +} + + +void alp_program( const size_t & n, alp::RC & rc ) { + + /* Basic checks */ + std::cout << "Basic type traits over views. Answering questions of type:\n" + << "Does a view of a given type correspond to a storage/functor-based ALP container?\n"; + + std::cout << "\tFunctor< std::function< ... > > --> functor-based? " + << alp::internal::is_view_over_functor< alp::view::Functor< std::function< int( int, int ) > > >::value << "\n"; + std::cout << "\tOriginal< void > --> functor-based? " + << alp::internal::is_view_over_functor< alp::view::Original< void > >::value << "\n"; + std::cout << "\tFunctor< std::function< ... > > --> storage-based? " + << alp::internal::is_view_over_storage< alp::view::Functor< std::function< int( int, int ) > > >::value << "\n"; + std::cout << "\tOriginal< void > --> storage-based? " + << alp::internal::is_view_over_storage< alp::view::Original< void > >::value << "\n"; + + /* Checks with container types */ + alp::Matrix< float, alp::structures::General > M( n, n ); + alp::Matrix< float, alp::structures::Square > A( n ); + auto At = alp::get_view< alp::view::transpose >( A ); + auto Mt = alp::get_view< alp::view::transpose >( M ); + auto Mview = alp::get_view( M, alp::utils::range(0,4), alp::utils::range(0,4) ); + auto Sq_Mref = alp::get_view< alp::structures::Square > ( M ); + + ask_questions( M, "M" ); + ask_questions( A, "A" ); + + ask_questions( At, "At" ); + ask_questions( Mt, "Mt" ); + ask_questions( Mview, "Mview" ); + ask_questions( Sq_Mref, "Sq_Mref" ); + + auto v_diag = alp::get_view< alp::view::diagonal >( M ); + auto v_view1 = alp::get_view( v_diag ); + auto v_view2 = alp::get_view( v_diag, alp::utils::range(1,2) ); + std::cout << "v_diag( " << alp::getLength( v_diag ) << " )" << std::endl; + std::cout << "v_view1( " << alp::getLength( v_view1 ) << " )" << std::endl; + std::cout << "v_view2( " << alp::getLength( v_view2 ) << " )" << std::endl; + + ask_questions( v_diag, "v_diag" ); + ask_questions( v_view1, "v_view1" ); + ask_questions( v_view2, "v_view2" ); + + // TODO: temporarily comented until containers are ready + //alp::Matrix< float, alp::structures::Band< alp::Interval<-2, 5> > > BM0( n, n ); + //alp::Matrix< float, alp::structures::Band< alp::RightOpenInterval<-2> > > BM1( n, n ); + //alp::Matrix< float, alp::structures::Band< alp::LeftOpenInterval<-2> > > BM2( n, n ); + //alp::Matrix< double, alp::structures::Band< alp::Interval<-2>, alp::Interval<1>, alp::Interval<3> > > BM3( n, n ); + rc = alp::SUCCESS; + + +} + +int main( int argc, char ** argv ) { + // defaults + bool printUsage = false; + size_t in = 100; + + // error checking + if( argc > 2 ) { + printUsage = true; + } + if( argc == 2 ) { + size_t read; + std::istringstream ss( argv[ 1 ] ); + if( ! ( ss >> read ) ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( ! ss.eof() ) { + std::cerr << "Error parsing first argument\n"; + printUsage = true; + } else if( read % 2 != 0 ) { + std::cerr << "Given value for n is odd\n"; + printUsage = true; + } else { + // all OK + in = read; + } + } + if( printUsage ) { + std::cerr << "Usage: " << argv[ 0 ] << " [n]\n"; + std::cerr << " -n (optional, default is 100): an even integer, the " + "test size.\n"; + return 1; + } + + std::cout << "This is functional test " << argv[ 0 ] << "\n"; + alp::Launcher< AUTOMATIC > launcher; + alp::RC out; + if( launcher.exec( &alp_program, in, out, true ) != SUCCESS ) { + std::cerr << "Launching test FAILED\n"; + return 255; + } + if( out != SUCCESS ) { + std::cerr << "Test FAILED (" << alp::toString( out ) << ")" << std::endl; + } else { + std::cout << "Test OK" << std::endl; + } + return 0; +} +