https://github.com/michelp/glaph
You gotta glaph about it
https://github.com/michelp/glaph
Last synced: over 1 year ago
JSON representation
You gotta glaph about it
- Host: GitHub
- URL: https://github.com/michelp/glaph
- Owner: michelp
- License: apache-2.0
- Created: 2021-02-14T05:31:54.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2021-02-22T18:49:31.000Z (over 5 years ago)
- Last Synced: 2025-01-10T03:44:29.949Z (over 1 year ago)
- Language: C
- Size: 54.7 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# glaph: (G)enerics (L)ibrary for Gr(aph)BLAS
glaph is an include-only C library of macros, inline functions and
pure evil that make working with the GraphBLAS API less C like and
more "Python like". Just copy the header files directory into your
project and do:
#include "glaph/glaph.h"
## So what's so funny
Glaph is macro and inline function meta-boilerplate for dealing with
GraphBLAS objects. Instead of:
GrB_Matrix A;
GrB_TRY(GrB_Matrix_new(&A, GrB_FP64, nrows, ncols));
In glaph you just do:
GrB_Matrix A;
GL_NEW(A);
It initializes it as a hypersparse matrix with default type FP64. It
is now ready to use in your program for many cases. But this is a
rather tame example of a glaph macro. Lets say instead you want a 10
by 10 boolean matrix:
GL_NEW(A, 10, 10, .type=GrB_BOOL);
It's the same macro, it's just generic enough to handle various cases
including optional types and dimensions provided as keywork arguments.
How about a new vector? Same macro, just pass a vector argument
instead. In this case it takes only one index argument for the vector
size:
GrB_Vector j;
GL_NEW(j); // hypersparse FP_64
GL_NEW(j, 10, .type=GrB_BOOL); // size=10 BOOL
Now, everyone knows C doesn't have keyword arguments. Glaph bends
reality with some macro and inline function sleigh of hand to make
using the C api a lot easier by making many of the optional arguments
defaulted internally my the glaph macros. Combined with the
polymorphism of C11 style `_Generic()` macros, there is a lot of
simplication, and glaph algorithms are usualy line for line
operationally identical to their Python equivalents.
There are no external dependencies and no runtime. Glaph generates C
code using standard `#define` C preprocessor macros that is directly
compiled like any other GraphBLAS program.
Another example is that where the GraphBLAS C API provides `GrB_mxm`,
`GrB_mxv` and `GrB_vxm` for Matix/Matrix, Matrix/Vector, and
Vector/Matrix multiplication respectively, glaph has only `GL_AXB(C,
A, B)`:
GL_AXB(C, A, B,
.mask=M,
.accum=GrB_MIN_INT64);
GL_AXB(c, a, B,
.mask=m,
.semiring=GrB_MIN_PLUS_SEMIRING_INT64);
GL_AXB(c, A, b,
.mask=m,
.semiring=GrB_MIN_PLUS_SEMIRING_INT64,
.accum=GrB_MIN_INT64);
The only invalid combination is (Vector, Vector, Vector) which will
throw a compiler error.
One of the consequence of glaph using C metaprogramming is that the C
compiler optimizing pass will generate the same code that you would
have written by hand. The keyword arguments structs and inline
functions "evaporate" to static arguments, just as if you had placed
them there by hand. In a sense glaph self-destructs when it's
compiled, leaving only a GraphBLAS program behind.
## shortest path example
This is the shortest path example:
GrB_Vector sssp(GrB_Matrix A, uint64_t start) {
GrB_Vector v, w;
GL_NEW(v);
GL_NEW(w);
GL_ASSIGN(v, start, 0);
GL_FORI(i, A) {
GL_CLEAR(w);
GL_ASSIGN(w, v);
GL_AXB(v, v, A,
.semiring=GrB_MIN_PLUS_SEMIRING_INT64,
.accum=GrB_MIN_INT64);
if (GL_ISEQ(w, v))
break;
}
GL_FREE(w);
return v;
}
Here is the equivalent Python with pygraphblas:
def sssp(A, start):
v = Vector.sparse(A.type, A.nrows)
v[start] = 0
with INT64.MIN_PLUS, Accum(INT64.MIN):
for i in A.I:
w.clear()
w[:] = v
v @= A
if w.iseq(v):
break
return v
## Even more Generic
SuiteSparse comes with a number of generic macros that can work with
various types with the same funciton name. Instead of calling
`GrB_Matrix_assign_FP64`, you can just call `GrB_assign` with the
right order and type of arguments that the compiler will pick the
right concrete function (for matrices with double floating point
arguments, as given in this example).
glaph takes this idea even further, instead of
`GrB_Matrix_setElement`, the `ASSIGN` macro can handle the scalar case
as well as the already supported vector and matrix cases. It also
includes support for `GxB_Scalar`, All in one macro.
This pattern is carred over to `GL_EXTRACT` as well, you can extract
sub-graphs, vectors, Scalars, and C scalar values all with one macro,
each with its own distinct set of keyword arguments depending on the
input types of the operation.
All macros take the pattern:
GL_NAME(C, [A, [B, ...] ...])
They all require at least one input (for things like `GL_PRINT`) or
output. Glaph macros NEVER return values so never do something like
`x = GL_NAME(...)`. If the macro has output, it is always the first
argument. Keyword arguments depend on whatever pattern of positional
arguments matched the _Generic expansion. `GL_EXTRACT` for example,
will accept `mask`, `accum` and `desc` keywork arguments in all cases
except scalar. Trying to use inappropriate keyworks arguments will
result in a compiler error.
All glaph macros automatically check for and handle SuiteSparse
errors. The default behavior is to print and abort on any error
except `GrB_SUCCESS` or `GrB_NO_VALUE`. You can re-define the
`GL_CATCH` macro to handle your own errors.
## Keyword arguments?
For example `AXB` is the generic matrix mulitpilicaiton macro in
glaph. It covers all cases that `GrB_mxm`, `GrB_vxm` and `GrB_mxv`
cover in the API. Just Do `AXB (C, A, B)` and the macro will expand
to the correct underlying function for the argument types with all the
optional arguments filled in with `NULL`.
All other arguments to those 3 functions are optional, so they are
specified by keyword argument:
AXB (v, AT, w,
.mask=M,
.accum=GrB_PLUS_FP32,
.semiring=GxB_PLUS_SECOND_FP32,
.desc=GrB_DESC_T0);
The "keyword arguments" are really the guts of a struct initializer
that the macro is hiding from you. No worries, it all works out
great. In fact, the C compiler knows that these structs are all
statically initialized, and completely optimizes all of glaph away,
exactly compiling the same assembly code that using the GraphBLAS API
directly "by hand" would create.