-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy pathLG_CC_Boruvka.c
280 lines (226 loc) · 10.7 KB
/
LG_CC_Boruvka.c
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
//------------------------------------------------------------------------------
// LG_CC_Boruvka.c: connected components using GrB* methods only
//------------------------------------------------------------------------------
// LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
// SPDX-License-Identifier: BSD-2-Clause
//
// For additional details (including references to third party source code and
// other files) see the LICENSE file or contact [email protected]. See
// Contributors.txt for a full list of contributors. Created, in part, with
// funding and support from the U.S. Government (see Acknowledgments.txt file).
// DM22-0790
// Contributed by Yongzhe Zhang ([email protected]).
// Modified by Timothy A. Davis, Texas A&M University
//------------------------------------------------------------------------------
// This is an Advanced algorithm (G->is_symmetric_structure must be known),
// but it is not user-callable (see LAGr_ConnectedComponents instead).
// Code is based on Boruvka's minimum spanning forest algorithm.
// This method relies solely on GrB* methods in the V2.0 C API, but it much
// slower in general than LG_CC_FastSV6, which uses GxB pack/unpack methods
// for faster access to the contents of the matrices and vectors.
#include <stdint.h>
#include "LG_internal.h"
//------------------------------------------------------------------------------
// Reduce_assign
//------------------------------------------------------------------------------
// w[Px[i]] = min(w[Px[i]], s[i]) for i in [0..n-1].
static GrB_Info Reduce_assign
(
GrB_Vector w, // input/output vector of size n
GrB_Vector s, // input vector of size n
GrB_Index *Px, // Px: array of size n
GrB_Index *mem, // workspace of size 3*n
GrB_Index n
)
{
char *msg = NULL ;
GrB_Index *ind = mem ;
GrB_Index *sval = ind + n ;
GrB_Index *wval = sval + n ;
GRB_TRY (GrB_Vector_extractTuples (ind, wval, &n, w)) ;
GRB_TRY (GrB_Vector_extractTuples (ind, sval, &n, s)) ;
for (GrB_Index j = 0 ; j < n ; j++)
{
if (sval [j] < wval [Px [j]])
{
wval [Px [j]] = sval [j] ;
}
}
GRB_TRY (GrB_Vector_clear (w)) ;
GRB_TRY (GrB_Vector_build (w, ind, wval, n, GrB_PLUS_UINT64)) ;
LG_FREE_ALL ;
return (GrB_SUCCESS) ;
}
//------------------------------------------------------------------------------
// select_func: IndexUnaryOp for pruning entries from S
//------------------------------------------------------------------------------
// The pointer to the Px array is passed to the select function as a component
// of a user-defined data type.
typedef struct
{
GrB_Index *pointer ;
}
Parent_struct ;
void my_select_func (void *z, const void *x,
const GrB_Index i, const GrB_Index j, const void *y)
{
Parent_struct *Parent = (Parent_struct *) y ;
GrB_Index *Px = Parent->pointer ;
(*((bool *) z)) = (Px [i] != Px [j]) ;
}
//------------------------------------------------------------------------------
// LG_CC_Boruvka
//------------------------------------------------------------------------------
#undef LG_FREE_ALL
#define LG_FREE_ALL \
{ \
LG_FREE_WORK ; \
GrB_free (&parent) ; \
}
#undef LG_FREE_WORK
#define LG_FREE_WORK \
{ \
LAGraph_Free ((void **) &I, NULL) ; \
LAGraph_Free ((void **) &Px, NULL) ; \
LAGraph_Free ((void **) &mem, NULL) ; \
GrB_free (&S) ; \
GrB_free (&Parent_Type) ; \
GrB_free (&gp) ; \
GrB_free (&mnp) ; \
GrB_free (&ccmn) ; \
GrB_free (&ramp) ; \
GrB_free (&mask) ; \
GrB_free (&select_op) ; \
}
int LG_CC_Boruvka
(
// output:
GrB_Vector *component, // output: array of component identifiers
// input:
const LAGraph_Graph G, // input graph, not modified
char *msg
)
{
//--------------------------------------------------------------------------
// check inputs
//--------------------------------------------------------------------------
GrB_Index n, *I = NULL, *Px = NULL, *mem = NULL ;
GrB_Vector parent = NULL, gp = NULL, mnp = NULL, ccmn = NULL, ramp = NULL,
mask = NULL ;
GrB_IndexUnaryOp select_op = NULL ;
GrB_Matrix S = NULL ;
GrB_Type Parent_Type = NULL ;
Parent_struct Parent ;
LG_CLEAR_MSG ;
LG_TRY (LAGraph_CheckGraph (G, msg)) ;
LG_ASSERT (component != NULL, GrB_NULL_POINTER) ;
LG_ASSERT_MSG ((G->kind == LAGraph_ADJACENCY_UNDIRECTED ||
(G->kind == LAGraph_ADJACENCY_DIRECTED &&
G->is_symmetric_structure == LAGraph_TRUE)),
LAGRAPH_SYMMETRIC_STRUCTURE_REQUIRED,
"G->A must be known to be symmetric") ;
//--------------------------------------------------------------------------
// initializations
//--------------------------------------------------------------------------
// S = structure of G->A
LG_TRY (LAGraph_Matrix_Structure (&S, G->A, msg)) ;
GRB_TRY (GrB_Matrix_nrows (&n, S)) ;
GRB_TRY (GrB_Vector_new (&parent, GrB_UINT64, n)) ; // final result
GRB_TRY (GrB_Vector_new (&gp, GrB_UINT64, n)) ; // grandparents
GRB_TRY (GrB_Vector_new (&mnp, GrB_UINT64, n)) ; // min neighbor parent
GRB_TRY (GrB_Vector_new (&ccmn, GrB_UINT64, n)) ; // cc's min neighbor
GRB_TRY (GrB_Vector_new (&mask, GrB_BOOL, n)) ; // various uses
LG_TRY (LAGraph_Malloc ((void **) &mem, 3*n, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &Px, n, sizeof (GrB_Index), msg)) ;
Parent.pointer = Px ;
GRB_TRY (GrB_Type_new (&Parent_Type, sizeof (Parent_struct))) ;
#if !LAGRAPH_SUITESPARSE
// I is not needed for SuiteSparse and remains NULL
LG_TRY (LAGraph_Malloc ((void **) &I, n, sizeof (GrB_Index), msg)) ;
#endif
// parent = 0:n-1, and copy to ramp
GRB_TRY (GrB_assign (parent, NULL, NULL, 0, GrB_ALL, n, NULL)) ;
GRB_TRY (GrB_apply (parent, NULL, NULL, GrB_ROWINDEX_INT64, parent, 0,
NULL)) ;
GRB_TRY (GrB_Vector_dup (&ramp, parent)) ;
// Px is a non-opaque copy of the parent GrB_Vector
GRB_TRY (GrB_Vector_extractTuples (I, Px, &n, parent)) ;
GRB_TRY (GrB_IndexUnaryOp_new (&select_op, my_select_func, GrB_BOOL,
/* aij: ignored */ GrB_BOOL, /* y: pointer to Px */ Parent_Type)) ;
GrB_Index nvals ;
GRB_TRY (GrB_Matrix_nvals (&nvals, S)) ;
//--------------------------------------------------------------------------
// find the connected components
//--------------------------------------------------------------------------
while (nvals > 0)
{
//----------------------------------------------------------------------
// mnp[u] = u's minimum neighbor's parent for all nodes u
//----------------------------------------------------------------------
// every vertex points to a root vertex at the begining
GRB_TRY (GrB_assign (mnp, NULL, NULL, n, GrB_ALL, n, NULL)) ;
GRB_TRY (GrB_mxv (mnp, NULL, GrB_MIN_UINT64,
GrB_MIN_SECOND_SEMIRING_UINT64, S, parent, NULL)) ;
//----------------------------------------------------------------------
// find the minimum neighbor
//----------------------------------------------------------------------
// ccmn[u] = connect component's minimum neighbor | if u is a root
// = n | otherwise
GRB_TRY (GrB_assign (ccmn, NULL, NULL, n, GrB_ALL, n, NULL)) ;
GRB_TRY (Reduce_assign (ccmn, mnp, Px, mem, n)) ;
//----------------------------------------------------------------------
// parent[u] = ccmn[u] if ccmn[u] != n
//----------------------------------------------------------------------
// mask = (ccnm != n)
GRB_TRY (GrB_apply (mask, NULL, NULL, GrB_NE_UINT64, ccmn, n, NULL)) ;
// parent<mask> = ccmn
GRB_TRY (GrB_assign (parent, mask, NULL, ccmn, GrB_ALL, n, NULL)) ;
//----------------------------------------------------------------------
// select new roots
//----------------------------------------------------------------------
// identify all pairs (u,v) where parent [u] == v and parent [v] == u
// and then select the minimum of u, v as the new root;
// if (parent [parent [i]] == i) parent [i] = min (parent [i], i)
// compute grandparents: gp = parent (parent)
GRB_TRY (GrB_Vector_extractTuples (I, Px, &n, parent)) ;
GRB_TRY (GrB_extract (gp, NULL, NULL, parent, Px, n, NULL)) ;
// mask = (gp == 0:n-1)
GRB_TRY (GrB_eWiseMult (mask, NULL, NULL, GrB_EQ_UINT64, gp, ramp,
NULL)) ;
// parent<mask> = min (parent, ramp)
GRB_TRY (GrB_assign (parent, mask, GrB_MIN_UINT64, ramp, GrB_ALL, n,
NULL)) ;
//----------------------------------------------------------------------
// shortcutting: parent [i] = parent [parent [i]] until convergence
//----------------------------------------------------------------------
bool changing = true ;
while (true)
{
// compute grandparents: gp = parent (parent)
GRB_TRY (GrB_Vector_extractTuples (I, Px, &n, parent)) ;
GRB_TRY (GrB_extract (gp, NULL, NULL, parent, Px, n, NULL)) ;
// changing = or (parent != gp)
GRB_TRY (GrB_eWiseMult (mask, NULL, NULL, GrB_NE_UINT64, parent, gp,
NULL)) ;
GRB_TRY (GrB_reduce (&changing, NULL, GrB_LOR_MONOID_BOOL, mask,
NULL)) ;
if (!changing) break ;
// parent = gp
GRB_TRY (GrB_assign (parent, NULL, NULL, gp, GrB_ALL, n, NULL)) ;
}
//----------------------------------------------------------------------
// remove the edges inside each connected component
//----------------------------------------------------------------------
// This step is the costliest part of this algorithm when dealing with
// large matrices.
GRB_TRY (GrB_Matrix_select_UDT (S, NULL, NULL, select_op, S,
(void *) (&Parent), NULL)) ;
GRB_TRY (GrB_Matrix_nvals (&nvals, S)) ;
}
//--------------------------------------------------------------------------
// free workspace and return result
//--------------------------------------------------------------------------
(*component) = parent ;
LG_FREE_WORK ;
return (GrB_SUCCESS) ;
}