GraphBLAS / graphblas-api-cpp

GraphBLAS C++ API Specification.
https://graphblas.org/graphblas-api-cpp/
10 stars 1 forks source link

Dealing with all of the variants of apply #3

Open mcmillan03 opened 3 years ago

mcmillan03 commented 3 years ago

The GraphBLAS C API v. 2.0 has the following variants and signatures (pseudocode)

Standard Variants

BinaryOp bind 1st/2nd Variants

IndexUnaryOp Variants

The C++ API way of defining the standard vector signature is as follows (matrix follows the same pattern):

    template<typename T, typename I, typename Hint, typename Allocator,
             typename MaskType,
             typename AccumulatorType,
             typename UnaryOpType,
             typename UVectorType>
    void apply(vector<T, I, Hint, Allocator>  &w,
               MaskType                 const &mask,
               AccumulatorType                 accum,
               UnaryOpType                     op,
               UVectorType              const &u,
               OutputControlEnum               outp = MERGE);

The one might define the BinaryOp+bind as follows but bind-1st and bind-second have the same templated signature and need to be dealt with with a static assert in the body as follows:

    template<typename T, typename I, typename Hint, typename Allocator,
             typename MaskT,
             typename AccumT,
             typename BinaryOpT,
             typename FirstT,
             typename SecondT>
    inline void apply(
        vector<T, I, Hint, Allocator>  &w,
        MaskT                    const &mask,
        AccumT                          accum,
        BinaryOpT                       op,
        FirstT                   const &lhs,
        SecondT                  const &rhs,
        OutputControlEnum               outp = MERGE)
    {
        // figure out if the user wants bind1st or bind2nd based on the arg types
        constexpr bool is_bind1st = is_vector_v<SecondT>;
        constexpr bool is_bind2nd = is_vector_v<FirstT>;

        // make sure only one of the types matches
        static_assert(is_bind1st ^ is_bind2nd, "apply isn't going to work");
        ...
    }

However this signature is not recommended for the C++ API was we should be using std::bind at the call site and call the standard variant as follows (for bind-2nd):

        // multiply all elements of m by damping_factor
        grb::apply(m, grb::NoMask(), grb::NoAccumulate(),
                   std::bind(grb::Times<RealT>(),
                             std::placeholders::_1,
                             damping_factor),
                   m);

Without the BinaryOp-bind variants then there is no collision with what the IndexUnaryOp variant would be, but one may still want to asser that u is a vector:

    template<typename T, typename I, typename Hint, typename Allocator,
             typename MaskType,
             typename AccumulatorType,
             typename IndexUnaryOpType,
             typename UVectorType>
    void apply(vector<T, I, Hint, Allocator>  &w,
               MaskType                 const &mask,
               AccumulatorType                 accum,
               IndexUnaryOpType                op,
               ValueType                const &s
               UVectorType              const &u,
               OutputControlEnum               outp = MERGE);

Is this approach the best? I see no way to do binding for index unary ops and call the standard variant because. While I could find a way to bind the val parameter, the element location indices need to be provided at the call site inside the implementation of apply.

BenBrock commented 2 years ago

Most of the variants of extract and apply are handled by some combination of:

1) submatrix_view, and then copying or assigning the submatrix 2) mask_view 3) grb::assign

We should take a deeper look as we go through algorithms over the next few weeks and see if we need to expand these features or add new ones to handle all of the C API's extract/assign.