Graphviz 12.0.1~dev.20240715.2254
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 <limits.h>
29#include <stdbool.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <cgraph/cgraph.h>
33#include <cgraph/exit.h>
34#include <cgraph/ingraphs.h>
35#include <cgraph/stack.h>
36#include <cgraph/unreachable.h>
37
38#include <getopt.h>
39#include "openFile.h"
40
41#define INF UINT_MAX
42
43typedef struct Agraphinfo_t {
44 Agrec_t h;
47
48typedef struct Agnodeinfo_t {
49 Agrec_t h;
50 unsigned int val;
53
55{
56 return ((Agraphinfo_t *)g->base.data)->rep;
57}
58static void setrep(Agraph_t * g, Agnode_t * rep)
59{
60 ((Agraphinfo_t *)g->base.data)->rep = rep;
61}
63{
64 return ((Agnodeinfo_t *)n->base.data)->scc;
65}
66static void setscc(Agnode_t * n, Agraph_t * scc)
67{
68 ((Agnodeinfo_t *)n->base.data)->scc = scc;
69}
70static unsigned getval(Agnode_t *n) {
71 return ((Agnodeinfo_t *)n->base.data)->val;
72}
73static void setval(Agnode_t *n, unsigned v) {
74 ((Agnodeinfo_t *)n->base.data)->val = v;
75}
76
77typedef struct {
78 unsigned Comp;
79 unsigned ID;
81} sccstate;
82
84static int Silent;
85static int StatsOnly;
86static int Verbose;
87static char *CmdName;
88static char **Files;
89static FILE *outfp; /* output; stdout by default */
90
91static void nodeInduce(Agraph_t * g, Agraph_t* map)
92{
93 Agnode_t *n;
94 Agedge_t *e;
95 Agraph_t* rootg = agroot (g);
96
97
98 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
99 for (e = agfstout(rootg, n); e; e = agnxtout(rootg, e)) {
100 if (agsubnode(g, aghead(e), 0))
101 agsubedge(g, e, 1);
102 else {
103 Agraph_t* tscc = getscc(agtail(e));
104 Agraph_t* hscc = getscc(aghead(e));
105 if (tscc && hscc)
106 agedge(map, getrep(tscc), getrep(hscc), NULL, 1);
107 }
108 }
109 }
110}
111
112static unsigned visit(Agnode_t *n, Agraph_t *map, gv_stack_t *sp, sccstate *st) {
113 unsigned int m, min;
114 Agnode_t *t;
115 Agraph_t *subg;
116 Agedge_t *e;
117
118 min = ++st->ID;
119 setval(n, min);
120 stack_push(sp, n);
121
122 for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e)) {
123 t = aghead(e);
124 if (getval(t) == 0)
125 m = visit(t, map, sp, st);
126 else
127 m = getval(t);
128 if (m < min)
129 min = m;
130 }
131
132 if (getval(n) == min) {
133 if (!wantDegenerateComp && stack_top(sp) == n) {
134 setval(n, INF);
135 stack_pop(sp);
136 } else {
137 char name[32];
138 Agraph_t *G = agraphof(n);;
139 snprintf(name, sizeof(name), "cluster_%u", st->Comp++);
140 subg = agsubg(G, name, 1);
141 agbindrec(subg, "scc_graph", sizeof(Agraphinfo_t), true);
142 setrep(subg, agnode(map, name, 1));
143 do {
144 t = stack_pop(sp);
145 agsubnode(subg, t, 1);
146 setval(t, INF);
147 setscc(t, subg);
149 } while (t != n);
150 nodeInduce(subg, map);
151 if (!StatsOnly)
152 agwrite(subg, outfp);
153 }
154 }
155 return min;
156}
157
158static int label(Agnode_t * n, int nodecnt, int *edgecnt)
159{
160 Agedge_t *e;
161
162 setval(n, 1);
163 nodecnt++;
164 for (e = agfstedge(n->root, n); e; e = agnxtedge(n->root, e, n)) {
165 *edgecnt += 1;
166 if (e->node == n)
167 e = agopp(e);
168 if (!getval(e->node))
169 nodecnt = label(e->node, nodecnt, edgecnt);
170 }
171 return nodecnt;
172}
173
174static int
175countComponents(Agraph_t * g, int *max_degree, float *nontree_frac)
176{
177 int nc = 0;
178 int sum_edges = 0;
179 int sum_nontree = 0;
180 int deg;
181 int n_edges;
182 int n_nodes;
183 Agnode_t *n;
184
185 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
186 if (!getval(n)) {
187 nc++;
188 n_edges = 0;
189 n_nodes = label(n, 0, &n_edges);
190 sum_edges += n_edges;
191 sum_nontree += n_edges - n_nodes + 1;
192 }
193 }
194 if (max_degree) {
195 int maxd = 0;
196 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
197 deg = agdegree(g, n, 1, 1);
198 if (maxd < deg)
199 maxd = deg;
200 setval(n, 0);
201 }
202 *max_degree = maxd;
203 }
204 if (nontree_frac) {
205 if (sum_edges > 0)
206 *nontree_frac = (float) sum_nontree / (float) sum_edges;
207 else
208 *nontree_frac = 0.0;
209 }
210 return nc;
211}
212
213static void process(Agraph_t * G)
214{
215 Agnode_t *n;
216 Agraph_t *map;
217 int nc = 0;
218 float nontree_frac = 0;
219 int Maxdegree = 0;
220 gv_stack_t stack = {0};
222
223 aginit(G, AGRAPH, "scc_graph", sizeof(Agraphinfo_t), true);
224 aginit(G, AGNODE, "scc_node", sizeof(Agnodeinfo_t), true);
225 state.Comp = state.ID = 0;
226 state.N_nodes_in_nontriv_SCC = 0;
227
228 if (Verbose)
229 nc = countComponents(G, &Maxdegree, &nontree_frac);
230
231 map = agopen("scc_map", Agdirected, (Agdisc_t *) 0);
232 for (n = agfstnode(G); n; n = agnxtnode(G, n))
233 if (getval(n) == 0)
234 visit(n, map, &stack, &state);
235 stack_reset(&stack);
236 if (!StatsOnly)
237 agwrite(map, outfp);
238 agclose(map);
239
240 if (Verbose)
241 fprintf(stderr, "%d %d %d %u %.4f %d %.4f\n",
242 agnnodes(G), agnedges(G), nc, state.Comp,
243 state.N_nodes_in_nontriv_SCC / (double) agnnodes(G),
244 Maxdegree, nontree_frac);
245 else if (!Silent)
246 fprintf(stderr, "%d nodes, %d edges, %u strong components\n",
247 agnnodes(G), agnedges(G), state.Comp);
248
249}
250
251static char *useString = "Usage: %s [-sdv?] <files>\n\
252 -s - only produce statistics\n\
253 -S - silent\n\
254 -d - allow degenerate components\n\
255 -o<outfile> - write to <outfile> (stdout)\n\
256 -v - verbose\n\
257 -? - print usage\n\
258If no files are specified, stdin is used\n";
259
260static void usage(int v)
261{
262 printf(useString, CmdName);
263 graphviz_exit(v);
264}
265
266static void scanArgs(int argc, char **argv)
267{
268 int c;
269
270 CmdName = argv[0];
271 opterr = 0;
272 while ((c = getopt(argc, argv, ":o:sdvS?")) != EOF) {
273 switch (c) {
274 case 's':
275 StatsOnly = 1;
276 break;
277 case 'd':
279 break;
280 case 'o':
281 if (outfp != NULL)
282 fclose(outfp);
283 outfp = openFile(CmdName, optarg, "w");
284 break;
285 case 'v':
286 Verbose = 1;
287 Silent = 0;
288 break;
289 case 'S':
290 Verbose = 0;
291 Silent = 1;
292 break;
293 case ':':
294 fprintf(stderr, "%s: option -%c missing argument - ignored\n", CmdName, optopt);
295 break;
296 case '?':
297 if (optopt == '\0' || optopt == '?')
298 usage(0);
299 else {
300 fprintf(stderr, "%s: option -%c unrecognized\n",
301 CmdName, optopt);
302 usage(1);
303 }
304 break;
305 default:
306 UNREACHABLE();
307 }
308 }
309 argv += optind;
310 argc -= optind;
311
312 if (argc > 0)
313 Files = argv;
314 if (!outfp)
315 outfp = stdout; /* stdout the default */
316}
317
318int main(int argc, char **argv)
319{
320 Agraph_t *g;
321 ingraph_state ig;
322
323 scanArgs(argc, argv);
324 newIngraph(&ig, Files);
325
326 while ((g = nextGraph(&ig)) != 0) {
327 if (agisdirected(g))
328 process(g);
329 else
330 fprintf(stderr, "Graph %s in %s is undirected - ignored\n",
331 agnameof(g), fileName(&ig));
332 agclose(g);
333 }
334
335 graphviz_exit(0);
336}
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:149
int agnedges(Agraph_t *g)
Definition graph.c:164
int agdegree(Agraph_t *g, Agnode_t *n, int in, int out)
Definition graph.c:226
int agnnodes(Agraph_t *g)
Definition graph.c:158
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:260
#define agopp(e)
opposite edge: flip Agedgepair_s.out ⇄ Agedgepair_s.in/*#end#*‍/
Definition cgraph.h:891
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int createflag)
Definition edge.c:355
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define agtail(e)
Definition cgraph.h:889
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:93
#define aghead(e)
Definition cgraph.h:890
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:38
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:84
int agisdirected(Agraph_t *g)
Definition graph.c:179
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:96
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:708
Agdesc_t Agdirected
directed
Definition graph.c:273
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:147
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:261
Agraph_t * agraphof(void *obj)
Definition obj.c:184
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
Agraph_t * agroot(void *obj)
Definition obj.c:167
@ 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:169
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:88
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:57
static const char * usage
Definition gvpr.c:53
static lexstate_t state
Definition htmllex.c:61
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
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:58
#define INF
Definition sccmap.c:41
static void setscc(Agnode_t *n, Agraph_t *scc)
Definition sccmap.c:66
static void setval(Agnode_t *n, unsigned v)
Definition sccmap.c:73
static void scanArgs(int argc, char **argv)
Definition sccmap.c:266
static void process(Agraph_t *G)
Definition sccmap.c:213
static int Silent
Definition sccmap.c:84
static unsigned visit(Agnode_t *n, Agraph_t *map, gv_stack_t *sp, sccstate *st)
Definition sccmap.c:112
static int StatsOnly
Definition sccmap.c:85
static unsigned getval(Agnode_t *n)
Definition sccmap.c:70
static int countComponents(Agraph_t *g, int *max_degree, float *nontree_frac)
Definition sccmap.c:175
static FILE * outfp
Definition sccmap.c:89
static int Verbose
Definition sccmap.c:86
struct Agnodeinfo_t Agnodeinfo_t
static int wantDegenerateComp
Definition sccmap.c:83
static int label(Agnode_t *n, int nodecnt, int *edgecnt)
Definition sccmap.c:158
static Agraph_t * getscc(Agnode_t *n)
Definition sccmap.c:62
static char * useString
Definition sccmap.c:251
struct Agraphinfo_t Agraphinfo_t
static Agnode_t * getrep(Agraph_t *g)
Definition sccmap.c:54
static char ** Files
Definition sccmap.c:88
static void nodeInduce(Agraph_t *g, Agraph_t *map)
Definition sccmap.c:91
static char * CmdName
Definition sccmap.c:87
Implementation of a dynamically expanding stack data structure.
static void stack_push(gv_stack_t *stack, void *item)
Definition stack.h:21
static void * stack_pop(gv_stack_t *stack)
Definition stack.h:33
static void * stack_top(gv_stack_t *stack)
Definition stack.h:25
static void stack_reset(gv_stack_t *stack)
Definition stack.h:35
user's discipline
Definition cgraph.h:337
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:51
Agrec_t h
Definition bcomps.c:50
unsigned int val
Definition sccmap.c:50
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:425
Agobj_t base
Definition cgraph.h:426
Agrec_t h
Definition bcomps.c:41
Agnode_t * rep
Definition sccmap.c:45
implementation of Agrec_t
Definition cgraph.h:172
unsigned Comp
Definition sccmap.c:78
int N_nodes_in_nontriv_SCC
Definition sccmap.c:80
unsigned ID
Definition sccmap.c:79
int main()
#define UNREACHABLE()
Definition unreachable.h:30