1 //------------------------------------------------------------------------------
2 // GraphBLAS/Demo/Source/mis_check.c: maximal independent set, w/error checking
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. It
18 // now uses GrB_vxm instead of GrB_mxv.
19
20 // This version also relies on predefined monoids and semirings, just to
21 // give an example of their use.
22
23 #include "GraphBLAS.h"
24
25 #ifdef GxB_SUITESPARSE_GRAPHBLAS
26 // use predefined semirings. They are safe to free,
27 // so the FREE_ALL macro can be used as-is in either case.
28 #define maxSelect1st GxB_MAX_FIRST_FP64
29 #endif
30
31 // "OK(x)" macro defined in graphblas_demos.h calls a GraphBLAS method, and if
32 // it fails, prints the error, frees workspace, and returns to the caller. It
33 // uses the FREE_ALL macro to free the workspace.
34 #define FREE_ALL \
35 GrB_Vector_free (&iset) ; \
36 GrB_Vector_free (&prob) ; \
37 GrB_Vector_free (&neighbor_max) ; \
38 GrB_Vector_free (&new_members) ; \
39 GrB_Vector_free (&new_neighbors) ; \
40 GrB_Vector_free (&candidates) ; \
41 GrB_Semiring_free (&maxSelect1st) ; \
42 GrB_Descriptor_free (&r_desc) ; \
43 GrB_Descriptor_free (&sr_desc) ; \
44 GrB_BinaryOp_free (&set_random) ; \
45 GrB_Vector_free (°rees) ; \
46 GrB_Vector_free (&Seed) ; \
47 GrB_Vector_free (&X) ; \
48 prand_finalize ( ) ;
49
50 #undef GB_PUBLIC
51 #define GB_LIBRARY
52 #include "graphblas_demos.h"
53
54 //------------------------------------------------------------------------------
55 // mis_check: maximal independent set, with error checking
56 //------------------------------------------------------------------------------
57
58 // A variant of Luby's randomized algorithm [Luby 1985].
59
60 // Given a numeric n x n adjacency matrix A of an unweighted and undirected
61 // graph (where the value true represents an edge), compute a maximal set of
62 // independent nodes and return it in a boolean n-vector, 'iset' where
63 // set[i] == true implies node i is a member of the set (the iset vector
64 // should be uninitialized on input.)
65
66 // The graph cannot have any self edges, and it must be symmetric. These
67 // conditions are not checked. Self-edges will cause the method to stall.
68
69 // Singletons require special treatment. Since they have no neighbors, their
70 // prob is never greater than the max of their neighbors, so they never get
71 // selected and cause the method to stall. To avoid this case they are removed
72 // from the candidate set at the begining, and added to the iset.
73
74 GB_PUBLIC
mis_check(GrB_Vector * iset_output,const GrB_Matrix A,int64_t seed)75 GrB_Info mis_check // compute a maximal independent set
76 (
77 GrB_Vector *iset_output, // iset(i) = true if i is in the set
78 const GrB_Matrix A, // symmetric Boolean matrix
79 int64_t seed // random number seed
80 )
81 {
82
83 // these are set to NULL so that FREE_ALL can safely free all objects
84 // at any time
85 GrB_Vector iset = NULL ;
86 GrB_Vector prob = NULL ; // random probability for each node
87 GrB_Vector neighbor_max = NULL ; // value of max neighbor probability
88 GrB_Vector new_members = NULL ; // set of new members to iset
89 GrB_Vector new_neighbors = NULL ; // new neighbors to new iset members
90 GrB_Vector candidates = NULL ; // candidate members to iset
91 #ifndef GxB_SUITESPARSE_GRAPHBLAS
92 GrB_Semiring maxSelect1st = NULL ; // Max/Select1st "semiring"
93 #endif
94 GrB_Descriptor r_desc = NULL ;
95 GrB_Descriptor sr_desc = NULL ;
96 GrB_BinaryOp set_random = NULL ;
97 GrB_Vector degrees = NULL ;
98 GrB_Vector Seed = NULL ;
99 GrB_Vector X = NULL ;
100
101 GrB_Index n ;
102 GrB_Info info ;
103
104 OK (GrB_Matrix_nrows (&n, A)) ; // n = # of nodes in graph
105
106 OK (GrB_Vector_new (&prob, GrB_FP64, n)) ;
107 OK (GrB_Vector_new (&neighbor_max, GrB_FP64, n)) ;
108 OK (GrB_Vector_new (&new_members, GrB_BOOL, n)) ;
109 OK (GrB_Vector_new (&new_neighbors, GrB_BOOL, n)) ;
110 OK (GrB_Vector_new (&candidates, GrB_BOOL, n)) ;
111
112 // Initialize independent set vector, bool
113 OK (GrB_Vector_new (&iset, GrB_BOOL, n)) ;
114
115 #ifndef GxB_SUITESPARSE_GRAPHBLAS
116 // create the maxSelect1st semiring
117 OK (GrB_Semiring_new (&maxSelect1st, GrB_MAX_MONOID_FP64, GrB_FIRST_FP64)) ;
118 #endif
119
120 // descriptor: C_replace
121 OK (GrB_Descriptor_new (&r_desc)) ;
122 OK (GrB_Descriptor_set (r_desc, GrB_OUTP, GrB_REPLACE)) ;
123
124 // create the random number seeds
125 OK (prand_init ( )) ;
126 OK (prand_seed (&Seed, seed, n, 0)) ;
127 OK (GrB_Vector_new (&X, GrB_FP64, n)) ;
128
129 // descriptor: C_replace + structural complement of mask
130 OK (GrB_Descriptor_new (&sr_desc)) ;
131 OK (GrB_Descriptor_set (sr_desc, GrB_MASK, GrB_COMP)) ;
132 OK (GrB_Descriptor_set (sr_desc, GrB_OUTP, GrB_REPLACE)) ;
133
134 // create the mis_score binary operator
135 OK (GrB_BinaryOp_new (&set_random, mis_score2,
136 GrB_FP64, GrB_UINT32, GrB_FP64)) ;
137
138 // compute the degree of each node
139 OK (GrB_Vector_new (°rees, GrB_FP64, n)) ;
140 OK (GrB_Matrix_reduce_Monoid (degrees, NULL, NULL, GrB_PLUS_MONOID_FP64,
141 A, NULL)) ;
142
143 // singletons are not candidates; they are added to iset first instead
144 // candidates[degree != 0] = 1
145 OK (GrB_Vector_assign_BOOL (candidates, degrees, NULL, true,
146 GrB_ALL, n, NULL)) ;
147
148 // add all singletons to iset
149 // iset[degree == 0] = 1
150 OK (GrB_Vector_assign_BOOL (iset, degrees, NULL, true,
151 GrB_ALL, n, sr_desc)) ;
152
153 // Iterate while there are candidates to check.
154 GrB_Index nvals ;
155 OK (GrB_Vector_nvals (&nvals, candidates)) ;
156
157 int64_t last_nvals = nvals ; // just for error-checking
158
159 while (nvals > 0)
160 {
161 // sparsify the random number seeds (just keep it for each candidate)
162 OK (GrB_Vector_assign (Seed, candidates, NULL, Seed,
163 GrB_ALL, n, r_desc)) ;
164
165 // compute a random probability scaled by inverse of degree
166 OK (prand_xget (X, Seed)) ;
167 OK (GrB_Vector_eWiseMult_BinaryOp (prob, candidates, NULL, set_random,
168 degrees, X, r_desc)) ;
169
170 // compute the max probability of all neighbors
171 OK (GrB_vxm (neighbor_max, candidates, NULL, maxSelect1st,
172 prob, A, r_desc)) ;
173
174 // select node if its probability is > than all its active neighbors
175 OK (GrB_Vector_eWiseAdd_BinaryOp (new_members, NULL, NULL, GrB_GT_FP64,
176 prob, neighbor_max, NULL)) ;
177
178 // add new members to independent set.
179 OK (GrB_Vector_eWiseAdd_BinaryOp (iset, NULL, NULL, GrB_LOR, iset,
180 new_members, NULL)) ;
181
182 // remove new members from set of candidates c = c & !new
183 OK (GrB_Vector_apply (candidates, new_members, NULL, GrB_IDENTITY_BOOL,
184 candidates, sr_desc)) ;
185
186 OK (GrB_Vector_nvals (&nvals, candidates)) ;
187 if (nvals == 0) { break ; } // early exit condition
188
189 // Neighbors of new members can also be removed from candidates
190 OK (GrB_vxm (new_neighbors, candidates, NULL,
191 GrB_LOR_LAND_SEMIRING_BOOL, new_members, A, NULL)) ;
192
193 OK (GrB_Vector_apply (candidates, new_neighbors, NULL,
194 GrB_IDENTITY_BOOL, candidates, sr_desc)) ;
195
196 OK (GrB_Vector_nvals (&nvals, candidates)) ;
197
198 // this will not occur, unless the input is corrupted somehow,
199 // or if two candidates have exactly the same score
200 if (last_nvals == nvals)
201 {
202 printf ("stall!\n") ;
203 OK (GrB_INVALID_VALUE) ;
204 }
205 last_nvals = nvals ;
206 }
207
208 // drop explicit false values
209 OK (GrB_Vector_apply (iset, iset, NULL, GrB_IDENTITY_BOOL, iset, r_desc)) ;
210
211 // return result
212 *iset_output = iset ;
213 iset = NULL ; // set to NULL so FREE_ALL doesn't free it
214
215 // free workspace
216 FREE_ALL ;
217 return (GrB_SUCCESS) ;
218 }
219
220