Graphviz 14.1.2~dev.20260118.1035
Loading...
Searching...
No Matches
sccmap.c
Go to the documentation of this file.
1
6/*************************************************************************
7 * Copyright (c) 2011 AT&T Intellectual Property
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/legal/epl-v10.html
12 *
13 * Contributors: Details at https://graphviz.org
14 *************************************************************************/
15
16
17/*
18 * Written by Stephen North
19 * Updated by Emden Gansner
20 */
21
22/*
23 * This is a filter that reads a graph, searches for strongly
24 * connected components, and writes each as a separate graph
25 * along with a map of the components.
26 */
27
28#include "config.h"
29
30#include <limits.h>
31#include <stdbool.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <cgraph/cgraph.h>
35#include <cgraph/ingraphs.h>
36
37#include <getopt.h>
38#include "openFile.h"
39#include <util/exit.h>
40#include <util/list.h>
41#include <util/unreachable.h>
42
43#define INF UINT_MAX
44
45typedef struct Agraphinfo_t {
46 Agrec_t h;
49
50typedef struct Agnodeinfo_t {
51 Agrec_t h;
52 unsigned int val;
55
57{
58 return ((Agraphinfo_t *)g->base.data)->rep;
59}
60static void setrep(Agraph_t * g, Agnode_t * rep)
61{
62 ((Agraphinfo_t *)g->base.data)->rep = rep;
63}
65{
66 return ((Agnodeinfo_t *)n->base.data)->scc;
67}
68static void setscc(Agnode_t * n, Agraph_t * scc)
69{
70 ((Agnodeinfo_t *)n->base.data)->scc = scc;
71}
72static unsigned getval(Agnode_t *n) {
73 return ((Agnodeinfo_t *)n->base.data)->val;
74}
75static void setval(Agnode_t *n, unsigned v) {
76 ((Agnodeinfo_t *)n->base.data)->val = v;
77}
78
79typedef struct {
80 unsigned Comp;
81 unsigned ID;
83} sccstate;
84
86static int Silent;
87static int StatsOnly;
88static int Verbose;
89static char *CmdName;
90static char **Files;
91static FILE *outfp; /* output; stdout by default */
92
93static void nodeInduce(Agraph_t * g, Agraph_t* map)
94{
95 Agnode_t *n;
96 Agedge_t *e;
97 Agraph_t* rootg = agroot (g);
98
99
100 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
101 for (e = agfstout(rootg, n); e; e = agnxtout(rootg, e)) {
102 if (agsubnode(g, aghead(e), 0))
103 agsubedge(g, e, 1);
104 else {
105 Agraph_t* tscc = getscc(agtail(e));
106 Agraph_t* hscc = getscc(aghead(e));
107 if (tscc && hscc)
108 agedge(map, getrep(tscc), getrep(hscc), NULL, 1);
109 }
110 }
111 }
112}
113
114typedef LIST(Agnode_t *) node_stack_t;
115
116static unsigned visit(Agnode_t *n, Agraph_t *map, node_stack_t *sp,
117 sccstate *st) {
118 unsigned int m, min;
119 Agnode_t *t;
120 Agraph_t *subg;
121 Agedge_t *e;
122
123 min = ++st->ID;
124 setval(n, min);
125 LIST_PUSH_BACK(sp, n);
126
127 for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e)) {
128 t = aghead(e);
129 if (getval(t) == 0)
130 m = visit(t, map, sp, st);
131 else
132 m = getval(t);
133 if (m < min)
134 min = m;
135 }
136
137 if (getval(n) == min) {
138 if (!wantDegenerateComp && *LIST_BACK(sp) == n) {
139 setval(n, INF);
140 LIST_DROP_BACK(sp);
141 } else {
142 char name[32];
143 Agraph_t *G = agraphof(n);;
144 snprintf(name, sizeof(name), "cluster_%u", st->Comp++);
145 subg = agsubg(G, name, 1);
146 agbindrec(subg, "scc_graph", sizeof(Agraphinfo_t), true);
147 setrep(subg, agnode(map, name, 1));
148 do {
149 t = LIST_POP_BACK(sp);
150 agsubnode(subg, t, 1);
151 setval(t, INF);
152 setscc(t, subg);
154 } while (t != n);
155 nodeInduce(subg, map);
156 if (!StatsOnly)
157 agwrite(subg, outfp);
158 }
159 }
160 return min;
161}
162
163static int label(Agnode_t * n, int nodecnt, int *edgecnt)
164{
165 Agedge_t *e;
166
167 setval(n, 1);
168 nodecnt++;
169 for (e = agfstedge(n->root, n); e; e = agnxtedge(n->root, e, n)) {
170 *edgecnt += 1;
171 if (e->node == n)
172 e = agopp(e);
173 if (!getval(e->node))
174 nodecnt = label(e->node, nodecnt, edgecnt);
175 }
176 return nodecnt;
177}
178
179static int
180countComponents(Agraph_t * g, int *max_degree, float *nontree_frac)
181{
182 int nc = 0;
183 int sum_edges = 0;
184 int sum_nontree = 0;
185 int deg;
186 int n_edges;
187 int n_nodes;
188 Agnode_t *n;
189
190 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
191 if (!getval(n)) {
192 nc++;
193 n_edges = 0;
194 n_nodes = label(n, 0, &n_edges);
195 sum_edges += n_edges;
196 sum_nontree += n_edges - n_nodes + 1;
197 }
198 }
199 if (max_degree) {
200 int maxd = 0;
201 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
202 deg = agdegree(g, n, 1, 1);
203 if (maxd < deg)
204 maxd = deg;
205 setval(n, 0);
206 }
207 *max_degree = maxd;
208 }
209 if (nontree_frac) {
210 if (sum_edges > 0)
211 *nontree_frac = (float) sum_nontree / (float) sum_edges;
212 else
213 *nontree_frac = 0.0;
214 }
215 return nc;
216}
217
218static void process(Agraph_t * G)
219{
220 Agnode_t *n;
221 Agraph_t *map;
222 int nc = 0;
223 float nontree_frac = 0;
224 int Maxdegree = 0;
225 node_stack_t stack = {0};
226 sccstate state;
227
228 aginit(G, AGRAPH, "scc_graph", sizeof(Agraphinfo_t), true);
229 aginit(G, AGNODE, "scc_node", sizeof(Agnodeinfo_t), true);
230 state.Comp = state.ID = 0;
231 state.N_nodes_in_nontriv_SCC = 0;
232
233 if (Verbose)
234 nc = countComponents(G, &Maxdegree, &nontree_frac);
235
236 map = agopen("scc_map", Agdirected, NULL);
237 for (n = agfstnode(G); n; n = agnxtnode(G, n))
238 if (getval(n) == 0)
239 visit(n, map, &stack, &state);
240 LIST_FREE(&stack);
241 if (!StatsOnly)
242 agwrite(map, outfp);
243 agclose(map);
244
245 if (Verbose)
246 fprintf(stderr, "%d %d %d %u %.4f %d %.4f\n",
247 agnnodes(G), agnedges(G), nc, state.Comp,
248 (double)state.N_nodes_in_nontriv_SCC / agnnodes(G),
249 Maxdegree, nontree_frac);
250 else if (!Silent)
251 fprintf(stderr, "%d nodes, %d edges, %u strong components\n",
252 agnnodes(G), agnedges(G), state.Comp);
253
254}
255
256static char *useString = "Usage: %s [-sdv?] <files>\n\
257 -s - only produce statistics\n\
258 -S - silent\n\
259 -d - allow degenerate components\n\
260 -o<outfile> - write to <outfile> (stdout)\n\
261 -v - verbose\n\
262 -? - print usage\n\
263If no files are specified, stdin is used\n";
264
265static void usage(int v)
266{
267 printf(useString, CmdName);
268 graphviz_exit(v);
269}
270
271static void scanArgs(int argc, char **argv)
272{
273 int c;
274
275 CmdName = argv[0];
276 opterr = 0;
277 while ((c = getopt(argc, argv, ":o:sdvS?")) != EOF) {
278 switch (c) {
279 case 's':
280 StatsOnly = 1;
281 break;
282 case 'd':
284 break;
285 case 'o':
286 if (outfp != NULL)
287 fclose(outfp);
288 outfp = openFile(CmdName, optarg, "w");
289 break;
290 case 'v':
291 Verbose = 1;
292 Silent = 0;
293 break;
294 case 'S':
295 Verbose = 0;
296 Silent = 1;
297 break;
298 case ':':
299 fprintf(stderr, "%s: option -%c missing argument - ignored\n", CmdName, optopt);
300 break;
301 case '?':
302 if (optopt == '\0' || optopt == '?')
303 usage(0);
304 else {
305 fprintf(stderr, "%s: option -%c unrecognized\n",
306 CmdName, optopt);
307 usage(1);
308 }
309 break;
310 default:
311 UNREACHABLE();
312 }
313 }
314 argv += optind;
315 argc -= optind;
316
317 if (argc > 0)
318 Files = argv;
319 if (!outfp)
320 outfp = stdout; /* stdout the default */
321}
322
323int main(int argc, char **argv)
324{
325 Agraph_t *g;
326 ingraph_state ig;
327
328 scanArgs(argc, argv);
329 newIngraph(&ig, Files);
330
331 while ((g = nextGraph(&ig)) != 0) {
332 if (agisdirected(g))
333 process(g);
334 else
335 fprintf(stderr, "Graph %s in %s is undirected - ignored\n",
336 agnameof(g), fileName(&ig));
337 agclose(g);
338 }
339
340 graphviz_exit(0);
341}
abstract graph C library, Cgraph API
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
#define G
Definition gdefs.h:7
node NULL
Definition grammar.y:181
int agnedges(Agraph_t *g)
Definition graph.c:163
int agdegree(Agraph_t *g, Agnode_t *n, int in, int out)
Definition graph.c:225
int agnnodes(Agraph_t *g)
Definition graph.c:157
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:255
#define agopp(e)
opposite edge: flip Agedgepair_s.out ⇄ Agedgepair_s.in/*#end#*‍/
Definition cgraph.h:979
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:350
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:977
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:98
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:89
int agisdirected(Agraph_t *g)
Definition graph.c:178
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:669
Agdesc_t Agdirected
directed
Definition graph.c:272
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:254
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGNODE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
void aginit(Agraph_t *g, int kind, const char *rec_name, int rec_size, int move_to_front)
attach new records to objects of specified kind
Definition rec.c:172
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:55
static const char * usage
Definition gvpr.c:54
char * fileName(ingraph_state *sp)
Return name of current file being processed.
Definition ingraphs.c:156
Agraph_t * nextGraph(ingraph_state *sp)
Definition ingraphs.c:61
ingraph_state * newIngraph(ingraph_state *sp, char **files)
Definition ingraphs.c:140
supports user-supplied data
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_BACK(list)
Definition list.h:191
#define LIST_DROP_BACK(list)
Definition list.h:422
#define LIST_FREE(list)
Definition list.h:370
#define LIST_POP_BACK(list)
Definition list.h:407
#define LIST_PUSH_BACK(list, item)
Definition list.h:384
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
static void setrep(Agraph_t *g, Agnode_t *rep)
Definition sccmap.c:60
#define INF
Definition sccmap.c:43
static void setscc(Agnode_t *n, Agraph_t *scc)
Definition sccmap.c:68
static void setval(Agnode_t *n, unsigned v)
Definition sccmap.c:75
static void scanArgs(int argc, char **argv)
Definition sccmap.c:271
static void process(Agraph_t *G)
Definition sccmap.c:218
static int Silent
Definition sccmap.c:86
static int StatsOnly
Definition sccmap.c:87
static unsigned getval(Agnode_t *n)
Definition sccmap.c:72
static int countComponents(Agraph_t *g, int *max_degree, float *nontree_frac)
Definition sccmap.c:180
static FILE * outfp
Definition sccmap.c:91
static int Verbose
Definition sccmap.c:88
struct Agnodeinfo_t Agnodeinfo_t
static int wantDegenerateComp
Definition sccmap.c:85
static int label(Agnode_t *n, int nodecnt, int *edgecnt)
Definition sccmap.c:163
static Agraph_t * getscc(Agnode_t *n)
Definition sccmap.c:64
static char * useString
Definition sccmap.c:256
struct Agraphinfo_t Agraphinfo_t
static Agnode_t * getrep(Agraph_t *g)
Definition sccmap.c:56
static char ** Files
Definition sccmap.c:90
static void nodeInduce(Agraph_t *g, Agraph_t *map)
Definition sccmap.c:93
static char * CmdName
Definition sccmap.c:89
Agnode_t * node
Definition cgraph.h:272
Agraph_t * root
Definition cgraph.h:261
Agobj_t base
Definition cgraph.h:260
Agraph_t * scc
Definition sccmap.c:53
Agrec_t h
Definition bcomps.c:50
unsigned int val
Definition sccmap.c:52
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:424
Agobj_t base
Definition cgraph.h:425
Agrec_t h
Definition bcomps.c:41
Agnode_t * rep
Definition sccmap.c:47
implementation of Agrec_t
Definition cgraph.h:172
unsigned Comp
Definition sccmap.c:80
int N_nodes_in_nontriv_SCC
Definition sccmap.c:82
unsigned ID
Definition sccmap.c:81
int main()
#define UNREACHABLE()
Definition unreachable.h:30