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}.
|