-
Notifications
You must be signed in to change notification settings - Fork 69
/
Copy pathLAGraph_msf.c
278 lines (241 loc) · 10.9 KB
/
LAGraph_msf.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
//------------------------------------------------------------------------------
// LAGraph_msf.c
//------------------------------------------------------------------------------
// 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])
//------------------------------------------------------------------------------
/**
* Code is based on Boruvka's minimum spanning forest algorithm
*/
#define LG_FREE_ALL \
{ \
GrB_free (&S); \
GrB_free (&T); \
free(I); free(V); \
free(SI); free(SJ); free(SX); \
free(parent); free(partner); free(weight); \
GrB_free (&f); \
GrB_free (&i); \
GrB_free (&t); \
GrB_free (&edge); \
GrB_free (&cedge); \
GrB_free (&mask); \
GrB_free (&index); \
GrB_free (&comb); \
GrB_free (&combMin); \
GrB_free (&fst); \
GrB_free (&snd); \
GrB_free (&s1); \
GrB_free (&s2); \
}
#include "LG_internal.h"
#include <LAGraph.h>
#include <LAGraphX.h>
//****************************************************************************
// encode each edge into a single uint64_t
static void combine (void *z, const void *x, const void *y)
{
*(uint64_t*)z = ((*(uint64_t*)x) << 32) + (*(uint64_t*)y);
}
static void get_fst (void *y, const void *x)
{
*(uint64_t*)y = (*(uint64_t*)x) >> 32;
}
static void get_snd (void *y, const void *x)
{
*(uint64_t*)y = (*(uint64_t*)x) & INT_MAX;
}
//****************************************************************************
// w[index[i]] = min(w[index[i]], s[i]) for i in [0..n-1]
static GrB_Info Reduce_assign (GrB_Vector w,
GrB_Vector s, GrB_Index *index, GrB_Index n)
{
GrB_Index *mem = (GrB_Index*) malloc(sizeof(GrB_Index) * n * 3);
GrB_Index *ind = mem, *sval = mem + n, *wval = sval + n;
GrB_Vector_extractTuples(ind, wval, &n, w);
GrB_Vector_extractTuples(ind, sval, &n, s);
for (GrB_Index i = 0; i < n; i++)
if (sval[i] < wval[index[i]])
wval[index[i]] = sval[i];
GrB_Vector_clear(w);
GrB_Vector_build(w, ind, wval, n, GrB_PLUS_UINT64);
free(mem);
return GrB_SUCCESS;
}
//****************************************************************************
// global C arrays (for implementing various GxB_SelectOp)
static GrB_Index *weight = NULL, *parent = NULL, *partner = NULL;
// generate solution:
// for each element A(i, j), it is selected if
// 1. weight[i] == A(i, j) -- where weight[i] stores i's minimum edge weight
// 2. parent[j] == partner[i] -- j belongs to the specified connected component
void f1 (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
{
uint64_t *aij = (uint64_t*) x;
(*z) = (weight[i] == *aij) && (parent[j] == partner[i]);
}
// edge removal:
// A(i, j) is removed when parent[i] == parent[j]
void f2 (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
{
(*z) = (parent[i] != parent[j]);
}
//****************************************************************************
//****************************************************************************
int LAGraph_msf
(
GrB_Matrix *result, // output: an unsymmetrical matrix, the spanning forest
GrB_Matrix A, // input matrix
bool sanitize, // if true, ensure A is symmetric
char *msg
)
{
LG_CLEAR_MSG ;
#if !LAGRAPH_SUITESPARSE
return (GrB_NOT_IMPLEMENTED) ;
#else
GrB_Info info;
GrB_Index n;
GrB_Matrix S = NULL, T = NULL;
GrB_Vector f = NULL, i = NULL, t = NULL,
edge = NULL, cedge = NULL, mask = NULL, index = NULL;
GrB_Index *I = NULL, *V = NULL, *SI = NULL, *SJ = NULL, *SX = NULL;
GrB_BinaryOp comb = NULL;
GrB_Semiring combMin = NULL;
GrB_UnaryOp fst = NULL, snd = NULL;
GrB_IndexUnaryOp s1 = NULL, s2 = NULL;
if (result == NULL || A == NULL) return (GrB_NULL_POINTER) ;
GrB_Index ncols ;
GRB_TRY (GrB_Matrix_nrows (&n, A));
GRB_TRY (GrB_Matrix_ncols (&ncols, A));
if (n != ncols) return (GrB_DIMENSION_MISMATCH) ;
if (sanitize)
{
// S = A+A'
GRB_TRY (GrB_Matrix_new (&S, GrB_UINT64, n, n));
GRB_TRY (GrB_eWiseAdd (S, 0, 0, GrB_PLUS_UINT64, A, A, GrB_DESC_T1));
}
else
{
// Use the input as-is, and assume it is GrB_UINT64 and symmetric
GRB_TRY (GrB_Matrix_dup (&S, A));
}
GRB_TRY (GrB_Matrix_new (&T, GrB_UINT64, n, n));
GRB_TRY (GrB_Vector_new (&t, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&f, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&i, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&edge, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&cedge, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&mask, GrB_BOOL, n));
GRB_TRY (GrB_Vector_new (&index, GrB_UINT64, n));
// temporary arrays
I = malloc (sizeof(GrB_Index) * n);
V = malloc (sizeof(GrB_Index) * n);
SI = malloc (sizeof(GrB_Index) * n * 2);
SJ = malloc (sizeof(GrB_Index) * n * 2);
SX = malloc (sizeof(GrB_Index) * n * 2);
// global arrays
parent = malloc (sizeof(GrB_Index) * n);
weight = malloc (sizeof(GrB_Index) * n);
partner = malloc (sizeof(GrB_Index) * n);
// prepare vectors
for (GrB_Index i = 0; i < n; i++)
I[i] = parent[i] = i;
GRB_TRY (GrB_Vector_build (f, I, parent, n, GrB_PLUS_UINT64));
GRB_TRY (GrB_assign (i, 0, 0, f, GrB_ALL, 0, 0));
// semiring & monoid
GrB_Index inf = ((uint64_t) INT_MAX << 32) ^ INT_MAX;
GRB_TRY (GrB_BinaryOp_new (&comb, combine, GrB_UINT64, GrB_UINT64, GrB_UINT64));
GRB_TRY (GrB_Semiring_new (&combMin, GrB_MIN_MONOID_UINT64, comb));
GRB_TRY (GrB_UnaryOp_new (&fst, get_fst, GrB_UINT64, GrB_UINT64));
GRB_TRY (GrB_UnaryOp_new (&snd, get_snd, GrB_UINT64, GrB_UINT64));
// GrB_SelectOp
GrB_IndexUnaryOp_new (&s1, (void *) f1, GrB_BOOL, GrB_UINT64, GrB_UINT64);
GrB_IndexUnaryOp_new (&s2, (void *) f2, GrB_BOOL, GrB_UINT64, GrB_UINT64);
// the main computation
GrB_Index nvals, diff, ntuples = 0, num;
GRB_TRY (GrB_Matrix_nvals (&nvals, S));
for (int iters = 1; nvals > 0; iters++)
{
// every vertex points to a root vertex at the beginning
// edge[u] = u's minimum edge (weight and index are encoded together)
GRB_TRY (GrB_assign (edge, 0, 0, inf, GrB_ALL, 0, 0));
GRB_TRY (GrB_mxv (edge, 0, GrB_MIN_UINT64, combMin, S, f, 0));
// cedge[u] = children's minimum edge | if u is a root
// = (INT_MAX, u) | otherwise
GRB_TRY (GrB_assign (t, 0, 0, (uint64_t) INT_MAX, GrB_ALL, 0, 0));
GRB_TRY (GrB_eWiseMult (cedge, 0, 0, comb, t, i, 0));
LG_TRY (Reduce_assign (cedge, edge, parent, n));
// if (f[u] == u) f[u] := snd(cedge[u]) -- the index part of the edge
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GrB_EQ_UINT64, f, i, 0));
GRB_TRY (GrB_apply (f, mask, GrB_SECOND_UINT64, snd, cedge, 0));
// identify all the vertex pairs (u, v) where f[u] == v and f[v] == u
// and then select the minimum of u, v as the new root;
// if (f[f[i]] == i) f[i] = min(f[i], i)
GRB_TRY (GrB_Vector_extractTuples (I, V, &n, f));
GRB_TRY (GrB_extract (t, 0, 0, f, V, n, 0));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GrB_EQ_UINT64, i, t, 0));
GRB_TRY (GrB_assign (f, mask, GrB_MIN_UINT64, i, GrB_ALL, 0, 0));
// five steps to generate the solution
// 1. new roots (f[i] == i) revise their entries in cedge
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GrB_EQ_UINT64, i, f, 0));
GRB_TRY (GrB_assign (cedge, mask, 0, inf, GrB_ALL, 0, 0));
// 2. every vertex tries to know whether one of its edges is selected
GRB_TRY (GrB_extract (t, 0, 0, cedge, parent, n, 0));
GRB_TRY (GrB_eWiseMult (mask ,0, 0, GrB_EQ_UINT64, edge, t, 0));
// 3. each root picks a vertex from its children to generate the solution
GRB_TRY (GrB_assign (index, 0, 0, n, GrB_ALL, 0, 0));
GRB_TRY (GrB_assign (index, mask, 0, i, GrB_ALL, 0, 0));
GRB_TRY (GrB_assign (t, 0, 0, n, GrB_ALL, 0, 0));
LG_TRY (Reduce_assign (t, index, parent, n));
GRB_TRY (GrB_extract (index, 0, 0, t, parent, n, 0));
GRB_TRY (GrB_eWiseMult (mask ,0, 0, GrB_EQ_UINT64, i, index, 0));
// 4. generate the select function (set the global pointers)
GRB_TRY (GrB_assign (t, 0, 0, inf, GrB_ALL, 0, 0));
GRB_TRY (GrB_apply (t, mask, 0, fst, edge, 0));
GRB_TRY (GrB_Vector_extractTuples (I, weight, &n, t));
GRB_TRY (GrB_assign (t, 0, 0, inf, GrB_ALL, 0, 0));
GRB_TRY (GrB_apply (t, mask, 0, snd, edge, 0));
GRB_TRY (GrB_Vector_extractTuples (I, partner, &n, t));
GRB_TRY (GrB_select (T, 0, 0, s1, S, 0, 0));
GRB_TRY (GrB_Vector_clear (t));
// 5. the generated matrix may still have redundant edges
// remove the duplicates by GrB_mxv() and store them as tuples
GRB_TRY (GrB_Vector_clear (edge));
GRB_TRY (GrB_mxv (edge, mask, GrB_MIN_UINT64, combMin, T, i, 0));
GRB_TRY (GrB_Vector_nvals (&num, edge));
GRB_TRY (GrB_apply (t, 0, 0, snd, edge, 0));
GRB_TRY (GrB_Vector_extractTuples (SI + ntuples, SJ + ntuples, &num, t));
GRB_TRY (GrB_apply (t, 0, 0, fst, edge, 0));
GRB_TRY (GrB_Vector_extractTuples (SI + ntuples, SX + ntuples, &num, t));
GRB_TRY (GrB_Vector_clear (t));
ntuples += num;
// path halving until every vertex points on a root
do {
GRB_TRY (GrB_Vector_extractTuples (I, V, &n, f));
GRB_TRY (GrB_extract (t, 0, 0, f, V, n, 0));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GrB_NE_UINT64, f, t, 0));
GRB_TRY (GrB_assign (f, 0, 0, t, GrB_ALL, 0, 0));
GRB_TRY (GrB_reduce (&diff, 0, GrB_PLUS_MONOID_UINT64, mask, 0));
} while (diff != 0);
// remove the edges in the same connected component
GRB_TRY (GrB_Vector_extractTuples (I, parent, &n, f));
GRB_TRY (GrB_select (S, 0, 0, s2, S, 0, 0));
GrB_Matrix_nvals (&nvals, S);
if (nvals == 0) break;
}
GRB_TRY (GrB_Matrix_clear (T));
GRB_TRY (GrB_Matrix_build (T, SI, SJ, SX, ntuples, GrB_SECOND_UINT64));
*result = T;
T = NULL ;
LG_FREE_ALL;
return GrB_SUCCESS;
#endif
}