2 * RANGI, a list-colored graph motif finding program
4 * Copyright (C) 2012 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the modified BSD license.
17 #define MIN(a, b) ((a) < (b) ? (a) : (b))
19 /* N[i] and C[i] lists are terminated with a -1 */
20 static int N
[NNODES
][NNODES
]; /* adjacency list */
21 static int V
[NNODES
][NNODES
]; /* adjacency matrix */
22 static int C
[NNODES
][NCOLORS
]; /* vertex color list */
23 static int D
[NNODES
]; /* degree of vertices */
24 static int S
[NNODES
]; /* node indices, sorted by degree */
25 static int F
[NCOLORS
]; /* color frequency */
27 static int showweights
; /* print weights too */
28 static int usekavosh
; /* use kavosh instead of fanmod */
29 static int doheur
= 1; /* enable heuristics */
30 static int matchingtest
;
32 static int readgraph(void);
33 static void readdot(void);
34 static int maxcolors(int *nodes
);
36 /* see if the vertices have at least the same number of colors */
37 static int feasible(int *nodes
, int rem
)
40 int nc
= 0; /* nodes */
43 for (i
= 0; nodes
[i
] >= 0; i
++)
45 return maxcolors(nodes
) == i
;
47 for (i
= 0; nodes
[i
] >= 0; i
++) {
48 for (j
= 0; C
[nodes
[i
]][j
] >= 0; j
++) {
49 if (!c
[C
[nodes
[i
]][j
]]) {
50 c
[C
[nodes
[i
]][j
]] = 1;
57 return !nsub_finished(i
+ rem
);
60 /* the maximum size of colors that can be assigned to vertices */
61 static int maxcolors(int *nodes
)
63 int *adj
[MNODES
] = {NULL
};
65 for (i
= 0; nodes
[i
] >= 0; i
++)
67 return matching(adj
, i
, NCOLORS
);
70 /* does adding a vertex create a larger motif? */
71 static int extend(int *nodes
)
73 int map
[MNODES
] = {0};
78 for (i
= 0; i
< n
; i
++)
81 for (i
= 0; i
< n
; i
++) {
82 for (j
= 0; N
[nodes
[i
]][j
] >= 0; j
++) {
88 matched
= maxcolors(nodes
);
97 /* found a candidate motif */
98 static int subgraph(int *nodes
)
101 while (nodes
[n
] >= 0)
103 if (maxcolors(nodes
) != n
)
106 if (doheur
&& extend(nodes
))
107 nsub_detected(n
+ 1);
111 /* the number of colors with with lower frequency than c */
112 static int colorrank(int c
)
118 for (i
= 0; i
< NCOLORS
; i
++)
119 if (F
[i
] && (F
[i
] < F
[c
] || (F
[i
] == F
[c
] && i
< c
)))
124 /* the total number of colors that occur in the graph */
125 static int ncolors(void)
129 for (i
= 0; i
< NCOLORS
; i
++)
135 /* is u a good choice for tree root? */
136 static int goodroot(int u
, int crank
)
139 /* ignore vertices with no edges */
142 /* ignore vertices with frequent colors */
143 for (i
= 0; C
[u
][i
] >= 0; i
++)
144 if (colorrank(C
[u
][i
]) <= crank
)
149 static int kavosh_enum(int v
, int *used
, int nsub
);
150 static int fanmod_enum(int v
, int *used
, int nsub
);
152 /* generate all subgraphs of size nsub with all possible root vertices */
153 static int gensubs(int nsub
)
155 int used
[NNODES
] = {0};
158 int crank
= ncolors() - nsub
; /* the maximum color rank of the root */
159 int (*x_enum
)(int, int *, int) = usekavosh
? kavosh_enum
: fanmod_enum
;
160 for (i
= 0; i
< NNODES
; i
++) {
161 if (!doheur
|| goodroot(S
[i
], crank
)) {
163 if (!ownroot(nsub
, i
))
164 found
+= x_enum(S
[i
], used
, nsub
);
170 static int degcmp(const void *i1
, const void *i2
)
172 return D
[*(int *) i1
] - D
[*(int *) i2
];
175 static void init(void)
178 /* remove color-less vertices */
179 for (i
= 0; i
< NNODES
; i
++)
183 for (i
= 0; i
< NNODES
; i
++)
184 for (j
= 0; N
[i
][j
] >= 0; j
++)
186 /* initialize S as the sorted list of vertices based on degrees */
187 for (i
= 0; i
< NNODES
; i
++)
190 qsort(S
, NNODES
, sizeof(S
[0]), degcmp
);
193 static void *thread_main(void *v
)
195 int nsub
= nsub_next(0);
198 } while ((nsub
= nsub_next(nsub
)) > 0);
202 static void printhelp(void)
204 printf("list-colored graph motif finding program\n\n");
205 printf("options:\n");
206 printf("\t-s size \tsize of the motif to search for\n");
207 printf("\t-n count \tthe number of threads to create\n");
208 printf("\t-w \tprint the weight of each motif too\n");
209 printf("\t-d secs \tmotif finding deadline\n");
210 printf("\t-1 \tterminate after finding the first motif\n");
211 printf("\t-k \tuse kavosh instead of fanmod for enumeration\n");
212 printf("\t-e \texhaustive search; disable heuristics\n");
213 printf("\t-m \tuse matching to discover infeasible subgraphs\n");
216 static void stopit(int i
)
218 fprintf(stderr
, "rangi: timeout\n");
222 static void printall(void);
224 int main(int argc
, char *argv
[])
226 char *nsub_spec
= NULL
;
231 int nsub_end
= NCOLORS
;
233 for (i
= 1; i
< argc
; i
++) {
234 switch (argv
[i
][0] == '-' ? argv
[i
][1] : 'h') {
236 nsub_spec
= argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
];
239 nthreads
= atoi(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
]);
242 alarm(atoi(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
]));
264 signal(SIGALRM
, stopit
);
268 nsub_end
= ncolors();
270 int c
= nsub_spec
[strlen(nsub_spec
) - 1];
271 int n
= atoi(nsub_spec
);
276 if (c
!= '-' && c
!= '+')
277 nsub_beg
= nsub_end
= n
;
278 nsub_dir
= c
== '-' ? -1 : 1;
280 proc_init(nsub_beg
, nsub_end
, nsub_dir
, one
, doheur
);
281 /* generate subgraphs */
285 pthread_t threads
[NTHREADS
];
286 for (i
= 0; i
< nthreads
; i
++)
287 pthread_create(&threads
[i
], NULL
, thread_main
, NULL
);
288 for (i
= 0; i
< nthreads
; i
++)
289 pthread_join(threads
[i
], NULL
);
296 /* reading the input graph */
298 static char names
[NNODES
][NLEN
]; /* vertex names */
299 static int nnodes
= 1;
301 static int nodeid(char *name
)
304 for (i
= 0; i
< nnodes
; i
++)
305 if (!strcmp(name
, names
[i
]))
307 strcpy(names
[nnodes
], name
);
312 * Read a graph in this format:
313 * + the number of vertices
314 * + the name and the list of colors of each vertex, terminated with -1
315 * + edges of graphs and their weights
318 * 3 # number vertices
319 * v1 2 4 -1 # the name and the colors of the first vertex
320 * v2 1 -1 # the name and the colors of the second vertex
321 * v3 1 3 4 -1 # the name and the colors of the third vertex
322 * v1 v2 102 # first edge with weight 102 between v1 and v2
323 * v1 v3 223 # second edge with weight 223 between v1 and v3
325 static int readgraph(void)
327 int nn
[NNODES
] = {0};
329 char u_
[NLEN
], v_
[NLEN
];
331 if (scanf("%d", &n
) != 1)
333 for (i
= 1; i
<= n
; i
++) {
337 while (scanf("%d", &c
) == 1 && c
!= -1) {
339 fprintf(stderr
, "rangi: increase NCOLORS!\n");
347 while (scanf("%s %s %d", u_
, v_
, &w
) == 3) {
355 for (i
= 0; i
< NNODES
; i
++)
360 /* reads a graph in graphviz format */
361 static void readdot(void)
363 int nn
[NNODES
] = {0};
369 if (scanf("%d", &u
) != 1)
371 if (getchar() == '-') {
372 if (scanf("-%d[weight=\"%d\"];", &v
, &w
) != 2)
380 if (scanf("colorlist=\"%lu\"];", &c
) != 1)
382 for (i
= 0; i
< sizeof(long) * 8; i
++)
383 if (c
& (1ul << i
)) {
390 for (i
= 0; i
< NNODES
; i
++)
395 /* weighing and printing motifs */
397 /* the minimum weight of the edges */
398 static int weight_min(int *nodes
, int n
)
402 for (i
= 0; i
< n
; i
++) {
403 for (j
= i
+ 1; j
< n
; j
++) {
404 int e
= V
[nodes
[i
]][nodes
[j
]];
412 /* the sum of the weight of the edges */
413 static int weight_sum(int *nodes
, int n
)
417 for (i
= 0; i
< n
; i
++)
418 for (j
= i
+ 1; j
< n
; j
++)
419 w
+= V
[nodes
[i
]][nodes
[j
]];
423 /* the number of edges */
424 static int weight_num(int *nodes
, int n
)
428 for (i
= 0; i
< n
; i
++)
429 for (j
= i
+ 1; j
< n
; j
++)
430 if (V
[nodes
[i
]][nodes
[j
]])
435 /* the weight of maximum spanning tree */
436 static int weight_mst(int *nodes
, int n
)
438 int nei
[NCOLORS
][NCOLORS
] = {{0}}; /* adjacency list */
439 int nnei
[NCOLORS
] = {0}; /* number of neighbors */
440 int wei
[NCOLORS
][NCOLORS
] = {{0}}; /* weight of edges */
441 int sel
[NCOLORS
][NCOLORS
] = {{0}}; /* selected edges in mst */
442 int mark
[NCOLORS
]; /* component of vertices */
444 int oldmark
, newmark
;
446 /* create the induced subgraph of nodes */
447 for (i
= 0; i
< n
; i
++) {
448 for (j
= 0; j
< n
; j
++) {
449 wei
[i
][j
] = V
[nodes
[i
]][nodes
[j
]];
451 nei
[i
][nnei
[i
]++] = j
;
454 /* each node has its own component at the beginning */
455 for (i
= 0; i
< n
; i
++)
457 /* add n - 1 edges */
458 for (i
= 0; i
< n
- 1; i
++) {
459 int maxu
= 0, maxv
= 1;
461 /* finding the maximum edge */
462 for (u
= 0; u
< n
; u
++) {
463 for (j
= 0; j
< nnei
[u
]; j
++) {
465 if (maxedge
< wei
[u
][v
] && !sel
[u
][v
] &&
466 mark
[u
] != mark
[v
]) {
474 /* merging the components */
475 oldmark
= mark
[maxu
];
476 newmark
= mark
[maxv
];
477 for (u
= 0; u
< n
; u
++)
478 if (mark
[u
] == oldmark
)
481 /* calculating the weight of the spanning tree */
482 for (u
= 0; u
< n
; u
++) {
483 for (j
= 0; j
< nnei
[u
]; j
++) {
485 if (u
< v
&& sel
[u
][v
])
492 /* print a motif and its weight */
493 static void printmotif(int *nodes
)
497 while (nodes
[n
] >= 0)
500 for (j
= 0; j
< n
; j
++)
501 printf("%s ", names
[nodes
[j
]]);
503 printf("\t%d %d %d %d",
504 weight_mst(nodes
, n
),
505 weight_sum(nodes
, n
),
506 weight_min(nodes
, n
),
507 weight_num(nodes
, n
));
512 static void printall(void)
516 n
= motifs(sols
, NSOLS
);
517 for (i
= 0; i
< n
; i
++)
522 /* the kavosh enumeration algorithm */
524 /* generate all C(k, n) ways of selecting k integers from {0, ..., n - 1} */
525 static int kavosh_comb(int k
, int n
, int *idx
)
528 /* find the first element that could be moved right */
529 for (i
= k
- 1; i
>= 0; i
--)
530 if (idx
[i
] < n
- (k
- i
- 1) - 1)
535 for (j
= i
+ 1; j
< k
; j
++)
536 idx
[j
] = idx
[i
] + (j
- i
);
541 /* extend the given subgraph starting at the given depth */
542 static int kavosh_fill(int *nodes
, int *prev
, int *used
, int rem
)
544 int cur
[NNODES
]; /* vertices in this depth */
545 int idx
[NNODES
]; /* index of selected vertices in cur[] */
546 int *sel
; /* inserted vertices into nodes */
550 if (doheur
&& !feasible(nodes
, rem
))
553 return subgraph(nodes
);
555 * put the vertices with the given depth into cur[] and mark
558 for (i
= 0; prev
[i
] >= 0; i
++) {
560 for (j
= 0; N
[u
][j
] >= 0; j
++) {
562 if (!used
[v
] && N
[v
][0] >= 0) {
569 /* where to insert new nodes */
573 /* try all subgraphs with k vertices at this depth */
574 for (k
= 1; k
<= MIN(ncur
, rem
); k
++) {
575 for (i
= 0; i
< k
; i
++)
578 /* enumerate all sel[] of size k from cur[] of size ncur */
580 for (i
= 0; i
< k
; i
++)
581 sel
[i
] = cur
[idx
[i
]];
582 ret
+= kavosh_fill(nodes
, sel
, used
, rem
- k
);
583 } while (!kavosh_comb(k
, ncur
, idx
));
585 for (i
= 0; cur
[i
] >= 0; i
++)
591 /* an optimized version of the Kavosh algorithm */
592 static int kavosh_enum(int v
, int *used
, int nsub
)
594 int nodes
[NNODES
] = {v
, -1};
596 return kavosh_fill(nodes
, nodes
, used
, nsub
- 1);
600 /* the fanmod enumeration algorithm */
602 /* insert the neighbors of w into the extension list */
603 static int fanmod_addext(int *sub_map
, int *ext_map
, int *ext
, int *used
, int w
)
607 for (i
= 0; N
[w
][i
] >= 0; i
++) {
609 if (used
[u
] || sub_map
[u
] || ext_map
[u
])
618 /* remove the vertices added via fanmod_addext */
619 static void fanmod_delext(int *ext_map
, int *ext
)
622 for (i
= 0; ext
[i
] >= 0; i
++)
627 /* recursively add vertices to sub; see the comments in fanmod_enum() */
628 static int fanmod_fill(int *used
, int *sub
, int *sub_map
,
629 int *ext
, int *ext_map
, int rem
)
631 int n_sub
= 0; /* number of entries in sub */
632 int n_ext
= 0; /* number of entries in ext */
635 if (doheur
&& !feasible(sub
, rem
))
638 return subgraph(sub
);
639 while (sub
[n_sub
] >= 0)
641 while (ext
[n_ext
] >= 0)
643 for (i
= 0; i
< n_ext
; i
++) {
647 fanmod_addext(sub_map
, ext_map
, ext
+ n_ext
, used
, ext
[i
]);
648 found
+= fanmod_fill(used
, sub
, sub_map
, ext
+ i
+ 1, ext_map
, rem
- 1);
649 fanmod_delext(ext_map
, ext
+ n_ext
);
656 /* an optimized version of the ESU algorithm */
657 static int fanmod_enum(int v
, int *used
, int nsub
)
659 int sub
[NNODES
] = {v
, -1}; /* the -1 terminated subgraph vertices */
660 int sub_map
[NNODES
] = {0}; /* sub_map[i] is one if i is in sub */
661 int ext
[NNODES
] = {-1}; /* the -1 terminated extension list */
662 int ext_map
[NNODES
] = {0}; /* ext_map[i] is one if i is in ext */
664 fanmod_addext(sub_map
, ext_map
, ext
, used
, v
);
665 return fanmod_fill(used
, sub
, sub_map
, ext
, ext_map
, nsub
- 1);