sccmap.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /**
  2. * @file
  3. * @brief extract strongly connected components of directed graphs
  4. */
  5. /*************************************************************************
  6. * Copyright (c) 2011 AT&T Intellectual Property
  7. * All rights reserved. This program and the accompanying materials
  8. * are made available under the terms of the Eclipse Public License v1.0
  9. * which accompanies this distribution, and is available at
  10. * https://www.eclipse.org/legal/epl-v10.html
  11. *
  12. * Contributors: Details at https://graphviz.org
  13. *************************************************************************/
  14. /*
  15. * Written by Stephen North
  16. * Updated by Emden Gansner
  17. */
  18. /*
  19. * This is a filter that reads a graph, searches for strongly
  20. * connected components, and writes each as a separate graph
  21. * along with a map of the components.
  22. */
  23. #include <limits.h>
  24. #include <stdbool.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <cgraph/cgraph.h>
  28. #include <cgraph/ingraphs.h>
  29. #include <cgraph/list.h>
  30. #include <getopt.h>
  31. #include "openFile.h"
  32. #include <util/exit.h>
  33. #include <util/unreachable.h>
  34. #define INF UINT_MAX
  35. typedef struct Agraphinfo_t {
  36. Agrec_t h;
  37. Agnode_t *rep;
  38. } Agraphinfo_t;
  39. typedef struct Agnodeinfo_t {
  40. Agrec_t h;
  41. unsigned int val;
  42. Agraph_t *scc;
  43. } Agnodeinfo_t;
  44. static Agnode_t *getrep(Agraph_t * g)
  45. {
  46. return ((Agraphinfo_t *)g->base.data)->rep;
  47. }
  48. static void setrep(Agraph_t * g, Agnode_t * rep)
  49. {
  50. ((Agraphinfo_t *)g->base.data)->rep = rep;
  51. }
  52. static Agraph_t *getscc(Agnode_t * n)
  53. {
  54. return ((Agnodeinfo_t *)n->base.data)->scc;
  55. }
  56. static void setscc(Agnode_t * n, Agraph_t * scc)
  57. {
  58. ((Agnodeinfo_t *)n->base.data)->scc = scc;
  59. }
  60. static unsigned getval(Agnode_t *n) {
  61. return ((Agnodeinfo_t *)n->base.data)->val;
  62. }
  63. static void setval(Agnode_t *n, unsigned v) {
  64. ((Agnodeinfo_t *)n->base.data)->val = v;
  65. }
  66. typedef struct {
  67. unsigned Comp;
  68. unsigned ID;
  69. int N_nodes_in_nontriv_SCC;
  70. } sccstate;
  71. static int wantDegenerateComp;
  72. static int Silent;
  73. static int StatsOnly;
  74. static int Verbose;
  75. static char *CmdName;
  76. static char **Files;
  77. static FILE *outfp; /* output; stdout by default */
  78. static void nodeInduce(Agraph_t * g, Agraph_t* map)
  79. {
  80. Agnode_t *n;
  81. Agedge_t *e;
  82. Agraph_t* rootg = agroot (g);
  83. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  84. for (e = agfstout(rootg, n); e; e = agnxtout(rootg, e)) {
  85. if (agsubnode(g, aghead(e), 0))
  86. agsubedge(g, e, 1);
  87. else {
  88. Agraph_t* tscc = getscc(agtail(e));
  89. Agraph_t* hscc = getscc(aghead(e));
  90. if (tscc && hscc)
  91. agedge(map, getrep(tscc), getrep(hscc), NULL, 1);
  92. }
  93. }
  94. }
  95. }
  96. DEFINE_LIST(node_stack, Agnode_t *)
  97. static unsigned visit(Agnode_t *n, Agraph_t *map, node_stack_t *sp,
  98. sccstate *st) {
  99. unsigned int m, min;
  100. Agnode_t *t;
  101. Agraph_t *subg;
  102. Agedge_t *e;
  103. min = ++st->ID;
  104. setval(n, min);
  105. node_stack_push_back(sp, n);
  106. for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e)) {
  107. t = aghead(e);
  108. if (getval(t) == 0)
  109. m = visit(t, map, sp, st);
  110. else
  111. m = getval(t);
  112. if (m < min)
  113. min = m;
  114. }
  115. if (getval(n) == min) {
  116. if (!wantDegenerateComp && *node_stack_back(sp) == n) {
  117. setval(n, INF);
  118. (void)node_stack_pop_back(sp);
  119. } else {
  120. char name[32];
  121. Agraph_t *G = agraphof(n);;
  122. snprintf(name, sizeof(name), "cluster_%u", st->Comp++);
  123. subg = agsubg(G, name, 1);
  124. agbindrec(subg, "scc_graph", sizeof(Agraphinfo_t), true);
  125. setrep(subg, agnode(map, name, 1));
  126. do {
  127. t = node_stack_pop_back(sp);
  128. agsubnode(subg, t, 1);
  129. setval(t, INF);
  130. setscc(t, subg);
  131. st->N_nodes_in_nontriv_SCC++;
  132. } while (t != n);
  133. nodeInduce(subg, map);
  134. if (!StatsOnly)
  135. agwrite(subg, outfp);
  136. }
  137. }
  138. return min;
  139. }
  140. static int label(Agnode_t * n, int nodecnt, int *edgecnt)
  141. {
  142. Agedge_t *e;
  143. setval(n, 1);
  144. nodecnt++;
  145. for (e = agfstedge(n->root, n); e; e = agnxtedge(n->root, e, n)) {
  146. *edgecnt += 1;
  147. if (e->node == n)
  148. e = agopp(e);
  149. if (!getval(e->node))
  150. nodecnt = label(e->node, nodecnt, edgecnt);
  151. }
  152. return nodecnt;
  153. }
  154. static int
  155. countComponents(Agraph_t * g, int *max_degree, float *nontree_frac)
  156. {
  157. int nc = 0;
  158. int sum_edges = 0;
  159. int sum_nontree = 0;
  160. int deg;
  161. int n_edges;
  162. int n_nodes;
  163. Agnode_t *n;
  164. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  165. if (!getval(n)) {
  166. nc++;
  167. n_edges = 0;
  168. n_nodes = label(n, 0, &n_edges);
  169. sum_edges += n_edges;
  170. sum_nontree += n_edges - n_nodes + 1;
  171. }
  172. }
  173. if (max_degree) {
  174. int maxd = 0;
  175. for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
  176. deg = agdegree(g, n, 1, 1);
  177. if (maxd < deg)
  178. maxd = deg;
  179. setval(n, 0);
  180. }
  181. *max_degree = maxd;
  182. }
  183. if (nontree_frac) {
  184. if (sum_edges > 0)
  185. *nontree_frac = (float) sum_nontree / (float) sum_edges;
  186. else
  187. *nontree_frac = 0.0;
  188. }
  189. return nc;
  190. }
  191. static void process(Agraph_t * G)
  192. {
  193. Agnode_t *n;
  194. Agraph_t *map;
  195. int nc = 0;
  196. float nontree_frac = 0;
  197. int Maxdegree = 0;
  198. node_stack_t stack = {0};
  199. sccstate state;
  200. aginit(G, AGRAPH, "scc_graph", sizeof(Agraphinfo_t), true);
  201. aginit(G, AGNODE, "scc_node", sizeof(Agnodeinfo_t), true);
  202. state.Comp = state.ID = 0;
  203. state.N_nodes_in_nontriv_SCC = 0;
  204. if (Verbose)
  205. nc = countComponents(G, &Maxdegree, &nontree_frac);
  206. map = agopen("scc_map", Agdirected, (Agdisc_t *) 0);
  207. for (n = agfstnode(G); n; n = agnxtnode(G, n))
  208. if (getval(n) == 0)
  209. visit(n, map, &stack, &state);
  210. node_stack_free(&stack);
  211. if (!StatsOnly)
  212. agwrite(map, outfp);
  213. agclose(map);
  214. if (Verbose)
  215. fprintf(stderr, "%d %d %d %u %.4f %d %.4f\n",
  216. agnnodes(G), agnedges(G), nc, state.Comp,
  217. state.N_nodes_in_nontriv_SCC / (double) agnnodes(G),
  218. Maxdegree, nontree_frac);
  219. else if (!Silent)
  220. fprintf(stderr, "%d nodes, %d edges, %u strong components\n",
  221. agnnodes(G), agnedges(G), state.Comp);
  222. }
  223. static char *useString = "Usage: %s [-sdv?] <files>\n\
  224. -s - only produce statistics\n\
  225. -S - silent\n\
  226. -d - allow degenerate components\n\
  227. -o<outfile> - write to <outfile> (stdout)\n\
  228. -v - verbose\n\
  229. -? - print usage\n\
  230. If no files are specified, stdin is used\n";
  231. static void usage(int v)
  232. {
  233. printf(useString, CmdName);
  234. graphviz_exit(v);
  235. }
  236. static void scanArgs(int argc, char **argv)
  237. {
  238. int c;
  239. CmdName = argv[0];
  240. opterr = 0;
  241. while ((c = getopt(argc, argv, ":o:sdvS?")) != EOF) {
  242. switch (c) {
  243. case 's':
  244. StatsOnly = 1;
  245. break;
  246. case 'd':
  247. wantDegenerateComp = 1;
  248. break;
  249. case 'o':
  250. if (outfp != NULL)
  251. fclose(outfp);
  252. outfp = openFile(CmdName, optarg, "w");
  253. break;
  254. case 'v':
  255. Verbose = 1;
  256. Silent = 0;
  257. break;
  258. case 'S':
  259. Verbose = 0;
  260. Silent = 1;
  261. break;
  262. case ':':
  263. fprintf(stderr, "%s: option -%c missing argument - ignored\n", CmdName, optopt);
  264. break;
  265. case '?':
  266. if (optopt == '\0' || optopt == '?')
  267. usage(0);
  268. else {
  269. fprintf(stderr, "%s: option -%c unrecognized\n",
  270. CmdName, optopt);
  271. usage(1);
  272. }
  273. break;
  274. default:
  275. UNREACHABLE();
  276. }
  277. }
  278. argv += optind;
  279. argc -= optind;
  280. if (argc > 0)
  281. Files = argv;
  282. if (!outfp)
  283. outfp = stdout; /* stdout the default */
  284. }
  285. int main(int argc, char **argv)
  286. {
  287. Agraph_t *g;
  288. ingraph_state ig;
  289. scanArgs(argc, argv);
  290. newIngraph(&ig, Files);
  291. while ((g = nextGraph(&ig)) != 0) {
  292. if (agisdirected(g))
  293. process(g);
  294. else
  295. fprintf(stderr, "Graph %s in %s is undirected - ignored\n",
  296. agnameof(g), fileName(&ig));
  297. agclose(g);
  298. }
  299. graphviz_exit(0);
  300. }