Graphviz 14.1.3~dev.20260228.0625
Loading...
Searching...
No Matches
tred.c
Go to the documentation of this file.
1
12/*
13 * Copyright (c) 2011 AT&T Intellectual Property
14 * All rights reserved. This program and the accompanying materials
15 * are made available under the terms of the Eclipse Public License v1.0
16 * which accompanies this distribution, and is available at
17 * https://www.eclipse.org/legal/epl-v10.html
18 *
19 * Authors: Stephen North, Emden Gansner
20 * Contributors: Details at https://graphviz.org
21 */
22
23#include "config.h"
24
25#include <cgraph/cghdr.h>
26#include <cgraph/node_set.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32#include <util/alloc.h>
33#include <util/list.h>
34
35typedef struct {
36 bool on_stack : 1;
37 unsigned char dist;
39
40#define ON_STACK(ninfo, n) (ninfo[AGSEQ(n)].on_stack)
41#define DISTANCE(ninfo, n) (ninfo[AGSEQ(n)].dist)
42#define agrootof(n) ((n)->root)
43
44static unsigned char uchar_min(unsigned char a, unsigned char b) {
45 if (a < b)
46 return a;
47 return b;
48}
49
50typedef LIST(Agedge_t *) edge_stack_t;
51
52static void push(edge_stack_t *sp, Agedge_t *ep, nodeinfo_t *ninfo) {
53
54 // mark this edge on the stack
55 ON_STACK(ninfo, aghead(ep)) = true;
56
57 // insert the new edge
58 LIST_PUSH_BACK(sp, ep);
59}
60
61static Agedge_t *pop(edge_stack_t *sp, nodeinfo_t *ninfo) {
62
63 if (LIST_IS_EMPTY(sp)) {
64 return NULL;
65 }
66
67 // remove the top
68 Agedge_t *e = LIST_POP_BACK(sp);
69
70 // mark it as no longer on the stack
71 ON_STACK(ninfo, aghead(e)) = false;
72
73 return e;
74}
75
76static Agedge_t *top(edge_stack_t *sp) {
77
78 if (LIST_IS_EMPTY(sp)) {
79 return NULL;
80 }
81
82 return *LIST_BACK(sp);
83}
84
85/* Main function for transitive reduction.
86 * This does a DFS starting at node n. Each node records the length of
87 * its largest simple path from n. We only care if the length is > 1. Node
88 * n will have distance 0; outneighbors of n will have distance 1 or 2; all
89 * others will have distance 2.
90 *
91 * During the DFS, we only push edges on the stack whose head has distance 0
92 * (i.e., hasn't been visited yet), setting its distance to the distance of the
93 * tail node plus one. If we find a head node with distance 1, we don't push the
94 * edge, since it has already been in a DFS, but we update its distance. We also
95 * check for back edges and report these.
96 *
97 * After the DFS, we check all outedges of n. Those edges whose head has
98 * distance 2 we delete. We also delete all but one copy of any edges with the
99 * same head.
100 */
101static int dfs(Agnode_t *n, nodeinfo_t *ninfo, int warn,
103 Agraph_t *g = agrootof(n);
104 Agedgepair_t dummy;
105 Agedge_t *link;
106 Agedge_t *next;
107 Agedge_t *prev;
108 Agedge_t *e;
109 Agedge_t *f;
110 Agnode_t *v;
111 Agnode_t *hd;
112 Agnode_t *oldhd;
113 int do_delete;
114
115 dummy.out.base.tag.objtype = AGOUTEDGE;
116 dummy.out.node = n;
117 dummy.in.base.tag.objtype = AGINEDGE;
118 dummy.in.node = NULL;
119
120 edge_stack_t estk = {0};
121 push(&estk, &dummy.out, ninfo);
122 prev = 0;
123
124 while ((link = top(&estk))) {
125 v = aghead(link);
126 if (prev)
127 next = agnxtout(g, prev);
128 else
129 next = agfstout(g, v);
130 for (; next; next = agnxtout(g, next)) {
131 hd = aghead(next);
132 if (hd == v)
133 continue; // Skip a loop
134 if (ON_STACK(ninfo, hd)) {
135 if (!warn) {
136 warn++;
137 if (opts->err != NULL) {
138 fprintf(
139 opts->err,
140 "warning: %s has cycle(s), transitive reduction not unique\n",
141 agnameof(g));
142 fprintf(opts->err, "cycle involves edge %s -> %s\n", agnameof(v),
143 agnameof(hd));
144 }
145 }
146 } else if (DISTANCE(ninfo, hd) == 0) {
147 DISTANCE(ninfo, hd) = uchar_min(1, DISTANCE(ninfo, v)) + 1;
148 break;
149 } else if (DISTANCE(ninfo, hd) == 1) {
150 DISTANCE(ninfo, hd) = uchar_min(1, DISTANCE(ninfo, v)) + 1;
151 }
152 }
153 if (next) {
154 push(&estk, next, ninfo);
155 prev = 0;
156 } else {
157 prev = pop(&estk, ninfo);
158 }
159 }
160 oldhd = NULL;
161 for (e = agfstout(g, n); e; e = f) {
162 do_delete = 0;
163 f = agnxtout(g, e);
164 hd = aghead(e);
165 if (oldhd == hd)
166 do_delete = 1;
167 else {
168 oldhd = hd;
169 if (DISTANCE(ninfo, hd) > 1)
170 do_delete = 1;
171 }
172 if (do_delete) {
173 if (opts->PrintRemovedEdges && opts->err != NULL)
174 fprintf(opts->err, "removed edge: %s: \"%s\" -> \"%s\"\n", agnameof(g),
175 agnameof(aghead(e)), agnameof(agtail(e)));
176 agdelete(g, e);
177 }
178 }
179 LIST_FREE(&estk);
180 return warn;
181}
182
183/* Do a DFS for each vertex in graph g, so the time
184 * complexity is O(|V||E|).
185 */
187 int cnt = 0;
188 int warn = 0;
189 time_t total_secs = 0;
190
191 const size_t infosize = (node_set_size(g->n_id) + 1) * sizeof(nodeinfo_t);
192 nodeinfo_t *const ninfo = gv_alloc(infosize);
193
194 if (opts->Verbose && opts->err != NULL)
195 fprintf(stderr, "Processing graph %s\n", agnameof(g));
196 for (Agnode_t *n = agfstnode(g); n; n = agnxtnode(g, n)) {
197 memset(ninfo, 0, infosize);
198 const time_t start = time(NULL);
199 warn = dfs(n, ninfo, warn, opts);
200 if (opts->Verbose) {
201 const time_t secs = time(NULL) - start;
202 total_secs += secs;
203 cnt++;
204 if (cnt % 1000 == 0 && opts->err != NULL) {
205 fprintf(opts->err, "[%d]\n", cnt);
206 }
207 }
208 }
209 if (opts->Verbose && opts->err != NULL)
210 fprintf(opts->err, "Finished graph %s: %lld.00 secs.\n", agnameof(g),
211 (long long)total_secs);
212 free(ninfo);
213 agwrite(g, opts->out);
214 fflush(opts->out);
215}
Memory allocation wrappers that exit on failure.
static void * gv_alloc(size_t size)
Definition alloc.h:47
cgraph.h additions
size_t node_set_size(const node_set_t *self)
Definition node.c:546
static Agnode_t * pop(void)
Definition ccomps.c:222
void free(void *)
static gstack_t * push(gstack_t *s, Agraph_t *subg)
Definition grammar.c:1655
node NULL
Definition grammar.y:181
static int cnt(Dict_t *d, Dtlink_t **set)
Definition graph.c:198
void graphviz_tred(Agraph_t *g, const graphviz_tred_options_t *opts)
programmatic access to tred - transitive reduction
Definition tred.c:186
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:977
#define aghead(e)
Definition cgraph.h:978
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
int agwrite(Agraph_t *g, void *chan)
Return 0 on success, EOF on failure.
Definition write.c:669
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:22
@ AGOUTEDGE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
static opts_t opts
Definition gvgen.c:415
$2 prev
Definition htmlparse.y:291
static int dfs(Agnode_t *n, nodeinfo_t *ninfo, int warn, const graphviz_tred_options_t *opts)
Definition tred.c:101
static unsigned char uchar_min(unsigned char a, unsigned char b)
Definition tred.c:44
#define ON_STACK(ninfo, n)
Definition tred.c:40
#define agrootof(n)
Definition tred.c:42
#define DISTANCE(ninfo, n)
Definition tred.c:41
static Agedge_t * top(edge_stack_t *sp)
Definition tred.c:76
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_BACK(list)
Definition list.h:191
#define LIST_FREE(list)
Definition list.h:370
#define LIST_POP_BACK(list)
Definition list.h:407
#define LIST_IS_EMPTY(list)
Definition list.h:90
#define LIST_PUSH_BACK(list, item)
Definition list.h:384
unordered set of Agsubnode_t *
Agnode_t * node
Definition cgraph.h:272
Agobj_t base
Definition cgraph.h:269
Agedge_t in
Definition cgraph.h:276
Agedge_t out
Definition cgraph.h:276
Agtag_t tag
access with AGTAG
Definition cgraph.h:211
graph or subgraph
Definition cgraph.h:424
struct graphviz_node_set * n_id
the node set indexed by ID
Definition cgraph.h:430
unsigned objtype
access with AGTYPE
Definition cgraph.h:199
options for passing to graphviz_tred
Definition cgraph.h:1031
unsigned char dist
Definition tred.c:37
bool on_stack
Definition tred.c:36
int Verbose
Definition gvgen.c:45