示例#1
0
GrB_Info read_matrix        // read a double-precision or boolean matrix
(
    GrB_Matrix *A_output,   // handle of matrix to create
    FILE *f,                // file to read the tuples from
    bool make_symmetric,    // if true, return A as symmetric
    bool no_self_edges,     // if true, then remove self edges from A
    bool one_based,         // if true, input matrix is 1-based
    bool boolean,           // if true, input is GrB_BOOL, otherwise GrB_FP64
    bool pr                 // if true, print status to stdout
)
{

    int64_t len = 256 ;
    int64_t ntuples = 0 ;
    double x ;
    GrB_Index nvals ;

    //--------------------------------------------------------------------------
    // set all pointers to NULL so that FREE_ALL can free everything safely
    //--------------------------------------------------------------------------

    GrB_Matrix C = NULL, A = NULL, B = NULL ;
    GrB_Descriptor dt1 = NULL, dt2 = NULL ;
    GrB_UnaryOp scale2_op = NULL ;

    //--------------------------------------------------------------------------
    // allocate initial space for tuples
    //--------------------------------------------------------------------------

    size_t xsize = ((boolean) ? sizeof (bool) : sizeof (double)) ;
    GrB_Index *I = malloc (len * sizeof (int64_t)), *I2 = NULL ;
    GrB_Index *J = malloc (len * sizeof (int64_t)), *J2 = NULL ;
    void *X = malloc (len * xsize) ;
    bool *Xbool ;
    double *Xdouble ;
    void *X2 = NULL ;
    if (I == NULL || J == NULL || X == NULL)
    {
        // out of memory
        if (pr) printf ("out of memory for initial tuples\n") ;
        FREE_ALL ;
        return (GrB_OUT_OF_MEMORY) ;
    }

    Xbool   = (bool   *) X ;
    Xdouble = (double *) X ;

    //--------------------------------------------------------------------------
    // read in the tuples from stdin, one per line
    //--------------------------------------------------------------------------

    // format warnings vary with compilers, so read in as double
    double i2, j2 ;
    while (fscanf (f, "%lg %lg %lg\n", &i2, &j2, &x) != EOF)
    {
        int64_t i = (int64_t) i2 ;
        int64_t j = (int64_t) j2 ;
        if (ntuples >= len)
        {
            I2 = realloc (I, 2 * len * sizeof (int64_t)) ;
            J2 = realloc (J, 2 * len * sizeof (int64_t)) ;
            X2 = realloc (X, 2 * len * xsize) ;
            if (I2 == NULL || J2 == NULL || X2 == NULL)
            {
                if (pr) printf ("out of memory for tuples\n") ;
                FREE_ALL ;
                return (GrB_OUT_OF_MEMORY) ;
            }
            I = I2 ; I2 = NULL ;
            J = J2 ; J2 = NULL ;
            X = X2 ; X2 = NULL ;
            len = len * 2 ;
            Xbool   = (bool   *) X ;
            Xdouble = (double *) X ;
        }
        if (one_based)
        {
            i-- ;
            j-- ;
        }
        I [ntuples] = i ;
        J [ntuples] = j ;
        if (boolean)
        {
            Xbool [ntuples] = (x != 0) ;
        }
        else
        {
            Xdouble [ntuples] = x ;
        }
        ntuples++ ;
    }

    //--------------------------------------------------------------------------
    // find the dimensions
    //--------------------------------------------------------------------------

    if (pr) printf ("ntuples: %.16g\n", (double) ntuples) ;
    int64_t nrows = 0 ;
    int64_t ncols = 0 ;
    for (int64_t k = 0 ; k < ntuples ; k++)
    {
        nrows = MAX (nrows, I [k]) ;
        ncols = MAX (ncols, J [k]) ;
    }
    nrows++ ;
    ncols++ ;

    if (pr) printf ("nrows %.16g ncols %.16g\n",
        (double) nrows, (double) ncols) ;

    //--------------------------------------------------------------------------
    // prune self edges
    //--------------------------------------------------------------------------

    // but not if creating the augmented system aka a bipartite graph
    double tic [2], t1 ;
    simple_tic (tic) ;
    if (no_self_edges && ! (make_symmetric && nrows != ncols))
    {
        int64_t ntuples2 = 0 ;
        for (int64_t k = 0 ; k < ntuples ; k++)
        {
            if (I [k] != J [k])
            {
                // keep this off-diagonal edge
                I [ntuples2] = I [k] ;
                J [ntuples2] = J [k] ;
                if (boolean)
                {
                    Xbool [ntuples2] = Xbool [k] ;
                }
                else
                {
                    Xdouble [ntuples2] = Xdouble [k] ;
                }
                ntuples2++ ;
            }
        }
        ntuples = ntuples2 ;
    }
    t1 = simple_toc (tic) ;
    if (pr) printf ("time to prune self-edges: %12.6f\n", t1) ;

    //--------------------------------------------------------------------------
    // build the matrix, summing up duplicates, and then free the tuples
    //--------------------------------------------------------------------------

    GrB_Type xtype ;
    GrB_BinaryOp xop, xop2 ;
    if (boolean)
    {
        xtype = GrB_BOOL ;
        xop   = GrB_LOR ;
        xop2  = GrB_FIRST_BOOL ;
    }
    else
    {
        xtype = GrB_FP64 ;
        xop   = GrB_PLUS_FP64 ;
        xop2  = GrB_FIRST_FP64 ;
    }

    simple_tic (tic) ;
    GrB_Info info ;
    OK (GrB_Matrix_new (&C, xtype, nrows, ncols)) ;

    if (boolean)
    {
        OK (GrB_Matrix_build (C, I, J, Xbool, ntuples, xop)) ;
    }
    else
    {
        OK (GrB_Matrix_build (C, I, J, Xdouble, ntuples, xop)) ;
    }
    t1 = simple_toc (tic) ;
    if (pr) printf ("time to build the graph with GrB_Matrix_build: %12.6f\n",
        t1) ;

#ifdef TEST_SETELEMENT
    {
        // This is just for testing performance of GrB_setElement and comparing
        // with GrB_build.  It is not needed if this function is used in 
        // production.

        // setElement will be just about as fast as build (perhaps 10% to 50%
        // more time) with non-blocking mode.  If blocking mode is enabled,
        // setElement will be extremely and painfully slow since the matrix is
        // rebuilt every time a single entry is added.

        simple_tic (tic) ;
        OK (GrB_Matrix_new (&B, xtype, nrows, ncols)) ;
        for (int64_t k = 0 ; k < ntuples ; k++)
        {
            // B (I[k], J[k]) = X [k]
            GrB_Matrix_setElement (B, X [k], I [k], J [k]) ;
        }
        // force completion of B
        GrB_Matrix_nvals (&nvals, B) ;
        double t2 = simple_toc (tic) ;
        if (pr) printf ("time to build the graph with GrB_setElement:"
            "   %12.6f\n", t2) ;
        GrB_free (&B) ;
    }
#endif

    free (I) ; I = NULL ;
    free (J) ; J = NULL ;
    free (X) ; X = NULL ;

    //--------------------------------------------------------------------------
    // construct the descriptors
    //--------------------------------------------------------------------------

    // descriptor dt2: transpose the 2nd input
    OK (GrB_Descriptor_new (&dt2)) ;
    OK (GrB_Descriptor_set (dt2, GrB_INP1, GrB_TRAN)) ;

    // descriptor dt1: transpose the 1st input
    OK (GrB_Descriptor_new (&dt1)) ;
    OK (GrB_Descriptor_set (dt1, GrB_INP0, GrB_TRAN)) ;

    //--------------------------------------------------------------------------
    // create the output matrix
    //--------------------------------------------------------------------------

    if (make_symmetric)
    {

        //----------------------------------------------------------------------
        // ensure the matrix is symmetric
        //----------------------------------------------------------------------

        if (pr) printf ("make symmetric\n") ;
        if (nrows == ncols)
        {

            //------------------------------------------------------------------
            // A = (C+C')/2
            //------------------------------------------------------------------

            if (pr) printf ("A = (C+C')/2\n") ;
            double tic [2], t ;
            simple_tic (tic) ;
            OK (GrB_Matrix_new (&A, xtype, nrows, nrows)) ;
            OK (GrB_eWiseAdd (A, NULL, NULL, xop, C, C, dt2)) ;
            OK (GrB_free (&C)) ;

            if (boolean)
            {
                *A_output = A ;
                A = NULL ;
            }
            else
            {
                OK (GrB_Matrix_new (&C, xtype, nrows, nrows)) ;
                OK (GrB_UnaryOp_new (&scale2_op, scale2, xtype, xtype)) ;
                OK (GrB_apply (C, NULL, NULL, scale2_op, A, NULL)) ;
                OK (GrB_free (&A)) ;
                OK (GrB_free (&scale2_op)) ;
                *A_output = C ;
                C = NULL ;
            }

            t = simple_toc (tic) ;
            if (pr) printf ("A = (C+C')/2 time %12.6f\n", t) ;

        }
        else
        {

            //------------------------------------------------------------------
            // A = [0 C ; C' 0], a bipartite graph
            //------------------------------------------------------------------

            // no self edges will exist
            if (pr) printf ("A = [0 C ; C' 0], a bipartite graph\n") ;

            double tic [2], t ;
            simple_tic (tic) ;

            int64_t n = nrows + ncols ;
            OK (GrB_Matrix_new (&A, xtype, n, n)) ;

            GrB_Index I_range [3], J_range [3] ;

            I_range [GxB_BEGIN] = 0 ;
            I_range [GxB_END  ] = nrows-1 ;

            J_range [GxB_BEGIN] = nrows ;
            J_range [GxB_END  ] = ncols+nrows-1 ;

            // A (nrows:n-1, 0:nrows-1) += C'
            OK (GrB_assign (A, NULL, xop2, // or NULL,
                C, J_range, GxB_RANGE, I_range, GxB_RANGE, dt1)) ;

            // A (0:nrows-1, nrows:n-1) += C
            OK (GrB_assign (A, NULL, xop2, // or NULL,
                C, I_range, GxB_RANGE, J_range, GxB_RANGE, NULL)) ;

            // force completion; if this statement does not appear, the
            // timing will not account for the final build, which would be
            // postponed until A is used by the caller in another GraphBLAS
            // operation.
            GrB_Matrix_nvals (&nvals, A) ;
            t = simple_toc (tic) ;

            if (pr) printf ("time to construct augmented system: %12.6f\n", t) ;
            *A_output = A ;
            // set A to NULL so the FREE_ALL macro does not free *A_output
            A = NULL ;

        }
    }
    else
    {

        //----------------------------------------------------------------------
        // return the matrix as-is
        //----------------------------------------------------------------------

        if (pr) printf ("leave A as-is\n") ;
        *A_output = C ;
        // set C to NULL so the FREE_ALL macro does not free *A_output
        C = NULL ;
    }

    //--------------------------------------------------------------------------
    // success: free everything except the result, and return it to the caller
    //--------------------------------------------------------------------------

    FREE_ALL ;
    if (pr) printf ("\nMatrix from file:\n") ;
    GxB_print (*A_output, pr ? GxB_SHORT : GxB_SILENT) ;
    return (GrB_SUCCESS) ;
}
示例#2
0
void mexFunction
(
    int nargout,
    mxArray *pargout [ ],
    int nargin,
    const mxArray *pargin [ ]
)
{

    bool malloc_debug = GB_mx_get_global (true) ;
    GrB_Matrix A = NULL ;
    void *Y = NULL ;
    void *Xtemp = NULL ;
    void *X = NULL ;
    GrB_Index nvals = 0 ;

    // check inputs
    GB_WHERE (USAGE) ;
    if (nargout > 3 || nargin < 1 || nargin > 2)
    {
        mexErrMsgTxt ("Usage: " USAGE) ;
    }

    #define GET_DEEP_COPY ;
    #define FREE_DEEP_COPY ;

    // get A (shallow copy)
    A = GB_mx_mxArray_to_Matrix (pargin [0], "A input", false, true) ;
    if (A == NULL)
    {
        FREE_ALL ;
        mexErrMsgTxt ("A failed") ;
    }
    mxClassID aclass = GB_mx_Type_to_classID (A->type) ;

    // get the number of entries in A
    GrB_Matrix_nvals (&nvals, A) ;

    mxClassID xclass ;
    GrB_Type xtype ;

    if (A->type == Complex)
    {
        // input argument xclass is ignored
        xtype = Complex ;
        xclass = mxDOUBLE_CLASS ;
        // create Xtemp
        if (nargout > 2)
        {
            GB_MALLOC_MEMORY (Xtemp, nvals, sizeof (double complex)) ;
        }
    }
    else
    {
        // get xclass, default is class (A), and the corresponding xtype
        xclass = GB_mx_string_to_classID (aclass, PARGIN (1)) ;
        xtype = GB_mx_classID_to_Type (xclass) ;
        if (xtype == NULL)
        {
            FREE_ALL ;
            mexErrMsgTxt ("X must be numeric") ;
        }
        // create X
        if (nargout > 2)
        {
            pargout [2] = mxCreateNumericMatrix (nvals, 1, xclass, mxREAL) ;
            X = (void *) mxGetData (pargout [2]) ;
        }
    }

    // create I
    pargout [0] = mxCreateNumericMatrix (nvals, 1, mxUINT64_CLASS, mxREAL) ;
    GrB_Index *I = (GrB_Index *) mxGetData (pargout [0]) ;

    // create J
    GrB_Index *J = NULL ;
    if (nargout > 1)
    {
        pargout [1] = mxCreateNumericMatrix (nvals, 1, mxUINT64_CLASS, mxREAL) ;
        J = (GrB_Index *) mxGetData (pargout [1]) ;
    }

    // [I,J,X] = find (A)
    if (GB_VECTOR_OK (A))
    {
        // test extract vector methods
        GrB_Vector v = (GrB_Vector) A ;
        switch (xtype->code)
        {
            case GB_BOOL_code   : METHOD (GrB_Vector_extractTuples (I, (bool     *) X, &nvals, v)) ; break ;
            case GB_INT8_code   : METHOD (GrB_Vector_extractTuples (I, (int8_t   *) X, &nvals, v)) ; break ;
            case GB_UINT8_code  : METHOD (GrB_Vector_extractTuples (I, (uint8_t  *) X, &nvals, v)) ; break ;
            case GB_INT16_code  : METHOD (GrB_Vector_extractTuples (I, (int16_t  *) X, &nvals, v)) ; break ;
            case GB_UINT16_code : METHOD (GrB_Vector_extractTuples (I, (uint16_t *) X, &nvals, v)) ; break ;
            case GB_INT32_code  : METHOD (GrB_Vector_extractTuples (I, (int32_t  *) X, &nvals, v)) ; break ;
            case GB_UINT32_code : METHOD (GrB_Vector_extractTuples (I, (uint32_t *) X, &nvals, v)) ; break ;
            case GB_INT64_code  : METHOD (GrB_Vector_extractTuples (I, (int64_t  *) X, &nvals, v)) ; break ;
            case GB_UINT64_code : METHOD (GrB_Vector_extractTuples (I, (uint64_t *) X, &nvals, v)) ; break ;
            case GB_FP32_code   : METHOD (GrB_Vector_extractTuples (I, (float    *) X, &nvals, v)) ; break ;
            case GB_FP64_code   : METHOD (GrB_Vector_extractTuples (I, (double   *) X, &nvals, v)) ; break ;
            case GB_UCT_code    : 
            case GB_UDT_code    : 
              METHOD (GrB_Vector_extractTuples (I, Xtemp, &nvals, v)) ; break ;
            default             : FREE_ALL ; mexErrMsgTxt ("unsupported class") ;
        }
        if (J != NULL)
        {
            for (int64_t p = 0 ; p < nvals ; p++) J [p] = 0 ;
        }
    }
    else
    {
        switch (xtype->code)
        {
            case GB_BOOL_code   : METHOD (GrB_Matrix_extractTuples (I, J, (bool     *) X, &nvals, A)) ; break ;
            case GB_INT8_code   : METHOD (GrB_Matrix_extractTuples (I, J, (int8_t   *) X, &nvals, A)) ; break ;
            case GB_UINT8_code  : METHOD (GrB_Matrix_extractTuples (I, J, (uint8_t  *) X, &nvals, A)) ; break ;
            case GB_INT16_code  : METHOD (GrB_Matrix_extractTuples (I, J, (int16_t  *) X, &nvals, A)) ; break ;
            case GB_UINT16_code : METHOD (GrB_Matrix_extractTuples (I, J, (uint16_t *) X, &nvals, A)) ; break ;
            case GB_INT32_code  : METHOD (GrB_Matrix_extractTuples (I, J, (int32_t  *) X, &nvals, A)) ; break ;
            case GB_UINT32_code : METHOD (GrB_Matrix_extractTuples (I, J, (uint32_t *) X, &nvals, A)) ; break ;
            case GB_INT64_code  : METHOD (GrB_Matrix_extractTuples (I, J, (int64_t  *) X, &nvals, A)) ; break ;
            case GB_UINT64_code : METHOD (GrB_Matrix_extractTuples (I, J, (uint64_t *) X, &nvals, A)) ; break ;
            case GB_FP32_code   : METHOD (GrB_Matrix_extractTuples (I, J, (float    *) X, &nvals, A)) ; break ;
            case GB_FP64_code   : METHOD (GrB_Matrix_extractTuples (I, J, (double   *) X, &nvals, A)) ; break;
            case GB_UCT_code    :
            case GB_UDT_code    :
                METHOD (GrB_Matrix_extractTuples (I, J, Xtemp, &nvals, A)) ; break;
            default             : FREE_ALL ; mexErrMsgTxt ("unsupported class") ;
        }
    }

    if (A->type == Complex && nargout > 2)
    {
        // create the MATLAB complex X
        pargout [2] = mxCreateNumericMatrix
            (nvals, 1, mxDOUBLE_CLASS, mxCOMPLEX) ;
        GB_mx_complex_split (nvals, Xtemp, pargout [2]) ;
    }

    FREE_ALL ;
}
示例#3
0
GrB_Info ktruss_graphblas       // compute the k-truss of a graph
(
    GrB_Matrix *p_C,            // output k-truss subgraph, C
    GrB_Matrix A,               // input adjacency matrix, A, not modified
    const int64_t k,            // find the k-truss, where k >= 3
    int64_t *p_nsteps           // # of steps taken
)
{

    //--------------------------------------------------------------------------
    // check inputs
    //--------------------------------------------------------------------------

    // ensure k is 3 or more
    if (k < 3) return (GrB_INVALID_VALUE) ;

    if (p_C == NULL || p_nsteps == NULL) return (GrB_NULL_POINTER) ;

    //--------------------------------------------------------------------------
    // initializations
    //--------------------------------------------------------------------------

    GrB_Info info ;
    GxB_SelectOp supportop = NULL ;

    GrB_Index n ;
    GrB_Matrix C = NULL ;
    OK (GrB_Matrix_nrows (&n, A)) ;
    OK (GrB_Matrix_new (&C, GrB_INT64, n, n)) ;

    // select operator
    int64_t support = (k-2) ;
    OK (GxB_SelectOp_new (&supportop, support_function, GrB_INT64)) ;

    // last_cnz = nnz (A)
    GrB_Index cnz, last_cnz ;
    OK (GrB_Matrix_nvals (&last_cnz, A)) ;

    //--------------------------------------------------------------------------
    // find the k-truss of A
    //--------------------------------------------------------------------------

    double tmult = 0 ;
    double tsel  = 0 ;

    for (int64_t nsteps = 1 ; ; nsteps++)
    {

        //----------------------------------------------------------------------
        // C<C> = C*C
        //----------------------------------------------------------------------

        GrB_Matrix Cin = (nsteps == 1) ? A : C ;
        double t1 = omp_get_wtime ( ) ;
        OK (GrB_mxm (C, Cin, NULL, GxB_PLUS_LAND_INT64, Cin, Cin, NULL)) ;
        double t2 = omp_get_wtime ( ) ;
        printf ("C<C>=C*C time: %g\n", t2-t1) ;
        tmult += (t2-t1) ;

        //----------------------------------------------------------------------
        // C = C .* (C >= support)
        //----------------------------------------------------------------------

        OK (GxB_select (C, NULL, NULL, supportop, C, &support, NULL)) ;

        double t3 = omp_get_wtime ( ) ;
        printf ("select time: %g\n", t3-t2) ;
        tsel += (t3-t2) ;

        //----------------------------------------------------------------------
        // check if the k-truss has been found
        //----------------------------------------------------------------------

        OK (GrB_Matrix_nvals (&cnz, C)) ;
        if (cnz == last_cnz)
        {
            printf ("ktruss_grb done: tmult %g tsel %g\n", tmult, tsel) ;
            (*p_C) = C ;                        // return the output matrix C
            (*p_nsteps) = nsteps ;              // return # of steps
            OK (GrB_free (&supportop)) ;        // free the select operator
            return (GrB_SUCCESS) ;
        }
        last_cnz = cnz ;
    }
}
示例#4
0
int main (int argc, char **argv)
{

    //--------------------------------------------------------------------------
    // initializations
    //--------------------------------------------------------------------------

    GrB_Info info ;
    GrB_Matrix A = NULL ;
    PageRank *Pd = NULL, *P2 = NULL ;
    iPageRank *Pi = NULL ;

    double tic [2], t ;
    OK (GrB_init (GrB_NONBLOCKING)) ;
    fprintf (stderr, "\npagerank_demo:\n") ;
    printf  (        "\npagerank_demo:\n") ;

    //--------------------------------------------------------------------------
    // read a matrix from stdin
    //--------------------------------------------------------------------------

    bool one_based = false ;
    if (argc > 1) one_based = strtol (argv [1], NULL, 0) ;

    OK (read_matrix (&A,
        stdin,      // read matrix from stdin
        false,      // unsymmetric
        false,      // self edges OK
        one_based,  // 0-based or 1-based, depending on input arg
        true,       // read input file as Boolean
        true)) ;    // print status to stdout

    GrB_Index n, nvals ;
    OK (GrB_Matrix_nrows (&n, A)) ;
    OK (GrB_Matrix_nvals (&nvals, A)) ;

    //--------------------------------------------------------------------------
    // compute the page rank via a real semiring
    //--------------------------------------------------------------------------

    simple_tic (tic) ;
    OK (dpagerank (&Pd, A)) ;
    t = simple_toc (tic) ;

    fprintf (stderr, "n %g edges %g  dpagerank time : %14.6f iters: 20\n",
        (double) n, (double) nvals, t) ;
    printf  (        "n %g edges %g  dpagerank time : %14.6f iters: 20\n",
        (double) n, (double) nvals, t) ;

    //--------------------------------------------------------------------------
    // compute the page rank via an integer semiring
    //--------------------------------------------------------------------------

    simple_tic (tic) ;
    OK (ipagerank (&Pi, A)) ;
    t = simple_toc (tic) ;

    fprintf (stderr, "n %g edges %g  ipagerank time : %14.6f iters: 20\n",
        (double) n, (double) nvals, t) ;
    printf  (        "n %g edges %g  ipagerank time : %14.6f iters: 20\n",
        (double) n, (double) nvals, t) ;

    //--------------------------------------------------------------------------
    // compute the page rank via an extreme semiring
    //--------------------------------------------------------------------------

    int iters ;
    simple_tic (tic) ;
    OK (dpagerank2 (&P2, A, 100, 1e-5, &iters, GxB_DEFAULT)) ;
    t = simple_toc (tic) ;

    fprintf (stderr, "n %g edges %g  dpagerank time : %14.6f iters: %d\n",
        (double) n, (double) nvals, t, iters) ;
    printf  (        "n %g edges %g  dpagerank time : %14.6f iters: %d\n",
        (double) n, (double) nvals, t, iters) ;

    //--------------------------------------------------------------------------
    // print results
    //--------------------------------------------------------------------------

    int64_t limit = MIN (n, 5000) ;
    printf ("Top %g nodes:\n", (double) limit) ;
    for (int64_t i = 0 ; i < limit ; i++)
    {
        printf ("%5g d:[%6g : %16.8e] i:[%6g : %16.8e] x:[%6g : %16.8e]",
            (double) i,
            (double) Pd [i].page, (double) Pd [i].pagerank,
            (double) Pi [i].page, (double) Pi [i].pagerank,
            (double) P2 [i].page, (double) P2 [i].pagerank) ;
        if (Pd [i].page != Pi [i].page || Pd [i].page != P2 [i].page)
        {
            printf ("mismatch") ;
        }
        printf ("\n") ;
    }

    //--------------------------------------------------------------------------
    // free all workspace
    //--------------------------------------------------------------------------

    FREE_ALL ;
    GrB_finalize ( ) ;
}
示例#5
0
void mexFunction
(
    int nargout,
    mxArray *pargout [ ],
    int nargin,
    const mxArray *pargin [ ]
)
{

    bool malloc_debug = GB_mx_get_global (true) ;
    GrB_Matrix C = NULL ;
    GrB_Descriptor desc = NULL ;
    GrB_Index *I = NULL, ni = 0, I_range [3] ;
    GrB_Index *J = NULL, nj = 0, J_range [3] ;
    bool ignore ;

    // check inputs
    GB_WHERE (USAGE) ;
    if (nargout > 1 || nargin < 2 || nargin > 5)
    {
        mexErrMsgTxt ("Usage: " USAGE) ;
    }

    // get C (make a deep copy)
    #define GET_DEEP_COPY \
        C = GB_mx_mxArray_to_Matrix (pargin [0], "C input", true, true) ;
    #define FREE_DEEP_COPY GB_MATRIX_FREE (&C) ;
    GET_DEEP_COPY ;
    if (C == NULL)
    {
        FREE_ALL ;
        mexErrMsgTxt ("C failed") ;
    }
    mxClassID cclass = GB_mx_Type_to_classID (C->type) ;

    // get accum; default: NOP, default class is class(C)
    GrB_BinaryOp accum ;
    if (!GB_mx_mxArray_to_BinaryOp (&accum, pargin [1], "accum",
        GB_NOP_opcode, cclass, C->type == Complex, C->type == Complex))
    {
        FREE_ALL ;
        mexErrMsgTxt ("accum failed") ;
    }

    // get I
    if (!GB_mx_mxArray_to_indices (&I, PARGIN (2), &ni, I_range, &ignore))
    {
        FREE_ALL ;
        mexErrMsgTxt ("I failed") ;
    }

    // get J
    if (!GB_mx_mxArray_to_indices (&J, PARGIN (3), &nj, J_range, &ignore))
    {
        FREE_ALL ;
        mexErrMsgTxt ("J failed") ;
    }

    // get desc
    if (!GB_mx_mxArray_to_Descriptor (&desc, PARGIN (4), "desc"))
    {
        FREE_ALL ;
        mexErrMsgTxt ("desc failed") ;
    }

    GrB_Index nrows, ncols ;
    GrB_Matrix_nvals (&nrows, C) ;
    GrB_Matrix_nvals (&ncols, C) ;

    // C(I,J) = accum (C(I,J),C)
    METHOD (GrB_assign (C, NULL, accum, C, I, ni, J, nj, desc)) ;

    GrB_wait ( ) ;
    TOC ;

    // return C to MATLAB as a struct and free the GraphBLAS C
    pargout [0] = GB_mx_Matrix_to_mxArray (&C, "C output", true) ;

    FREE_ALL ;
}