1 //------------------------------------------------------------------------------
2 // GraphBLAS/Demo/Source/bfs6_check.c: breadth first search using vxm
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 
8 //------------------------------------------------------------------------------
9 
10 // Modified from the GraphBLAS C API Specification, by Aydin Buluc, Timothy
11 // Mattson, Scott McMillan, Jose' Moreira, Carl Yang.  Based on "GraphBLAS
12 // Mathematics" by Jeremy Kepner.
13 
14 // No copyright claim is made for this particular file; the above copyright
15 // applies to all of SuiteSparse:GraphBLAS, not this file.
16 
17 // This method has been updated as of Version 2.2 of SuiteSparse:GraphBLAS.
18 // It now assumes the matrix is held by row (GxB_BY_ROW) and uses GrB_vxm
19 // instead of GrB_mxv.  It now more closely matches the BFS example in the
20 // GraphBLAS C API Specification.
21 
22 // "OK(x)" macro calls a GraphBLAS method, and if it fails, prints the error,
23 // frees workspace, and returns to the caller.  It uses the FREE_ALL macro
24 // to free the workspace
25 
26 // A must not have any explicit zeros.
27 
28 // NOTE: this method can be *slow*, in special cases (v very sparse on output,
29 // A in CSC format instead of the default CSR, or if A has any explicit values
30 // equal to zero in its pattern).  See LAGraph_bfs_pushpull for a faster method
31 // that handles these cases.  Do not benchmark this code!  It is just for
32 // simple illustration.  Use the LAGraph_bfs_pushpull for benchmarking and
33 // production use.
34 
35 #include "GraphBLAS.h"
36 
37 #define FREE_ALL                        \
38     GrB_Vector_free (&v) ;              \
39     GrB_Vector_free (&q) ;              \
40     GrB_Descriptor_free (&desc) ;       \
41     GrB_UnaryOp_free (&apply_level) ;
42 
43 #undef GB_PUBLIC
44 #define GB_LIBRARY
45 #include "graphblas_demos.h"
46 
47 //------------------------------------------------------------------------------
48 // bfs_level2: for unary operator
49 //------------------------------------------------------------------------------
50 
51 // level = depth in BFS traversal, roots=1, unvisited=0.
52 
53 // Note the operator accesses a global variable outside the control of
54 // GraphBLAS.  This is safe, but care must be taken not to change the global
55 // variable "level" while pending operations have yet to be completed.
56 
57 int32_t bfs_level2_global = 0 ;
58 
bfs_level2(void * result,const void * element)59 void bfs_level2 (void *result, const void *element)
60 {
61     // Note this function does not depend on its input.  It returns the value
62     // of the global variable level for all inputs.  It is applied to the
63     // vector q via GrB_apply, which only applies the unary operator to entries
64     // in the pattern.  Entries not in the pattern remain implicit (zero in
65     // this case), and then are not added by the GrB_PLUS_INT32 accum function.
66     (* ((int32_t *) result)) = bfs_level2_global ;
67 }
68 
69 //------------------------------------------------------------------------------
70 // bfs6: breadth first search using a Boolean semiring
71 //------------------------------------------------------------------------------
72 
73 // Given a n x n adjacency matrix A and a source node s, performs a BFS
74 // traversal of the graph and sets v[i] to the level in which node i is
75 // visited (v[s] == 1).  If i is not reacheable from s, then v[i] = 0. (Vector
76 // v should be empty on input.)  The graph A need not be Boolean on input;
77 // if it isn't Boolean, the semiring will properly typecast it to Boolean.
78 
79 GB_PUBLIC
bfs6_check(GrB_Vector * v_output,const GrB_Matrix A,GrB_Index s)80 GrB_Info bfs6_check         // BFS of a graph (using apply)
81 (
82     GrB_Vector *v_output,   // v [i] is the BFS level of node i in the graph
83     const GrB_Matrix A,     // input graph, treated as if boolean in semiring
84     GrB_Index s             // starting node of the BFS
85 )
86 {
87 
88     //--------------------------------------------------------------------------
89     // set up the semiring and initialize the vector v
90     //--------------------------------------------------------------------------
91 
92     GrB_Info info ;
93     GrB_Index n ;                               // # of nodes in the graph
94     GrB_Vector q = NULL ;                       // nodes visited at each level
95     GrB_Vector v = NULL ;                       // result vector
96     GrB_Descriptor desc = NULL ;                // Descriptor for vxm
97     GrB_UnaryOp apply_level = NULL ;            // unary op:
98                                                 // z = f(x) = bfs_level2_global
99 
100     OK (GrB_Matrix_nrows (&n, A)) ;             // n = # of rows of A
101     OK (GrB_Vector_new (&v, GrB_INT32, n)) ;    // Vector<int32_t> v(n) = 0
102     OK (GrB_Vector_assign_INT32 (v, NULL, NULL, 0, GrB_ALL, n, NULL)) ;
103     OK (GrB_Vector_nvals (&n, v)) ;              // finish pending work on v
104 
105     OK (GrB_Vector_new (&q, GrB_BOOL, n)) ;     // Vector<bool> q(n) = false
106     OK (GrB_Vector_setElement_BOOL (q, true, s)) ;   // q[s] = true
107 
108     // descriptor: invert the mask for vxm, and clear output before assignment
109     OK (GrB_Descriptor_new (&desc)) ;
110     OK (GxB_Desc_set (desc, GrB_MASK, GrB_COMP)) ;
111     OK (GxB_Desc_set (desc, GrB_OUTP, GrB_REPLACE)) ;
112 
113     // create a unary operator
114     OK (GrB_UnaryOp_new (&apply_level, bfs_level2, GrB_INT32, GrB_BOOL)) ;
115 
116     //--------------------------------------------------------------------------
117     // BFS traversal and label the nodes
118     //--------------------------------------------------------------------------
119 
120     bool successor = true ; // true when some successor found
121     for (bfs_level2_global = 1 ; successor && bfs_level2_global <= n ;
122          bfs_level2_global++)
123     {
124         // v[q] = bfs_level2_global, using apply.  This function applies the
125         // unary operator to the entries in q, which are the unvisited
126         // successors, and then writes their levels to v, thus updating the
127         // levels of those nodes in v.  The patterns of v and q are disjoint.
128 
129         OK (GrB_Vector_apply (v, NULL, GrB_PLUS_INT32, apply_level, q, NULL)) ;
130 
131         // q'<!v> = q ||.&& A ; finds all the unvisited
132         // successors from current q, using !v as the mask
133         OK (GrB_vxm (q, v, NULL, GrB_LOR_LAND_SEMIRING_BOOL, q, A, desc)) ;
134 
135         // successor = ||(q)
136         GrB_Vector_reduce_BOOL (&successor, NULL, GrB_LOR_MONOID_BOOL,
137             q, NULL) ;
138     }
139 
140     // make v sparse
141     OK (GrB_Descriptor_set (desc, GrB_MASK, GxB_DEFAULT)) ; // mask not inverted
142     OK (GrB_Vector_assign (v, v, NULL, v, GrB_ALL, n, desc)) ;
143 
144     *v_output = v ;         // return result
145     v = NULL ;              // set to NULL so FREE_ALL doesn't free it
146 
147     FREE_ALL ;              // free all workspace
148 
149     return (GrB_SUCCESS) ;
150 }
151 
152