Graphviz 16.1.1~dev.20260922.0048
Loading...
Searching...
No Matches
mq.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/* Modularity Quality definition:
12
13 We assume undirected graph. Directed graph should be converted by summing edge weights.
14
15 Given a partition P of V into k clusters.
16
17 Let E(i,j) be the set of edges between cluster i and j.
18 Let |E(i,j)| be the sum of edge weights of edges in E(i,j).
19
20 Let E(i,i) be the set of edges within cluster i, but excluding self-edges.
21 Let |E(i,i)| be the sum of edge weights of edges in E(i,i).
22
23 Let V(i) be the sets of vertices in i
24
25 The intra-cluster edges concentration for a cluster i is
26 (the denominator could be |V(i)|*(|V(i)-1)/2 strictly speaking as we exclude self-edges):
27
28 |E(i,i)|
29 -----------
30 (|V(i)|^2/2)
31
32 The inter-cluster edges concentration between cluster i and j is
33
34 |E(i,j)|
35 ------------
36 |V(i)|*|V(j)|
37
38 So the cluster index is defined as the average intra cluster edge concentration, minus
39 the inter-cluster edge concentration:
40
41 . |E(i,i)| |E(i,j)|
42 MQ(P) = (1/k) * \sum_{i=1...k} ------------ - (1/(k*(k-1)/2)) * \sum_{i<j} ------------------- = mq_in/k - mq_out/(k*(k-1)/2)
43 . (|V(i)|^2/2) |V(i)|*|V(j)|
44
45 or
46
47 . |E(i,i)| |E(i,j)|
48 MQ(P)/2 = (1/k) * \sum_{i=1...k} ------------ - (1/(k*(k-1))) * \sum_{i<j} ------------------ = mq_in/k - mq_out/(k*(k-1))
49 . |V(i)|^2 |V(i)|*|V(j)|
50
51 Notice that if we assume the graph is unweights (edge weights = 1), then 0<= MQ <= 1.
52 For weighted graph, MQ may not be within 0 to 1. We could normalized it, but
53 for comparing clustering quality of the same graph but different partitioning, this
54 unnormalized quantity is not a problem.
55
56*/
57
58#include "config.h"
59
60#define STANDALONE
61#include <assert.h>
62#include <limits.h>
63#include <sparse/general.h>
64#include <sparse/SparseMatrix.h>
65#include <sparse/mq.h>
66#include <stdbool.h>
67#include <stddef.h>
68#include <string.h>
69#include <util/alloc.h>
70#include <util/list.h>
71
72static double get_mq(SparseMatrix A, int *assignment, int *ncluster0, double *mq_in0, double *mq_out0, double **dout0){
73 /* given a symmetric matrix representation of a graph and an assignment of nodes into clusters, calculate the modularity quality.
74 assignment: assignment[i] gives the cluster assignment of node i. 0 <= assignment[i] < ncluster.
75 ncluster: number of clusters
76 mq_in: the part of MQ to do with intra-cluster edges, before divide by 1/k
77 mq_out: the part of MQ to do with inter-cluster edges, before divide by 1/(k*(k-1))
78 mq = 2*(mq_in/k - mq_out/(k*(k-1)));
79 */
80 int ncluster = 0;
81 const size_t n = A->m;
82 bool test_pattern_symmetry_only = false;
83 int *counts, *ia = A->ia, *ja = A->ja, k, j, jj;
84 double mq_in = 0, mq_out = 0, *a = NULL, Vi, Vj;
85 int c;
86 double *dout;
87
88
89 assert(SparseMatrix_is_symmetric(A, test_pattern_symmetry_only));
90 (void)test_pattern_symmetry_only;
91 assert((size_t)A->n == n);
92 if (A->type == MATRIX_TYPE_REAL) a = A->a;
93
94 counts = gv_calloc(n, sizeof(int));
95
96 for (size_t i = 0; i < n; i++){
97 assert(assignment[i] >= 0 && (size_t)assignment[i] < n);
98 if (counts[assignment[i]] == 0) ncluster++;
99 counts[assignment[i]]++;
100 }
101 k = ncluster;
102 assert(ncluster <= (int)n);
103
104 for (size_t i = 0; i < n; i++){
105 assert(assignment[i] < ncluster);
106 c = assignment[i];
107 Vi = counts[c];
108 for (j = ia[i] ; j < ia[i+1]; j++){
109 /* ASSUME UNDIRECTED */
110 jj = ja[j];
111 if (jj >= (int)i) continue;
112 assert(assignment[jj] < ncluster);
113 Vj = counts[assignment[jj]];
114 if (assignment[jj] == c){
115 if (a) {
116 mq_in += a[j]/(Vi*Vi);
117 } else {
118 mq_in += 1./(Vi*Vi);
119 }
120 } else {
121 if (a) {
122 mq_out += a[j]/(Vi*Vj);
123 } else {
124 mq_out += 1./(Vi*Vj);
125 }
126 }
127
128 }
129 }
130
131 /* calculate scaled out degree */
132 dout = gv_calloc(n, sizeof(double));
133 for (size_t i = 0; i < n; i++){
134 for (j = ia[i]; j < ia[i+1]; j++){
135 jj = ja[j];
136 if (jj == (int)i) continue;
137 if (a){
138 dout[i] += a[j]/(double) counts[assignment[jj]];
139 } else {
140 dout[i] += 1./(double) counts[assignment[jj]];
141 }
142 }
143 }
144
145 *ncluster0 = k;
146 *mq_in0 = mq_in;
147 *mq_out0 = mq_out;
148 *dout0 = dout;
149 free(counts);
150
151 if (k > 1){
152 return 2*(mq_in/k - mq_out/(k*(k-1)));
153 } else {
154 return 2*mq_in;
155 }
156}
157
160 int n = A->n, i;
161 int *matching;
162
163 assert(A->type == MATRIX_TYPE_REAL);
164 assert(SparseMatrix_is_symmetric(A, false));
165
166 assert(A->m == (size_t)n);
168 grid->level = level;
169 grid->n = n;
170 grid->A = A;
171 grid->P = NULL;
172 grid->next = NULL;
173 grid->prev = NULL;
174 grid->delete_top_level_A = false;
175 matching = grid->matching = gv_calloc(n, sizeof(double));
176 grid->deg_intra = NULL;
177 grid->dout = NULL;
178 grid->wgt = NULL;
179
180 if (level == 0){
181 double mq = 0, mq_in, mq_out;
182 int ncluster;
183 double *deg_intra, *wgt, *dout;
184
185 grid->deg_intra = gv_calloc(n, sizeof(double));
186 deg_intra = grid->deg_intra;
187
188 grid->wgt = gv_calloc(n, sizeof(double));
189 wgt = grid->wgt;
190
191 for (i = 0; i < n; i++){
192 deg_intra[i] = 0;
193 wgt[i] = 1.;
194 }
195 for (i = 0; i < n; i++) matching[i] = i;
196 mq = get_mq(A, matching, &ncluster, &mq_in, &mq_out, &dout);
197 fprintf(stderr,"ncluster = %d, mq = %f\n", ncluster, mq);
198 grid->mq = mq;
199 grid->mq_in = mq_in;
200 grid->mq_out = mq_out;
201 grid->dout = dout;
202 grid->ncluster = ncluster;
203
204 }
205
206
207 return grid;
208}
209
211 if (!grid) return;
212 if (grid->A){
213 if (grid->level == 0) {
214 if (grid->delete_top_level_A) SparseMatrix_delete(grid->A);
215 } else {
217 }
218 }
220 free(grid->matching);
221 free(grid->deg_intra);
222 free(grid->dout);
223 free(grid->wgt);
225 free(grid);
226}
227
229 int *matching = grid->matching;
230 SparseMatrix A = grid->A;
231 int n = grid->n, level = grid->level, nc = 0, nclusters = n;
232 double mq = 0, mq_in = 0, mq_out = 0, mq_new, mq_in_new, mq_out_new, mq_max = 0, mq_in_max = 0, mq_out_max = 0;
233 int *ia = A->ia, *ja = A->ja;
234 double amax = 0;
235 double *deg_intra = grid->deg_intra, *wgt = grid->wgt;
236 int i, j, k, jj, jc, jmax;
237 double gain = 0, *dout = grid->dout, deg_in_i, deg_in_j, wgt_i, wgt_j, a_ij, dout_i, dout_j, dout_max = 0, wgt_jmax = 0;
238 double maxgain = 0;
239 double total_gain = 0;
240
241 LIST(int) *neighbors = gv_calloc(n, sizeof(neighbors[0]));
242
243 mq = grid->mq;
244 mq_in = grid->mq_in;
245 mq_out = grid->mq_out;
246
247 double *deg_intra_new = gv_calloc(n, sizeof(double));
248 double *wgt_new = gv_calloc(n, sizeof(double));
249 double *deg_inter = gv_calloc(n, sizeof(double));
250 int *mask = gv_calloc(n, sizeof(int));
251 double *dout_new = gv_calloc(n, sizeof(double));
252 for (i = 0; i < n; i++) mask[i] = -1;
253
254 assert(n == A->n);
255 for (i = 0; i < n; i++) matching[i] = UNMATCHED;
256
257 /* gain in merging node A into cluster B is
258 mq_in_new = mq_in - |E(A,A)|/(V(A))^2 - |E(B,B)|/(V(B))^2 + (|E(A,A)|+|E(B,B)|+|E(A,B)|)/(|V(A)|+|V(B)|)^2
259 . = mq_in - deg_intra(A)/|A|^2 - deg_intra(B)/|B|^2 + (deg_intra(A)+deg_intra(B)+a(A,B))/(|A|+|B|)^2
260
261 mq_out_new = mq_out - |E(A,B)|/(|V(A)|*V(B)|)-\sum_{C and A connected, C!=B} |E(A,C)|/(|V(A)|*|V(C)|)-\sum_{C and B connected,C!=B} |E(B,C)|/(|V(B)|*|V(C)|)
262 . + \sum_{C connected to A or B, C!=A, C!=B} (|E(A,C)|+|E(B,C)|)/(|V(C)|*(|V(A)|+|V(B)|)
263 . = mq_out + a(A,B)/(|A|*|B|)-\sum_{C and A connected} a(A,C)/(|A|*|C|)-\sum_{C and B connected} a(B,C)/(|B|*|C|)
264 . + \sum_{C connected to A or B, C!=A, C!=B} (a(A,C)+a(B,C))/(|C|*(|A|+|B|))
265 Denote:
266 dout(i) = \sum_{j -- i} a(i,j)/|j|
267 then
268
269 mq_out_new = mq_out - |E(A,B)|/(|V(A)|*V(B)|)-\sum_{C and A connected, C!=B} |E(A,C)|/(|V(A)|*|V(C)|)-\sum_{C and B connected,C!=B} |E(B,C)|/(|V(B)|*|V(C)|)
270 . + \sum_{C connected to A or B, C!=A, C!=B} (|E(A,C)|+|E(B,C)|)/(|V(C)|*(|V(A)|+|V(B)|)
271 . = mq_out + a(A,B)/(|A|*|B|)-dout(A)/|A| - dout(B)/|B|
272 . + (dout(A)+dout(B))/(|A|+|B|) - (a(A,B)/|A|+a(A,B)/|B|)/(|A|+|B|)
273 . = mq_out -dout(A)/|A| - dout(B)/|B| + (dout(A)+dout(B))/(|A|+|B|)
274 after merging A and B into cluster AB,
275 dout(AB) = dout(A) + dout(B);
276 dout(C) := dout(C) - a(A,C)/|A| - a(B,C)/|B| + a(A,C)/(|A|+|B|) + a(B, C)/(|A|+|B|)
277
278 mq_new = mq_in_new/(k-1) - mq_out_new/((k-1)*(k-2))
279 gain = mq_new - mq
280 */
281 double *a = A->a;
282 for (i = 0; i < n; i++){
283 if (matching[i] != UNMATCHED) continue;
284 /* accumulate connections between i and clusters */
285 for (j = ia[i]; j < ia[i+1]; j++){
286 jj = ja[j];
287 if (jj == i) continue;
288 if ((jc=matching[jj]) != UNMATCHED){
289 if (mask[jc] != i) {
290 mask[jc] = i;
291 deg_inter[jc] = a[j];
292 } else {
293 deg_inter[jc] += a[j];
294 }
295 }
296 }
297 deg_in_i = deg_intra[i];
298 wgt_i = wgt[i];
299 dout_i = dout[i];
300
301 maxgain = 0;
302 jmax = -1;
303 for (j = ia[i]; j < ia[i+1]; j++){
304 jj = ja[j];
305 if (jj == i) continue;
306 jc = matching[jj];
307 if (jc == UNMATCHED){
308 a_ij = a[j];
309 wgt_j = wgt[jj];
310 deg_in_j = deg_intra[jj];
311 dout_j = dout[jj];
312 } else if (deg_inter[jc] < 0){
313 continue;
314 } else {
315 a_ij = deg_inter[jc];
316 wgt_j = wgt_new[jc];
317 deg_inter[jc] = -1; // so that we do not redo the calculation when we hit another neighbor in cluster jc
318 deg_in_j = deg_intra_new[jc];
319 dout_j = dout_new[jc];
320 }
321
322 mq_in_new = mq_in - deg_in_i/pow(wgt_i, 2) - deg_in_j/pow(wgt_j,2)
323 + (deg_in_i + deg_in_j + a_ij)/pow(wgt_i + wgt_j,2);
324
325 mq_out_new = mq_out - dout_i/wgt_i - dout_j/wgt_j + (dout_i + dout_j)/(wgt_i + wgt_j);
326
327 if (nclusters > 2){
328 mq_new = 2*(mq_in_new/(nclusters - 1) - mq_out_new/((nclusters - 1)*(nclusters - 2)));
329 } else {
330 mq_new = 2*mq_in_new/(nclusters - 1);
331 }
332
333#ifdef DEBUG
334 {int ncluster;
335 double mq2, mq_in2, mq_out2, *dout2;
336 int nc2 = nc;
337 int *matching2 = gv_calloc(A->m, sizeof(int));
338 memcpy(matching2, matching, sizeof(double)*A->m);
339 if (jc != UNMATCHED) {
340 matching2[i] = jc;
341 } else {
342 matching2[i] = nc2;
343 matching2[jj] = nc2;
344 nc2++;
345 }
346 for (k = 0; k < n; k++) if (matching2[k] == UNMATCHED) matching2[k] =nc2++;
347 mq2 = get_mq(A, matching2, &ncluster, &mq_in2, &mq_out2, &dout2);
348 fprintf(stderr," {dout_i, dout_j}={%f,%f}, {predicted, calculated}: mq = {%f, %f}, mq_in ={%f,%f}, mq_out = {%f,%f}\n",dout_i, dout_j, mq_new, mq2, mq_in_new, mq_in2, mq_out_new, mq_out2);
349
350 mq_new = mq2;
351
352 }
353#endif
354
355 gain = mq_new - mq;
356 if (Verbose) fprintf(stderr,"gain in merging node %d with node %d = %f-%f = %f\n", i, jj, mq, mq_new, gain);
357 if (j == ia[i] || gain > maxgain){
358 maxgain = gain;
359 jmax = jj;
360 amax = a_ij;
361 dout_max = dout_j;
362 wgt_jmax = wgt_j;
363 mq_max = mq_new;
364 mq_in_max = mq_in_new;
365 mq_out_max = mq_out_new;
366 }
367
368 }
369
370 /* now merge i and jmax */
371 if (maxgain > 0 || (nc >= 1 && nc > maxcluster)){
372 total_gain += maxgain;
373 jc = matching[jmax];
374 if (jc == UNMATCHED){
375 fprintf(stderr, "maxgain=%f, merge %d, %d\n",maxgain, i, jmax);
376 LIST_APPEND(&neighbors[nc], jmax);
377 LIST_APPEND(&neighbors[nc], i);
378 dout_new[nc] = dout_i + dout_max;
379 matching[i] = matching[jmax] = nc;
380 wgt_new[nc] = wgt[i] + wgt[jmax];
381 deg_intra_new[nc] = deg_intra[i] + deg_intra[jmax] + amax;
382 nc++;
383 } else {
384 fprintf(stderr,"maxgain=%f, merge with existing cluster %d, %d\n",maxgain, i, jc);
385 LIST_APPEND(&neighbors[jc], i);
386 dout_new[jc] = dout_i + dout_max;
387 wgt_new[jc] += wgt[i];
388 matching[i] = jc;
389 deg_intra_new[jc] += deg_intra[i] + amax;
390 }
391 mq = mq_max;
392 mq_in = mq_in_max;
393 mq_out = mq_out_max;
394 nclusters--;
395 } else {
396 fprintf(stderr,"gain: %f -- no gain, skip merging node %d\n", maxgain, i);
397 assert(maxgain <= 0);
398 LIST_APPEND(&neighbors[nc], i);
399 matching[i] = nc;
400 deg_intra_new[nc] = deg_intra[i];
401 wgt_new[nc] = wgt[i];
402 nc++;
403 }
404
405
406 /* update scaled outdegree of neighbors of i and its merged node/cluster jmax */
407 jc = matching[i];
408 for (size_t l = LIST_SIZE(&neighbors[jc]) - 1; l != SIZE_MAX; --l) {
409 mask[LIST_GET(&neighbors[jc], l)] = n + i;
410 }
411
412 for (size_t l = LIST_SIZE(&neighbors[jc]) - 1; l != SIZE_MAX; --l) {
413 k = LIST_GET(&neighbors[jc], l);
414 for (j = ia[k]; j < ia[k+1]; j++){
415 jj = ja[j];
416 if (mask[jj] == n+i) continue;/* link to within cluster */
417 const int match = matching[jj];
418 if (match == UNMATCHED){
419 if (k == i){
420 dout[jj] += -a[j]/wgt_i + a[j]/(wgt_i + wgt_jmax);
421 } else {
422 dout[jj] += -a[j]/wgt_jmax + a[j]/(wgt_i + wgt_jmax);
423 }
424 } else {
425 if (k == i){
426 dout_new[match] += -a[j] / wgt_i + a[j] / (wgt_i + wgt_jmax);
427 } else {
428 dout_new[match] += -a[j] / wgt_jmax + a[j] / (wgt_i + wgt_jmax);
429 }
430 }
431 }
432 }
433
434 }
435
436 fprintf(stderr,"verbose=%d\n",Verbose);
437 if (Verbose) fprintf(stderr,"mq = %f new mq = %f level = %d, n = %d, nc = %d, gain = %g, mq_in = %f, mq_out = %f\n", mq, mq + total_gain,
438 level, n, nc, total_gain, mq_in, mq_out);
439
440#ifdef DEBUG
441 {int ncluster;
442
443 mq = get_mq(A, matching, &ncluster, &mq_in, &mq_out, &dout);
444 fprintf(stderr," mq = %f\n",mq);
445
446 }
447#endif
448
449 if (nc >= 1 && (total_gain > 0 || nc < n)){
450 /* now set up restriction and prolongation operator */
451 SparseMatrix P, R, R0, B, cA;
452 double one = 1.;
454
455 R0 = SparseMatrix_new((size_t)nc, n, 1, MATRIX_TYPE_REAL, FORMAT_COORD);
456 for (i = 0; i < n; i++){
457 jj = matching[i];
459 }
465 if (!B) {
466 free(deg_intra_new);
467 free(wgt_new);
468 free(dout_new);
469 goto RETURN;
470 }
471 cA = SparseMatrix_multiply(B, P);
473 if (!cA) {
474 free(deg_intra_new);
475 free(wgt_new);
476 free(dout_new);
477 goto RETURN;
478 }
479 grid->P = P;
480 level++;
481 cgrid = Multilevel_MQ_Clustering_init(cA, level);
482 deg_intra_new = gv_recalloc(deg_intra_new, n, nc, sizeof(double));
483 wgt_new = gv_recalloc(wgt_new, n, nc, sizeof(double));
484 cgrid->deg_intra = deg_intra_new;
485 cgrid->mq = grid->mq + total_gain;
486 cgrid->wgt = wgt_new;
487 dout_new = gv_recalloc(dout_new, n, nc, sizeof(double));
488 cgrid->dout = dout_new;
489
490 cgrid = Multilevel_MQ_Clustering_establish(cgrid, maxcluster);
491
492 grid->next = cgrid;
493 cgrid->prev = grid;
494 } else {
495 /* no more improvement, stop and final clustering found */
496 for (i = 0; i < n; i++) matching[i] = i;
497
498 free(deg_intra_new);
499 free(wgt_new);
500 free(dout_new);
501 }
502
503 RETURN:
504 for (i = 0; i < n; i++) LIST_FREE(&neighbors[i]);
505 free(neighbors);
506
507 free(deg_inter);
508 free(mask);
509 return grid;
510}
511
513 /* maxcluster is used to specify the maximum number of cluster desired, e.g., maxcluster=10 means that a maximum of 10 clusters
514 is desired. this may not always be realized, and mq may be low when this is specified. Default: maxcluster = 0 */
516 SparseMatrix A = A0;
517
518 if (maxcluster <= 0) maxcluster = (int)A->m;
519 if (!SparseMatrix_is_symmetric(A, false) || A->type != MATRIX_TYPE_REAL){
521 }
523
525
526 if (A != A0) grid->delete_top_level_A = true; // be sure to clean up later
527 return grid;
528}
529
530
531static void hierachical_mq_clustering(SparseMatrix A, int maxcluster,
532 int *nclusters, int **assignment, double *mq){
533 /* find a clustering of vertices by maximize mq
534 A: symmetric square matrix n x n. If real value, value will be used as edges weights, otherwise edge weights are considered as 1.
535 maxcluster: used to specify the maximum number of cluster desired, e.g., maxcluster=10 means that a maximum of 10 clusters
536 . is desired. this may not always be realized, and mq may be low when this is specified. Default: maxcluster = 0
537 nclusters: on output the number of clusters
538 assignment: dimension n. Node i is assigned to cluster "assignment[i]". 0 <= assignment < nclusters
539 */
540
542 int *matching, i;
543 SparseMatrix P;
544 assert(A->m == (size_t)A->n);
545
546 *mq = 0.;
547
548 grid = Multilevel_MQ_Clustering_new(A, maxcluster);
549
550 /* find coarsest */
551 cgrid = grid;
552 while (cgrid->next){
553 cgrid = cgrid->next;
554 }
555
556 /* project clustering up */
557 double *u = gv_calloc(cgrid->n, sizeof(double));
558 for (i = 0; i < cgrid->n; i++) u[i] = (double) (cgrid->matching)[i];
559 *nclusters = cgrid->n;
560 *mq = cgrid->mq;
561
562 while (cgrid->prev){
563 double *v = NULL;
564 P = cgrid->prev->P;
566 free(u);
567 u = v;
568 cgrid = cgrid->prev;
569 }
570
571 if (*assignment){
572 matching = *assignment;
573 } else {
574 matching = gv_calloc(grid->n, sizeof(int));
575 *assignment = matching;
576 }
577 for (i = 0; i < grid->n; i++) (matching)[i] = (int) u[i];
578 free(u);
579
581}
582
583
584
585void mq_clustering(SparseMatrix A, int maxcluster,
586 int *nclusters, int **assignment, double *mq){
587 /* find a clustering of vertices by maximize mq
588 A: symmetric square matrix n x n. If real value, value will be used as edges weights, otherwise edge weights are considered as 1.
589 maxcluster: used to specify the maximum number of cluster desired, e.g., maxcluster=10 means that a maximum of 10 clusters
590 . is desired. this may not always be realized, and mq may be low when this is specified. Default: maxcluster = 0
591 nclusters: on output the number of clusters
592 assignment: dimension n. Node i is assigned to cluster "assignment[i]". 0 <= assignment < nclusters
593 */
595
596 assert(A->m == (size_t)A->n);
597
598 B = SparseMatrix_symmetrize(A, false);
599
600 if (B == A) {
602 }
603
605
606 assert(B->type == MATRIX_TYPE_REAL);
607
608 hierachical_mq_clustering(B, maxcluster, nclusters, assignment, mq);
609
610 if (B != A) SparseMatrix_delete(B);
611
612}
SparseMatrix SparseMatrix_new(size_t m, int n, size_t nz, int type, int format)
SparseMatrix SparseMatrix_from_coordinate_format(SparseMatrix A)
SparseMatrix SparseMatrix_transpose(SparseMatrix A)
SparseMatrix SparseMatrix_symmetrize(SparseMatrix A, bool pattern_symmetric_only)
bool SparseMatrix_is_symmetric(SparseMatrix A, bool test_pattern_symmetry_only)
void SparseMatrix_multiply_vector(SparseMatrix A, double *v, double **res)
SparseMatrix SparseMatrix_multiply(SparseMatrix A, SparseMatrix B)
void SparseMatrix_delete(SparseMatrix A)
SparseMatrix SparseMatrix_copy(SparseMatrix A)
SparseMatrix SparseMatrix_get_real_adjacency_matrix_symmetrized(SparseMatrix A)
SparseMatrix SparseMatrix_remove_diagonal(SparseMatrix A)
@ MATRIX_TYPE_REAL
@ FORMAT_COORD
#define SparseMatrix_coordinate_form_add_entry(A, irn, jcn, val)
wrap SparseMatrix_coordinate_form_add_entry_ for type safety
size_t match(char *str, char *pat)
return index of pattern pat in string str, or SIZE_MAX
Definition actions.c:91
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define A(n, t)
Definition expr.h:76
@ UNMATCHED
Definition general.h:75
static bool Verbose
Definition gml2gv.c:26
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:181
@ grid
Definition gvgen.c:34
#define B
Definition hierarchy.c:120
type-generic dynamically expanding list
#define LIST_APPEND(list,...)
Definition list.h:151
#define LIST(type)
Definition list.h:66
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_FREE(list)
Definition list.h:413
#define LIST_GET(list, index)
Definition list.h:197
void mq_clustering(SparseMatrix A, int maxcluster, int *nclusters, int **assignment, double *mq)
Definition mq.c:585
static Multilevel_MQ_Clustering Multilevel_MQ_Clustering_init(SparseMatrix A, int level)
Definition mq.c:158
static void hierachical_mq_clustering(SparseMatrix A, int maxcluster, int *nclusters, int **assignment, double *mq)
Definition mq.c:531
static Multilevel_MQ_Clustering Multilevel_MQ_Clustering_new(SparseMatrix A0, int maxcluster)
Definition mq.c:512
static void Multilevel_MQ_Clustering_delete(Multilevel_MQ_Clustering grid)
Definition mq.c:210
static double get_mq(SparseMatrix A, int *assignment, int *ncluster0, double *mq_in0, double *mq_out0, double **dout0)
Definition mq.c:72
static Multilevel_MQ_Clustering Multilevel_MQ_Clustering_establish(Multilevel_MQ_Clustering grid, int maxcluster)
Definition mq.c:228
#define RETURN(v)
Definition strmatch.c:146
Multilevel_MQ_Clustering prev
Definition mq.h:27
Multilevel_MQ_Clustering next
Definition mq.h:26
size_t m
row dimension