File: GrB_objects_IndexBinaryOp.tex

package info (click to toggle)
suitesparse 1%3A7.10.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 254,920 kB
  • sloc: ansic: 1,134,743; cpp: 46,133; makefile: 4,875; fortran: 2,087; java: 1,826; sh: 996; ruby: 725; python: 495; asm: 371; sed: 166; awk: 44
file content (287 lines) | stat: -rw-r--r-- 11,850 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

\newpage
%===============================================================================
\subsection{GraphBLAS index-binary operators: {\sf GxB\_IndexBinaryOp}}
%===============================================================================
\label{idxbinaryop}

An index-binary operator is a scalar function of the following form:
\[
z=f(x,i_x,j_x,y,i_y,j_y,\Theta),
\]
where the value $x$ appears at row $i_x$ and column $j_x$ in its matrix,
and the value $y$ appears at row $i_y$ and column $j_y$ in its matrix.
The value $\Theta$ is a scalar that is the same for all uses of the operator.
See our IEEE HPEC'24 paper for more details (\cite{idxbinop}),
in the \verb'GraphBLAS/Doc' folder.

When used in an element-wise method for $\bf C = A \oplus B$ and related
methods (\verb'GrB_eWiseAdd', \verb'GxB_eWiseUnion', or \verb'GrB_eWiseMult'),
operator is used for a pair of entries
$a_{ij}$ and $b_{ij}$, as
\[
z=f(a_{ij},i,j,b_{ij},i,j,\Theta).
\]
When used in \verb'GrB_kronecker', it is used on a pair of entries
$a_{i_1,j_1}$ and $b_{i_2,j_2}$, as
\[
z=f(a_{ij},i_1,j_1,b_{ij},i_2,j_2,\Theta).
\]
When used as the multiplicative operator in a semiring, to compute
$\bf C = A \oplus.\otimes B$, the operator is used as
\[
z=f(a_{ik},i,k,b_{kj},k,j,\Theta)
\]
to compute an entry to be summed by the monoid of the semiring.

No GraphBLAS operations directly use the \verb'GxB_IndexBinaryOp'.  Instead,
the operator is coupled with a scalar \verb'Theta' value to create a new
index-based binary operator, which is simply a special case of a
\verb'GrB_BinaryOp'.  The resulting \verb'GrB_BinaryOp' can then be passed to
element-wise methods and as the multiplicative operator of a new semiring.

The signature of the index-binary function \verb'f' is as follows:

{\footnotesize
\begin{verbatim}
void f
(
    void *z,            // output value z, of type ztype
    const void *x,      // input value x of type xtype; value of v(ix) or A(ix,jx)
    GrB_Index ix,       // row index of v(ix) or A(ix,jx)
    GrB_Index jx,       // column index of A(ix,jx), or zero for v(ix)
    const void *y,      // input value y of type ytype; value of w(iy) or B(iy,jy)
    GrB_Index iy,       // row index of w(iy) or B(iy,jy)
    GrB_Index jy,       // column index of B(iy,jy), or zero for w(iy)
    const void *theta   // input scalar theta of type theta_type
) ; \end{verbatim}}

The following binary operators (\verb'GrB_BinaryOp' objects) are pre-defined,
where $N$ can be \verb'INT32' or \verb'INT64'.  These operators do not use
\verb'theta'.  Instead, the offset of 1 in \verb'GxB_FIRSTI1' is fixed into
the operator itself.

\vspace{0.2in}
{\footnotesize
\begin{tabular}{|llll|}
\hline
\multicolumn{4}{|c|}{Built-in index-based binary operators for any type} \\
\hline
GraphBLAS name            & types (domains)  & $z=f(x,y)$    & description \\
\hline
\verb'GxB_FIRSTI_'$N$    & $ \rightarrow N$  & $z = i_x$   & row index of $x$ (0-based) \\
\verb'GxB_FIRSTI1_'$N$   & $ \rightarrow N$  & $z = i_x+1$ & row index of $x$ (1-based) \\
\verb'GxB_FIRSTJ_'$N$    & $ \rightarrow N$  & $z = j_x$   & column index of $x$ (0-based) \\
\verb'GxB_FIRSTJ1_'$N$   & $ \rightarrow N$  & $z = j_x+1$ & column index of $x$ (1-based) \\
\verb'GxB_SECONDI_'$N$   & $ \rightarrow N$  & $z = i_y$   & row index of $y$ (0-based) \\
\verb'GxB_SECONDI1_'$N$  & $ \rightarrow N$  & $z = i_y+1$ & row index of $y$ (1-based) \\
\verb'GxB_SECONDJ_'$N$   & $ \rightarrow N$  & $z = j_y$   & column index of $y$ (0-based) \\
\verb'GxB_SECONDJ1_'$N$  & $ \rightarrow N$  & $z = j_y+1$ & column index of $y$ (1-based) \\
\hline
\end{tabular}
}

\vspace{0.2in}
The following methods operate on the \verb'GxB_IndexBinaryOp' object:

\vspace{0.1in}
\noindent
{\footnotesize
\begin{tabular}{lll}
\hline
GraphBLAS function   & purpose                                      & Section \\
\hline
\verb'GxB_IndexBinaryOp_new'   & create a named user-defined index-binary operator   & \ref{idxbinop_new_named} \\
\verb'GxB_IndexBinaryOp_wait'  & wait for a user-defined index-binary operator  & \ref{idxbinop_wait} \\
\verb'GxB_IndexBinaryOp_free'  & free a user-defined index-binary operator      & \ref{idxbinop_free} \\
\verb'GxB_BinaryOp_new_IndexOp' & create a new index-based \verb'GrB_BinaryOp' & \ref{binop_new_idxop} \\
\verb'GrB_get'           & get properties of an operator    & \ref{get_set_idxbinop} \\
\verb'GrB_set'           & set the operator name/definition & \ref{get_set_idxbinop} \\
\hline
\end{tabular}
}
\vspace{0.1in}

\newpage
%-------------------------------------------------------------------------------
\subsubsection{{\sf GxB\_IndexBinaryOp\_new:} create a user-defined index-binary operator}
%-------------------------------------------------------------------------------
\label{idxbinop_new_named}

\begin{mdframed}[userdefinedwidth=6in]
{\footnotesize
\begin{verbatim}
GrB_Info GxB_IndexBinaryOp_new
(
    GxB_IndexBinaryOp *op,          // handle for the new index binary operator
    GxB_index_binary_function function, // pointer to the index binary function
    GrB_Type ztype,                 // type of output z
    GrB_Type xtype,                 // type of input x
    GrB_Type ytype,                 // type of input y
    GrB_Type theta_type,            // type of input theta
    const char *idxbinop_name,      // name of the user function
    const char *idxbinop_defn       // definition of the user function
) ;
\end{verbatim} }\end{mdframed}

Creates a named \verb'GxB_IndexBinaryOp'.  Only the first 127 characters of
\verb'idxbinop_name' are used.  The \verb'ixdbinop_defn' is a string containing
the entire function itself.

The two strings \verb'idxbinop_name' and \verb'idxbinop_defn' are optional, but
are required to enable the JIT compilation of kernels that use this operator.
For example, the following operator can be used to compute the argmax of a
matrix with a single call to \verb'GrB_mxv'.  It returns a vector \verb'c'
where \verb'c(i) = (k,v)', where the largest value in the $i$th row of \verb'A'
has value \verb'v' and appears in column \verb'k'.  If multiple values in the
$i$th row have the same largest value, the one with the smallest column index
is returned.

{\footnotesize
\begin{verbatim}
    typedef struct { int64_t k ; double v ; } tuple_fp64 ;
    #define FP64_K "typedef struct { int64_t k ; double v ; } tuple_fp64 ;"
    void make_fp64 (tuple_fp64 *z,
        const double *x, GrB_Index ix, GrB_Index jx,
        const void   *y, GrB_Index iy, GrB_Index jy,
        const void *theta)
    {
        z->k = (int64_t) jx ;
        z->v = (*x) ;
    }
    void max_fp64 (tuple_fp64 *z, const tuple_fp64 *x, const tuple_fp64 *y)
    {
        if (x->v > y->v || (x->v == y->v && x->k < y->k))
        {
            z->k = x->k ;
            z->v = x->v ;
        }
        else
        {
            z->k = y->k ;
            z->v = y->v ;
        }
    }
    #define MAX_FP64 (a string containing the max_fp64 function above)

    // create the types and operators:
    GrB_Scalar Theta ;                  // unused, but cannot be NULL
    GrB_Scalar_new (&Theta, GrB_BOOL) ;
    GrB_Scalar_setElement_BOOL (Theta, 0) ;
    GxB_IndexBinaryOp Iop ;
    GrB_BinaryOp Bop, MonOp ;
    GrB_Type Tuple ;
    GxB_Type_new (&Tuple, sizeof (tuple_fp64), "tuple_fp64", FP64_K) ;
    GxB_IndexBinaryOp_new (&Iop, make_fp64, Tuple, GrB_FP64, GrB_BOOL, GrB_BOOL,
        "make_fp64", MAKE_FP64)) ;
    GxB_BinaryOp_new_IndexOp (&Bop, Iop, Theta) ;
    tuple_fp64 id ;
    memset (&id, 0, sizeof (tuple_fp64)) ;
    id.k = INT64_MAX ;
    id.v = (double) (-INFINITY) ;
    GxB_BinaryOp_new (&MonOp, max_fp64, Tuple, Tuple, Tuple, "max_fp64", MAX_FP64) ;
    GrB_Monoid MonOp ;
    GrB_Semiring Semiring ;
    GrB_Monoid_new_UDT (&Monoid, MonOp, &id) ;
    GrB_Semiring_new (&Semiring, Monoid, Bop) ;

    // compute the argmax of each row of a GrB_FP64 matrix A:
    // y = zeros (ncols,1) ;
    GrB_Vector y ;
    GrB_Matrix_new (&y, GrB_BOOL, ncols, 1)) ;
    GrB_Matrix_assign_BOOL (y, NULL, NULL, 0, GrB_ALL, ncols, GrB_ALL, 1, NULL)) ;
    // c = A*y using the argmax semiring
    GrB_Vector_new (&c, Tuple, nrows, 1)) ;
    GrB_mxv (c, NULL, NULL, Semiring, A, y, NULL) ; \end{verbatim}}

\newpage
%-------------------------------------------------------------------------------
\subsubsection{{\sf GxB\_IndexBinaryOp\_wait:} wait for an index-binary operator}
%-------------------------------------------------------------------------------
\label{idxbinop_wait}

\begin{mdframed}[userdefinedwidth=6in]
{\footnotesize
\begin{verbatim}
GrB_Info GxB_IndexBinaryOp_wait
(
    GxB_IndexBinaryOp op,
    int mode                    // GrB_COMPLETE or GrB_MATERIALIZE
) ;
\end{verbatim}
}\end{mdframed}

After creating a user-defined index-binary operator, a GraphBLAS library may choose
to exploit non-blocking mode to delay its creation.  Currently,
SuiteSparse:GraphBLAS currently does nothing except to ensure that the
\verb'op' is valid.

% \newpage
%-------------------------------------------------------------------------------
\subsubsection{{\sf GxB\_IndexBinaryOp\_free:} free a user-defined index-binary operator}
%-------------------------------------------------------------------------------
\label{idxbinop_free}

\begin{mdframed}[userdefinedwidth=6in]
{\footnotesize
\begin{verbatim}
GrB_Info GrB_free               // free a user-created index-binary operator
(
    GxB_IndexBinaryOp *op       // handle of IndexBinaryOp to free
) ;
\end{verbatim}
}\end{mdframed}

\verb'GxB_IndexBinaryOp_free' frees a user-defined index-binary operator.  Either usage:

    {\small
    \begin{verbatim}
    GxB_IndexBinaryOp_free (&op) ;
    GrB_free (&op) ; \end{verbatim}}

\noindent
frees the \verb'op' and sets \verb'op' to \verb'NULL'.  It safely
does nothing if passed a \verb'NULL' handle, or if \verb'op == NULL' on
input.  No built-in index-binary operators exist, but if they did,
the method does nothing at all if passed a built-in index-binary operator.

\newpage
%-------------------------------------------------------------------------------
\subsubsection{{\sf GxB\_BinaryOp\_new\_IndexOp:} create a index-based binary operator}
%-------------------------------------------------------------------------------
\label{binop_new_idxop}

\begin{mdframed}[userdefinedwidth=6in]
{\footnotesize
\begin{verbatim}
GrB_Info GxB_BinaryOp_new_IndexOp
(
    GrB_BinaryOp *binop,            // handle of binary op to create
    GxB_IndexBinaryOp idxbinop,     // based on this index binary op
    GrB_Scalar theta                // theta value to bind to the new binary op
) ;
\end{verbatim}
}\end{mdframed}

The \verb'GxB_IndexBinaryOp' cannot be directly used in any GraphBLAS operation
such as \verb'GrB_mxm'.  Instead, it must be used to create a new index-based
\verb'GrB_BinaryOp'.  The resulting binary operator can then be used to as the
multiplicative operator in a new user-defined semiring, or as the primary
binary operator of the element-wise operations (\verb'eWiseAdd',
\verb'eWiseUnion', \verb'eWiseMult', or \verb'kronecker').

The resulting binary operator cannot be used as the \verb'accum' operator in
any GraphBLAS operation.  It also cannot be used in other places where a binary
operator appears, including \verb'GrB_*_build', \verb'GrB_apply',
\verb'GrB_reduce' and \verb'GrB_*_sort'.

The \verb'GxB_BinaryOp_new_IndexOp' method creates this index-based binary
operator.  It takes two input parameters:  an index-binary operator, and a
scalar \verb'Theta'.  The value of \verb'Theta' is copied into this new binary
operator, and the value cannot be changed.  To change \verb'Theta', the binary
operator must be freed, and any semiring that would like to use the new value
of \verb'Theta' must also be recreated.

An example of its use is given in Section~\ref{idxbinop_new_named}.