Skip to content
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

Implement GraphBLAS C++ Spec interface on top of GBTL #54

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/examples/Makefile
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions src/examples/ewise.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <grb/grb.hpp>

int main(int argc, char** argv) {
spec::matrix<int> a({10, 10});
spec::matrix<int> b({10, 10});
spec::matrix<int> c({10, 10});

a[{2, 3}] = 12;
b[{2, 3}] = 12;

a[{1, 8}] = 7;
b[{1, 8}] = 4;

a[{7, 3}] = 2;
b[{4, 3}] = 2;

spec::ewise_intersection(c, a, b, spec::plus{});

return 0;
}
25 changes: 25 additions & 0 deletions src/examples/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <grb/grb.hpp>

int main(int argc, char** argv) {
spec::matrix<int> 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;
}
4 changes: 4 additions & 0 deletions src/interfaces/spec/grb/algorithms/algorithms.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

// #include "multiply.hpp"
#include "ewise_intersection.hpp"
30 changes: 30 additions & 0 deletions src/interfaces/spec/grb/algorithms/ewise_intersection.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include "../detail/detail.hpp"
#include "../util/util.hpp"
#include "../functional/functional.hpp"
#include "../matrix.hpp"
#include "../views/views.hpp"

namespace GRB_SPEC_NAMESPACE {

// NOTE: concepts are missing because `GRB_SPEC_NAMESPACE::matrix` does not
// satisfy iteration yet.
template <typename A,
typename B,
typename Combine,
typename C,
typename M = GRB_SPEC_NAMESPACE::full_matrix_mask<>,
typename Accumulate = GRB_SPEC_NAMESPACE::take_right<>
>
void ewise_intersection(C&& c, A&& a, B&& b, Combine&& combine, M&& mask = M{}, Accumulate&& acc = Accumulate{}, bool merge = false)
{
auto merge_enum = (merge) ? GBTL_NAMESPACE::OutputControlEnum::MERGE : GBTL_NAMESPACE::OutputControlEnum::REPLACE;
if constexpr(std::is_same_v<M, GRB_SPEC_NAMESPACE::full_matrix_mask<>>) {
GBTL_NAMESPACE::eWiseAdd(c.backend_, GBTL_NAMESPACE::NoMask(), acc, combine, a.backend_, b.backend_, merge_enum);
} else {
GBTL_NAMESPACE::eWiseAdd(c.backend_, mask.backend_, acc, combine, a.backend_, b.backend_, merge_enum);
}
}

} // GRB_SPEC_NAMESPACE
27 changes: 27 additions & 0 deletions src/interfaces/spec/grb/algorithms/multiply.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include "../detail/detail.hpp"
#include "../util/util.hpp"
#include "../matrix.hpp"

namespace GRB_SPEC_NAMESPACE {

template <MatrixRange A,
MatrixRange B,
BinaryOperator<GRB_SPEC_NAMESPACE::matrix_scalar_t<A>, GRB_SPEC_NAMESPACE::matrix_scalar_t<B>> Combine = GRB_SPEC_NAMESPACE::multiplies<>,
BinaryOperator<GRB_SPEC_NAMESPACE::elementwise_return_type_t<A, B, Combine>,
GRB_SPEC_NAMESPACE::elementwise_return_type_t<A, B, Combine>,
GRB_SPEC_NAMESPACE::elementwise_return_type_t<A, B, Combine>> Reduce = GRB_SPEC_NAMESPACE::plus<>,
MaskMatrixRange M = GRB_SPEC_NAMESPACE::full_matrix_mask<>>
auto multiply(A&& a,
B&& b,
Reduce&& reduce = Reduce{},
Combine&& combine = Combine{},
M&& mask = GRB_SPEC_NAMESPACE::full_matrix_mask())
{
using T = GRB_SPEC_NAMESPACE::elementwise_return_type_t<A, B, Combine>;
matrix<T> c(a.shape()[0], b.shape()[1]);
multiply(c, a, b, reduce, combine, mask);
}

} // end GRB_SPEC_NAMESPACE
105 changes: 105 additions & 0 deletions src/interfaces/spec/grb/detail/concepts.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@

#pragma once

#include <ranges>

#include "matrix_traits.hpp"
#include "cpos.hpp"
#include "get.hpp"
#include "namespace_macros.hpp"

namespace GRB_SPEC_NAMESPACE {

template <typename T, std::size_t I, typename U = GRB_SPEC_NAMESPACE::any>
concept TupleElementGettable = requires(T tuple) {
{GRB_SPEC_NAMESPACE::get<I>(tuple)} -> std::convertible_to<U>;
};

template <typename T, typename... Args>
concept TupleLike =
requires {
typename std::tuple_size<std::remove_cvref_t<T>>::type;
requires std::same_as<std::remove_cvref_t<decltype(std::tuple_size_v<std::remove_cvref_t<T>>)>, std::size_t>;
} &&
sizeof...(Args) == std::tuple_size_v<std::remove_cvref_t<T>> &&
[]<std::size_t... I>(std::index_sequence<I...>) {
return (TupleElementGettable<T, I, Args> && ...);
}(std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<T>>>());

template <typename Entry, typename T, typename I>
concept MatrixEntry = TupleLike<Entry, GRB_SPEC_NAMESPACE::any, GRB_SPEC_NAMESPACE::any> &&
requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> TupleLike<I, I>; } &&
requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to<T>; };

template <typename Entry, typename T, typename I, typename U>
concept MutableMatrixEntry = MatrixEntry<Entry, T, I> &&
std::is_assignable_v<decltype(GRB_SPEC_NAMESPACE::get<1>(std::declval<Entry>())), U>;

template <typename M>
concept MatrixRange = std::ranges::sized_range<M> &&
requires(M matrix) {
typename container_traits<std::remove_cvref_t<M>>;
// typename GRB_SPEC_NAMESPACE::matrix_scalar_t<M>;
// typename GRB_SPEC_NAMESPACE::matrix_index_t<M>;
{std::declval<std::ranges::range_value_t<M>>()}
-> MatrixEntry<GRB_SPEC_NAMESPACE::matrix_scalar_t<M>,
GRB_SPEC_NAMESPACE::matrix_index_t<M>>;
{GRB_SPEC_NAMESPACE::shape(matrix)} -> TupleLike<GRB_SPEC_NAMESPACE::matrix_index_t<M>,
GRB_SPEC_NAMESPACE::matrix_index_t<M>>;
{GRB_SPEC_NAMESPACE::find(matrix, {GRB_SPEC_NAMESPACE::matrix_index_t<M>{}, GRB_SPEC_NAMESPACE::matrix_index_t<M>{}})} -> std::convertible_to<std::ranges::iterator_t<M>>;
};

template <typename M, typename T>
concept MutableMatrixRange = GRB_SPEC_NAMESPACE::MatrixRange<M> &&
GRB_SPEC_NAMESPACE::MutableMatrixEntry<std::ranges::range_reference_t<M>,
GRB_SPEC_NAMESPACE::matrix_scalar_t<M>,
GRB_SPEC_NAMESPACE::matrix_index_t<M>,
T> &&
requires(M matrix, T value) {
{GRB_SPEC_NAMESPACE::insert(matrix, {{GRB_SPEC_NAMESPACE::matrix_index_t<M>{}, GRB_SPEC_NAMESPACE::matrix_index_t<M>{}}, value})}
-> std::same_as<std::pair<std::ranges::iterator_t<M>, bool>>;
} &&
std::is_constructible_v<GRB_SPEC_NAMESPACE::matrix_scalar_t<M>, T>;

template <typename M>
concept MaskMatrixRange = MatrixRange<M> &&
std::is_convertible_v<GRB_SPEC_NAMESPACE::matrix_scalar_t<M>, bool>;

template <typename Entry, typename T, typename I>
concept VectorEntry = TupleLike<Entry, GRB_SPEC_NAMESPACE::any, GRB_SPEC_NAMESPACE::any> &&
requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<0>(entry)} -> std::integral; } &&
requires(Entry entry) { {GRB_SPEC_NAMESPACE::get<1>(entry)} -> std::convertible_to<T>; };

template <typename Entry, typename T, typename I, typename U>
concept MutableVectorEntry = VectorEntry<Entry, T, I> &&
std::is_assignable_v<decltype(GRB_SPEC_NAMESPACE::get<1>(std::declval<Entry>())), U>;

template <typename V>
concept VectorRange = std::ranges::sized_range<V> &&
requires(V vector) {
typename GRB_SPEC_NAMESPACE::vector_scalar_t<V>;
typename GRB_SPEC_NAMESPACE::vector_index_t<V>;
{std::declval<std::ranges::range_value_t<V>>()}
-> VectorEntry<GRB_SPEC_NAMESPACE::vector_scalar_t<V>,
GRB_SPEC_NAMESPACE::vector_index_t<V>>;
{GRB_SPEC_NAMESPACE::shape(vector)} -> std::same_as<GRB_SPEC_NAMESPACE::vector_index_t<V>>;
{GRB_SPEC_NAMESPACE::find(vector, GRB_SPEC_NAMESPACE::vector_index_t<V>{})} -> std::convertible_to<std::ranges::iterator_t<V>>;
};

template <typename V, typename T>
concept MutableVectorRange = VectorRange<V> &&
MutableVectorEntry<std::ranges::range_reference_t<V>,
GRB_SPEC_NAMESPACE::vector_scalar_t<V>,
GRB_SPEC_NAMESPACE::vector_index_t<V>,
T> &&
requires(V vector, T value) {
{GRB_SPEC_NAMESPACE::insert(vector, {GRB_SPEC_NAMESPACE::vector_index_t<V>{}, GRB_SPEC_NAMESPACE::vector_scalar_t<V>{}})}
-> std::same_as<std::pair<std::ranges::iterator_t<V>, bool>>;
} &&
std::is_constructible_v<GRB_SPEC_NAMESPACE::vector_scalar_t<V>, T>;

template <typename M>
concept MaskVectorRange = VectorRange<M> &&
std::is_convertible_v<GRB_SPEC_NAMESPACE::vector_scalar_t<M>, bool>;

} // end GRB_SPEC_NAMESPACE
107 changes: 107 additions & 0 deletions src/interfaces/spec/grb/detail/cpos.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include <type_traits>
#include <concepts>
#include <utility>
#include <ranges>
#include <any>
#include "tag_invoke.hpp"
#include "matrix_traits.hpp"

namespace GRB_SPEC_NAMESPACE {

// Helper concepts for CPOs.

namespace {

template <typename T>
concept has_matrix_shape = requires(T t) { {t.shape()} -> std::same_as<typename std::tuple_element<0, std::ranges::range_value_t<T>>::type>; };

template <typename T>
concept has_vector_shape = requires(T t) { {t.shape()} -> std::same_as<std::ranges::range_value_t<T>>; };

template <typename T>
concept has_find_method = requires(T t) { {t.find(std::declval<typename container_traits<T>::key_type>())} -> std::same_as<typename container_traits<T>::iterator>; };

template <typename T>
concept has_insert_method = requires(T t) { {t.insert({std::declval<typename container_traits<T>::key_type>(), std::declval<container_scalar_t<T>>()})}; };

template <typename T, typename M>
concept has_insert_or_assign_method = requires(T t, M obj) { {t.insert_or_assign(std::declval<typename container_traits<T>::key_type>(), std::forward<M>(obj))}; };

} // end anonymous

inline constexpr struct shape_fn_ {
template <typename T>
auto operator()(T&& x) const
requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v<shape_fn_, T> ||
has_matrix_shape<T> ||
has_vector_shape<T>)
{
if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v<shape_fn_, T>) {
return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward<T>(x));
} else if constexpr(has_matrix_shape<T>) {
return std::forward<T>(x).shape();
} else if constexpr(has_vector_shape<T>) {
return std::forward<T>(x).shape();
}
}
} shape{};

inline constexpr struct size_fn_ {
template <typename T>
auto operator()(T&& x) const
requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v<size_fn_, T> ||
std::ranges::sized_range<T>)
{
if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v<size_fn_, T>) {
return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward<T>(x));
} else if constexpr(std::ranges::sized_range<T>) {
return std::ranges::size(std::forward<T>(x));
}
}
} size{};

inline constexpr struct find_fn_ {
template <typename T>
auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type key) const
requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v<find_fn_, T, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type> ||
has_find_method<T>)
{
if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v<find_fn_, T, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type>) {
return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward<T>(x), key);
} else if constexpr(has_find_method<T>) {
return std::forward<T>(x).find(key);
}
}
} find{};

inline constexpr struct insert_fn_ {
template <typename T>
auto operator()(T&& x, const container_value_t<T>& entry) const
requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v<insert_fn_, T, const container_value_t<T>&> ||
has_insert_method<T>)
{
if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v<insert_fn_, T, const container_value_t<T>&>) {
return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward<T>(x), entry);
} else if constexpr(has_insert_method<T>) {
return std::forward<T>(x).insert(entry);
}
}
} insert{};

inline constexpr struct insert_or_assign_fn_ {
template <typename T, typename M>
auto operator()(T&& x, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type key, M&& obj) const
requires(GRB_SPEC_NAMESPACE::is_tag_invocable_v<insert_or_assign_fn_, T, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type, M> ||
has_insert_or_assign_method<T, M>)
{
if constexpr(GRB_SPEC_NAMESPACE::is_tag_invocable_v<insert_or_assign_fn_, T, typename GRB_SPEC_NAMESPACE::container_traits<T>::key_type, M>) {
return GRB_SPEC_NAMESPACE::tag_invoke(*this, std::forward<T>(x), key, std::forward<M>(obj));
} else if constexpr(has_insert_or_assign_method<T, M>) {
return std::forward<T>(x).insert_or_assign(key, std::forward<M>(obj));
}
}
} insert_or_assign{};

} // end GRB_SPEC_NAMESPACE
6 changes: 6 additions & 0 deletions src/interfaces/spec/grb/detail/detail.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#pragma once

#include "namespace_macros.hpp"
#include "concepts.hpp"
#include "iterator_adaptor.hpp"
39 changes: 39 additions & 0 deletions src/interfaces/spec/grb/detail/get.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include <tuple>
#include <concepts>

#include "namespace_macros.hpp"

namespace GRB_SPEC_NAMESPACE {

template <typename T, std::size_t I>
concept method_gettable = requires(T tuple) {
{tuple. template get<I>()} -> std::convertible_to<GRB_SPEC_NAMESPACE::any>;
};

template <typename T, std::size_t I>
concept adl_gettable = requires(T tuple) {
{get<I>(tuple)} -> std::convertible_to<GRB_SPEC_NAMESPACE::any>;
};

template <typename T, std::size_t I>
concept std_gettable = requires(T tuple) {
{std::get<I>(tuple)} -> std::convertible_to<GRB_SPEC_NAMESPACE::any>;
};

template <std::size_t I, typename T>
inline constexpr decltype(auto) get(T&& tuple)
requires(method_gettable<T, I>)
{
return std::forward<T>(tuple). template get<I>();
}

template <std::size_t I, typename T>
inline constexpr decltype(auto) get(T&& tuple)
requires(!method_gettable<T, I> && std_gettable<T, I>)
{
return std::get<I>(std::forward<T>(tuple));
}

} // end GRB_SPEC_NAMESPACE
Loading