Graphviz 13.0.0~dev.20241220.2304
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/ingraphs.h>
34#include <cgraph/list.h>
35
36#include <getopt.h>
37#include "openFile.h"
38#include <util/exit.h>
39#include <util/unreachable.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
112DEFINE_LIST(node_stack, Agnode_t *)
113
114static unsigned visit(Agnode_t *n, Agraph_t *map, node_stack_t *sp,
115 sccstate *st) {
116 unsigned int m, min;
117 Agnode_t *t;
118 Agraph_t *subg;
119 Agedge_t *e;
120
121 min = ++st->ID;
122 setval(n, min);
123 node_stack_push_back(sp, n);
124
125 for (e = agfstout(n->root, n); e; e = agnxtout(n->root, e)) {
126 t = aghead(e);
127 if (getval(t) == 0)
128 m = visit(t, map, sp, st);
129 else
130 m = getval(t);
131 if (m < min)
132 min = m;
133 }
134
135 if (getval(n) == min) {
136 if (!wantDegenerateComp && *node_stack_back(sp) == n) {
137 setval(n, INF);
138 (void)node_stack_pop_back(sp);
139 } else {
140 char name[32];
141 Agraph_t *G = agraphof(n);;
142 snprintf(name, sizeof(name), "cluster_%u", st->Comp++);
143 subg = agsubg(G, name, 1);
144 agbindrec(subg, "scc_graph", sizeof(Agraphinfo_t), true);
145 setrep(subg, agnode(map, name, 1));
146 do {
147 t = node_stack_pop_back(sp);
148 agsubnode(subg, t, 1);
149 setval(t, INF);
150 setscc(t, subg);
151 st->N_nodes_in_nontriv_SCC++;
152 } while (t != n);
153 nodeInduce(subg, map);
154 if (!StatsOnly)
155 agwrite(subg, outfp);
156 }
157 }
158 return min;
159}
160
161static int label(Agnode_t * n, int nodecnt, int *edgecnt)
162{
163 Agedge_t *e;
164
165 setval(n, 1);
166 nodecnt++;
167 for (e = agfstedge(n->root, n); e; e = agnxtedge(n->root, e, n)) {
168 *edgecnt += 1;
169 if (e->node == n)
170 e = agopp(e);
171 if (!getval(e->node))
172 nodecnt = label(e->node, nodecnt, edgecnt);
173 }
174 return nodecnt;
175}
176
177static int
178countComponents(Agraph_t * g, int *max_degree, float *nontree_frac)
179{
180 int nc = 0;
181 int sum_edges = 0;
182 int sum_nontree = 0;
183 int deg;
184 int n_edges;
185 int n_nodes;
186 Agnode_t *n;
187
188 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
189 if (!getval(n)) {
190 nc++;
191 n_edges = 0;
192 n_nodes = label(n, 0, &n_edges);
193 sum_edges += n_edges;
194 sum_nontree += n_edges - n_nodes + 1;
195 }
196 }
197 if (max_degree) {
198 int maxd = 0;
199 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
200 deg = agdegree(g, n, 1, 1);
201 if (maxd < deg)
202 maxd = deg;
203 setval(n, 0);
204 }
205 *max_degree = maxd;
206 }
207 if (nontree_frac) {
208 if (sum_edges > 0)
209 *nontree_frac = (float) sum_nontree / (float) sum_edges;
210 else
211 *nontree_frac = 0.0;
212 }
213 return nc;
214}
215
216static void process(Agraph_t * G)
217{
218 Agnode_t *n;
219 Agraph_t *map;
220 int nc = 0;
221 float nontree_frac = 0;
222 int Maxdegree = 0;
223 node_stack_t stack = {0};
224 sccstate state;
225
226 aginit(G, AGRAPH, "scc_graph", sizeof(Agraphinfo_t), true);
227 aginit(G, AGNODE, "scc_node", sizeof(Agnodeinfo_t), true);
228 state.Comp = state.ID = 0;
229 state.N_nodes_in_nontriv_SCC = 0;
230
231 if (Verbose)
232 nc = countComponents(G, &Maxdegree, &nontree_frac);
233
234 map = agopen("scc_map", Agdirected, (Agdisc_t *) 0);
235 for (n = agfstnode(G); n; n = agnxtnode(G, n))
236 if (getval(n) == 0)
237 visit(n, map, &stack, &state);
238 node_stack_free(&stack);
239 if (!StatsOnly)
240 agwrite(map, outfp);
241 agclose(map);
242
243 if (Verbose)
244 fprintf(stderr, "%d %d %d %u %.4f %d %.4f\n",
245 agnnodes(G), agnedges(G), nc, state.Comp,
246 state.N_nodes_in_nontriv_SCC / (double) agnnodes(G),
247 Maxdegree, nontree_frac);
248 else if (!Silent)
249 fprintf(stderr, "%d nodes, %d edges, %u strong components\n",
250 agnnodes(G), agnedges(G), state.Comp);
251
252}
253
254static char *useString = "Usage: %s [-sdv?] <files>\n\
255 -s - only produce statistics\n\
256 -S - silent\n\
257 -d - allow degenerate components\n\
258 -o<outfile> - write to <outfile> (stdout)\n\
259 -v - verbose\n\
260 -? - print usage\n\
261If no files are specified, stdin is used\n";
262
263static void usage(int v)
264{
265 printf(useString, CmdName);
266 graphviz_exit(v);
267}
268
269static void scanArgs(int argc, char **argv)
270{
271 int c;
272
273 CmdName = argv[0];
274 opterr = 0;
275 while ((c = getopt(argc, argv, ":o:sdvS?")) != EOF) {
276 switch (c) {
277 case 's':
278 StatsOnly = 1;
279 break;
280 case 'd':
282 break;
283 case 'o':
284 if (outfp != NULL)
285 fclose(outfp);
286 outfp = openFile(CmdName, optarg, "w");
287 break;
288 case 'v':
289 Verbose = 1;
290 Silent = 0;
291 break;
292 case 'S':
293 Verbose = 0;
294 Silent = 1;
295 break;
296 case ':':
297 fprintf(stderr, "%s: option -%c missing argument - ignored\n", CmdName, optopt);
298 break;
299 case '?':
300 if (optopt == '\0' || optopt == '?')
301 usage(0);
302 else {
303 fprintf(stderr, "%s: option -%c unrecognized\n",
304 CmdName, optopt);
305 usage(1);
306 }
307 break;
308 default:
309 UNREACHABLE();
310 }
311 }
312 argv += optind;
313 argc -= optind;
314
315 if (argc > 0)
316 Files = argv;
317 if (!outfp)
318 outfp = stdout; /* stdout the default */
319}
320
321int main(int argc, char **argv)
322{
323 Agraph_t *g;
324 ingraph_state ig;
325
326 scanArgs(argc, argv);
327 newIngraph(&ig, Files);
328
329 while ((g = nextGraph(&ig)) != 0) {
330 if (agisdirected(g))
331 process(g);
332 else
333 fprintf(stderr, "Graph %s in %s is undirected - ignored\n",
334 agnameof(g), fileName(&ig));
335 agclose(g);
336 }
337
338 graphviz_exit(0);
339}
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:163
int agnedges(Agraph_t *g)
Definition graph.c:175
int agdegree(Agraph_t *g, Agnode_t *n, int in, int out)
Definition graph.c:237
int agnnodes(Agraph_t *g)
Definition graph.c:169
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:882
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:24
#define agtail(e)
Definition cgraph.h:880
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:94
#define aghead(e)
Definition cgraph.h:881
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:85
int agisdirected(Agraph_t *g)
Definition graph.c:190
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:102
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:730
Agdesc_t Agdirected
directed
Definition graph.c:284
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:145
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:259
Agraph_t * agraphof(void *obj)
Definition obj.c:185
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
Agraph_t * agroot(void *obj)
Definition obj.c:168
@ 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:170
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:89
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:58
static const char * usage
Definition gvpr.c:51
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
#define DEFINE_LIST(name, type)
Definition list.h:26
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:269
static void process(Agraph_t *G)
Definition sccmap.c:216
static int Silent
Definition sccmap.c:84
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:178
static unsigned visit(Agnode_t *n, Agraph_t *map, node_stack_t *sp, sccstate *st)
Definition sccmap.c:114
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:161
static Agraph_t * getscc(Agnode_t *n)
Definition sccmap.c:62
static char * useString
Definition sccmap.c:254
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
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