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 (&degrees) ;        \
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 (&degrees, 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