From 3e0cca2a151afa79a4617dad13a509a0e6d3c0d5 Mon Sep 17 00:00:00 2001 From: Benjamin Brock Date: Mon, 5 Jun 2023 11:49:18 -0700 Subject: [PATCH 1/3] Begin implementation of spec wrapper for GBTL --- src/examples/Makefile | 16 ++ src/examples/test.cpp | 25 ++ src/interfaces/spec/grb/detail/concepts.hpp | 105 ++++++++ src/interfaces/spec/grb/detail/cpos.hpp | 107 ++++++++ src/interfaces/spec/grb/detail/detail.hpp | 5 + src/interfaces/spec/grb/detail/get.hpp | 39 +++ .../spec/grb/detail/matrix_traits.hpp | 106 ++++++++ .../spec/grb/detail/namespace_macros.hpp | 5 + src/interfaces/spec/grb/detail/tag_invoke.hpp | 86 +++++++ src/interfaces/spec/grb/grb.hpp | 4 + src/interfaces/spec/grb/matrix.hpp | 118 +++++++++ src/interfaces/spec/grb/scalar_ref.hpp | 45 ++++ src/interfaces/spec/grb/util/hints.hpp | 15 ++ src/interfaces/spec/grb/util/index.hpp | 83 +++++++ src/interfaces/spec/grb/util/matrix_entry.hpp | 233 ++++++++++++++++++ src/interfaces/spec/grb/util/util.hpp | 6 + 16 files changed, 998 insertions(+) create mode 100644 src/examples/Makefile create mode 100644 src/examples/test.cpp create mode 100644 src/interfaces/spec/grb/detail/concepts.hpp create mode 100644 src/interfaces/spec/grb/detail/cpos.hpp create mode 100644 src/interfaces/spec/grb/detail/detail.hpp create mode 100644 src/interfaces/spec/grb/detail/get.hpp create mode 100644 src/interfaces/spec/grb/detail/matrix_traits.hpp create mode 100644 src/interfaces/spec/grb/detail/namespace_macros.hpp create mode 100644 src/interfaces/spec/grb/detail/tag_invoke.hpp create mode 100644 src/interfaces/spec/grb/grb.hpp create mode 100644 src/interfaces/spec/grb/matrix.hpp create mode 100644 src/interfaces/spec/grb/scalar_ref.hpp create mode 100644 src/interfaces/spec/grb/util/hints.hpp create mode 100644 src/interfaces/spec/grb/util/index.hpp create mode 100644 src/interfaces/spec/grb/util/matrix_entry.hpp create mode 100644 src/interfaces/spec/grb/util/util.hpp diff --git a/src/examples/Makefile b/src/examples/Makefile new file mode 100644 index 0000000..5250c12 --- /dev/null +++ b/src/examples/Makefile @@ -0,0 +1,16 @@ +CXX=g++-13 + +SOURCES += $(wildcard *.cpp) +TARGETS := $(patsubst %.cpp, %, $(SOURCES)) + +INCLUDE_FLAGS = -I../ -I../graphblas/platforms/sequential -I../interfaces/spec + +CXXFLAGS = -std=c++2b -O3 $(INCLUDE_FLAGS) + +all: $(TARGETS) + +%: %.cpp + $(CXX) $(CXXFLAGS) -o $@ $^ $(CXXFLAGS) + +clean: + rm -fv $(TARGETS) diff --git a/src/examples/test.cpp b/src/examples/test.cpp new file mode 100644 index 0000000..0c9f3c9 --- /dev/null +++ b/src/examples/test.cpp @@ -0,0 +1,25 @@ +#include + +int main(int argc, char** argv) { + spec::matrix m({100, 100}); + + // Write to missing element. + m[{4, 4}] = 12; + + // Access present element. + int v = m[{4, 4}]; + std::cout << v << std::endl; + + // Access missing element. + int g = m[{4, 3}]; + std::cout << g << std::endl; + + // Write to present element. + m[{4, 3}] = 12; + + g = m[{4, 3}]; + std::cout << g << std::endl; + + + return 0; +} diff --git a/src/interfaces/spec/grb/detail/concepts.hpp b/src/interfaces/spec/grb/detail/concepts.hpp new file mode 100644 index 0000000..ce50e42 --- /dev/null +++ b/src/interfaces/spec/grb/detail/concepts.hpp @@ -0,0 +1,105 @@ + +#pragma once + +#include + +#include "matrix_traits.hpp" +#include "cpos.hpp" +#include "get.hpp" +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +template +concept TupleElementGettable = requires(T tuple) { + {GRB_SPEC_NAMESPACE::get(tuple)} -> std::convertible_to; + }; + +template +concept TupleLike = + requires { + typename std::tuple_size>::type; + requires std::same_as>)>, std::size_t>; + } && + sizeof...(Args) == std::tuple_size_v> && + [](std::index_sequence) { + return (TupleElementGettable && ...); + }(std::make_index_sequence>>()); + +template +concept MatrixEntry = TupleLike && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> TupleLike; } && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to; }; + +template +concept MutableMatrixEntry = MatrixEntry && + std::is_assignable_v(std::declval())), U>; + +template +concept MatrixRange = std::ranges::sized_range && + requires(M matrix) { + typename container_traits>; + // typename GRB_SPEC_NAMESPACE::matrix_scalar_t; + // typename GRB_SPEC_NAMESPACE::matrix_index_t; + {std::declval>()} + -> MatrixEntry, + GRB_SPEC_NAMESPACE::matrix_index_t>; + {GRB_SPEC_NAMESPACE::shape(matrix)} -> TupleLike, + GRB_SPEC_NAMESPACE::matrix_index_t>; + {GRB_SPEC_NAMESPACE::find(matrix, {GRB_SPEC_NAMESPACE::matrix_index_t{}, GRB_SPEC_NAMESPACE::matrix_index_t{}})} -> std::convertible_to>; + }; + +template +concept MutableMatrixRange = GRB_SPEC_NAMESPACE::MatrixRange && + GRB_SPEC_NAMESPACE::MutableMatrixEntry, + GRB_SPEC_NAMESPACE::matrix_scalar_t, + GRB_SPEC_NAMESPACE::matrix_index_t, + T> && + requires(M matrix, T value) { + {GRB_SPEC_NAMESPACE::insert(matrix, {{GRB_SPEC_NAMESPACE::matrix_index_t{}, GRB_SPEC_NAMESPACE::matrix_index_t{}}, value})} + -> std::same_as, bool>>; + } && + std::is_constructible_v, T>; + +template +concept MaskMatrixRange = MatrixRange && + std::is_convertible_v, bool>; + +template +concept VectorEntry = TupleLike && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> std::integral; } && + requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to; }; + +template +concept MutableVectorEntry = VectorEntry && + std::is_assignable_v(std::declval())), U>; + +template +concept VectorRange = std::ranges::sized_range && + requires(V vector) { + typename GRB_SPEC_NAMESPACE::vector_scalar_t; + typename GRB_SPEC_NAMESPACE::vector_index_t; + {std::declval>()} + -> VectorEntry, + GRB_SPEC_NAMESPACE::vector_index_t>; + {GRB_SPEC_NAMESPACE::shape(vector)} -> std::same_as>; + {GRB_SPEC_NAMESPACE::find(vector, GRB_SPEC_NAMESPACE::vector_index_t{})} -> std::convertible_to>; +}; + +template +concept MutableVectorRange = VectorRange && + MutableVectorEntry, + GRB_SPEC_NAMESPACE::vector_scalar_t, + GRB_SPEC_NAMESPACE::vector_index_t, + T> && + requires(V vector, T value) { + {GRB_SPEC_NAMESPACE::insert(vector, {GRB_SPEC_NAMESPACE::vector_index_t{}, GRB_SPEC_NAMESPACE::vector_scalar_t{}})} + -> std::same_as, bool>>; + } && + std::is_constructible_v, T>; + +template +concept MaskVectorRange = VectorRange && + std::is_convertible_v, bool>; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/cpos.hpp b/src/interfaces/spec/grb/detail/cpos.hpp new file mode 100644 index 0000000..e54a08e --- /dev/null +++ b/src/interfaces/spec/grb/detail/cpos.hpp @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "tag_invoke.hpp" +#include "matrix_traits.hpp" + +namespace GRB_SPEC_NAMESPACE { + +// Helper concepts for CPOs. + +namespace { + +template +concept has_matrix_shape = requires(T t) { {t.shape()} -> std::same_as>::type>; }; + +template +concept has_vector_shape = requires(T t) { {t.shape()} -> std::same_as>; }; + +template +concept has_find_method = requires(T t) { {t.find(std::declval::key_type>())} -> std::same_as::iterator>; }; + +template +concept has_insert_method = requires(T t) { {t.insert({std::declval::key_type>(), std::declval>()})}; }; + +template +concept has_insert_or_assign_method = requires(T t, M obj) { {t.insert_or_assign(std::declval::key_type>(), std::forward(obj))}; }; + +} // end anonymous + +inline constexpr struct shape_fn_ { + template + auto operator()(T&& x) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v || + has_matrix_shape || + has_vector_shape) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x)); + } else if constexpr(has_matrix_shape) { + return std::forward(x).shape(); + } else if constexpr(has_vector_shape) { + return std::forward(x).shape(); + } + } +} shape{}; + +inline constexpr struct size_fn_ { + template + auto operator()(T&& x) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v || + std::ranges::sized_range) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x)); + } else if constexpr(std::ranges::sized_range) { + return std::ranges::size(std::forward(x)); + } + } +} size{}; + +inline constexpr struct find_fn_ { + template + auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits::key_type key) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type> || + has_find_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), key); + } else if constexpr(has_find_method) { + return std::forward(x).find(key); + } + } +} find{}; + +inline constexpr struct insert_fn_ { + template + auto operator()(T&& x, const container_value_t& entry) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v&> || + has_insert_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v&>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), entry); + } else if constexpr(has_insert_method) { + return std::forward(x).insert(entry); + } + } +} insert{}; + +inline constexpr struct insert_or_assign_fn_ { + template + auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits::key_type key, M&& obj) const + requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type, M> || + has_insert_or_assign_method) + { + if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v::key_type, M>) { + return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward(x), key, std::forward(obj)); + } else if constexpr(has_insert_or_assign_method) { + return std::forward(x).insert_or_assign(key, std::forward(obj)); + } + } +} insert_or_assign{}; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/detail.hpp b/src/interfaces/spec/grb/detail/detail.hpp new file mode 100644 index 0000000..f17bf44 --- /dev/null +++ b/src/interfaces/spec/grb/detail/detail.hpp @@ -0,0 +1,5 @@ + +#pragma once + +#include "namespace_macros.hpp" +#include "concepts.hpp" \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/get.hpp b/src/interfaces/spec/grb/detail/get.hpp new file mode 100644 index 0000000..64027a7 --- /dev/null +++ b/src/interfaces/spec/grb/detail/get.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +template +concept method_gettable = requires(T tuple) { + {tuple. template get()} -> std::convertible_to; +}; + +template +concept adl_gettable = requires(T tuple) { + {get(tuple)} -> std::convertible_to; +}; + +template +concept std_gettable = requires(T tuple) { + {std::get(tuple)} -> std::convertible_to; +}; + +template +inline constexpr decltype(auto) get(T&& tuple) +requires(method_gettable) +{ + return std::forward(tuple). template get(); +} + +template +inline constexpr decltype(auto) get(T&& tuple) +requires(!method_gettable && std_gettable) +{ + return std::get(std::forward(tuple)); +} + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/matrix_traits.hpp b/src/interfaces/spec/grb/detail/matrix_traits.hpp new file mode 100644 index 0000000..547d62d --- /dev/null +++ b/src/interfaces/spec/grb/detail/matrix_traits.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "tag_invoke.hpp" +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +using any = std::any; + +namespace { + +template +struct get_index_type { +private: + using value_type = std::ranges::range_value_t; + using key_type = typename std::tuple_element<0, value_type>::type; +public: + using type = key_type; +}; + +template +requires requires { typename std::tuple_element<0, typename std::tuple_element<0, std::ranges::range_value_t>::type>::type; } +struct get_index_type +{ +private: + using value_type = std::ranges::range_value_t; + using key_type = typename std::tuple_element<0, value_type>::type; +public: + using type = typename std::tuple_element<0, key_type>::type; +}; +} + +template +struct container_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = std::remove_cvref_t::type>; + using reference = std::ranges::range_reference_t; +}; + +template +struct matrix_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = std::remove_cvref_t::type>; + using reference = std::ranges::range_reference_t; +}; + +template +struct vector_traits { + using size_type = std::ranges::range_size_t; + using difference_type = std::ranges::range_difference_t; + using iterator = std::ranges::iterator_t; + using value_type = std::ranges::range_value_t; + using key_type = std::remove_cvref_t::type>; + using map_type = std::remove_cvref_t::type>; + using scalar_type = std::remove_cvref_t::type>; + using index_type = key_type; + using reference = std::ranges::range_reference_t; +}; + +template +using container_scalar_t = typename container_traits::scalar_type; + +template +using container_index_t = typename container_traits::index_type; + +template +using container_value_t = typename container_traits::value_type; + +template +using container_key_t = typename container_traits::key_type; + +template +using matrix_scalar_t = typename container_traits>::scalar_type; + +template +using matrix_index_t = typename container_traits>::index_type; + +template +using vector_scalar_t = typename container_traits>::scalar_type; + +template +using vector_index_t = typename container_traits>::index_type; + +template +using elementwise_return_type_t = std::invoke_result_t, container_scalar_t>; + +} // end GRB_SPEC_NAMESPACE \ No newline at end of file diff --git a/src/interfaces/spec/grb/detail/namespace_macros.hpp b/src/interfaces/spec/grb/detail/namespace_macros.hpp new file mode 100644 index 0000000..71053a9 --- /dev/null +++ b/src/interfaces/spec/grb/detail/namespace_macros.hpp @@ -0,0 +1,5 @@ + +#pragma once + +#define GRB_SPEC_NAMESPACE spec +#define GBTL_NAMESPACE grb diff --git a/src/interfaces/spec/grb/detail/tag_invoke.hpp b/src/interfaces/spec/grb/detail/tag_invoke.hpp new file mode 100644 index 0000000..57fdadb --- /dev/null +++ b/src/interfaces/spec/grb/detail/tag_invoke.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +#include "namespace_macros.hpp" + +namespace GRB_SPEC_NAMESPACE { + +namespace _tag_invoke { + void tag_invoke(); + + struct _fn { + template + constexpr auto operator()(CPO cpo, Args&&... args) const + noexcept(noexcept(tag_invoke((CPO &&) cpo, (Args &&) args...))) + -> decltype(tag_invoke((CPO &&) cpo, (Args &&) args...)) { + return tag_invoke((CPO &&) cpo, (Args &&) args...); + } + }; + + template + using tag_invoke_result_t = decltype( + tag_invoke(std::declval(), std::declval()...)); + + using yes_type = char; + using no_type = char(&)[2]; + + template + auto try_tag_invoke(int) // + noexcept(noexcept(tag_invoke( + std::declval(), std::declval()...))) + -> decltype(static_cast(tag_invoke( + std::declval(), std::declval()...)), yes_type{}); + + template + no_type try_tag_invoke(...) noexcept(false); + + template