Graphviz 12.0.1~dev.20240715.2254
Loading...
Searching...
No Matches
cluster.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include <assert.h>
12#include <cgraph/alloc.h>
13#include <cgraph/queue.h>
14#include <dotgen/dot.h>
15#include <stdbool.h>
16#include <stddef.h>
17
18static node_t*
20{
21 node_t *rv;
22
23 if ((ND_clust(n) == NULL) || ( GD_expanded(ND_clust(n))) )
24 rv = n;
25 else
26 rv = GD_rankleader(ND_clust(n))[ND_rank(n)];
27 return rv;
28}
29
30/* make d slots starting at position pos (where 1 already exists) */
31static void
32make_slots(graph_t * root, int r, int pos, int d)
33{
34 int i;
35 node_t *v, **vlist;
36 vlist = GD_rank(root)[r].v;
37 if (d <= 0) {
38 for (i = pos - d + 1; i < GD_rank(root)[r].n; i++) {
39 v = vlist[i];
40 ND_order(v) = i + d - 1;
41 vlist[ND_order(v)] = v;
42 }
43 for (i = GD_rank(root)[r].n + d - 1; i < GD_rank(root)[r].n; i++)
44 vlist[i] = NULL;
45 } else {
46/*assert(ND_rank(root)[r].n + d - 1 <= ND_rank(root)[r].an);*/
47 for (i = GD_rank(root)[r].n - 1; i > pos; i--) {
48 v = vlist[i];
49 ND_order(v) = i + d - 1;
50 vlist[ND_order(v)] = v;
51 }
52 for (i = pos + 1; i < pos + d; i++)
53 vlist[i] = NULL;
54 }
55 GD_rank(root)[r].n += d - 1;
56}
57
58static node_t*
60{
61 node_t *rv;
62 int r;
63
64 r = ND_rank(vn);
65 make_slots(g, r, ND_order(vn), 2);
66 rv = virtual_node(g);
67 ND_lw(rv) = ND_lw(vn);
68 ND_rw(rv) = ND_rw(vn);
69 ND_rank(rv) = ND_rank(vn);
70 ND_order(rv) = ND_order(vn) + 1;
71 GD_rank(g)[r].v[ND_order(rv)] = rv;
72 return rv;
73}
74
75static void
76map_path(node_t * from, node_t * to, edge_t * orig, edge_t * ve, int type)
77{
78 int r;
79 node_t *u, *v;
80 edge_t *e;
81
82 assert(ND_rank(from) < ND_rank(to));
83
84 if ((agtail(ve) == from) && (aghead(ve) == to))
85 return;
86
87 if (ED_count(ve) > 1) {
88 ED_to_virt(orig) = NULL;
89 if (ND_rank(to) - ND_rank(from) == 1) {
90 if ((e = find_fast_edge(from, to)) && (ports_eq(orig, e))) {
91 merge_oneway(orig, e);
92 if ((ND_node_type(from) == NORMAL)
93 && (ND_node_type(to) == NORMAL))
94 other_edge(orig);
95 return;
96 }
97 }
98 u = from;
99 for (r = ND_rank(from); r < ND_rank(to); r++) {
100 if (r < ND_rank(to) - 1)
101 v = clone_vn(dot_root(from), aghead(ve));
102 else
103 v = to;
104 e = virtual_edge(u, v, orig);
105 ED_edge_type(e) = type;
106 u = v;
107 ED_count(ve)--;
108 ve = ND_out(aghead(ve)).list[0];
109 }
110 } else {
111 if (ND_rank(to) - ND_rank(from) == 1) {
112 if ((ve = find_fast_edge(from, to)) && (ports_eq(orig, ve))) {
113 /*ED_to_orig(ve) = orig; */
114 ED_to_virt(orig) = ve;
115 ED_edge_type(ve) = type;
116 ED_count(ve)++;
117 if ((ND_node_type(from) == NORMAL)
118 && (ND_node_type(to) == NORMAL))
119 other_edge(orig);
120 } else {
121 ED_to_virt(orig) = NULL;
122 ve = virtual_edge(from, to, orig);
123 ED_edge_type(ve) = type;
124 }
125 }
126 if (ND_rank(to) - ND_rank(from) > 1) {
127 if (agtail(ve) != from) {
128 ED_to_virt(orig) = NULL;
129 e = ED_to_virt(orig) = virtual_edge(from, aghead(ve), orig);
131 } else
132 e = ve;
133 while (ND_rank(aghead(e)) != ND_rank(to))
134 e = ND_out(aghead(e)).list[0];
135 if (aghead(e) != to) {
136 ve = e;
137 e = virtual_edge(agtail(e), to, orig);
138 ED_edge_type(e) = type;
140 }
141 }
142 }
143}
144
145static void make_interclust_chain(node_t * from, node_t * to, edge_t * orig) {
146 int newtype;
147 node_t *u, *v;
148
149 u = map_interclust_node(from);
150 v = map_interclust_node(to);
151 if ((u == from) && (v == to))
152 newtype = VIRTUAL;
153 else
154 newtype = CLUSTER_EDGE;
155 map_path(u, v, orig, ED_to_virt(orig), newtype);
156}
157
158/*
159 * attach and install edges between clusters.
160 * essentially, class2() for interclust edges.
161 */
162static void interclexp(graph_t * subg)
163{
164 graph_t *g;
165 node_t *n;
166 edge_t *e, *prev, *next;
167
168 g = dot_root(subg);
169 for (n = agfstnode(subg); n; n = agnxtnode(subg, n)) {
170
171 /* N.B. n may be in a sub-cluster of subg */
172 prev = NULL;
173 for (e = agfstedge(g, n); e; e = next) {
174 next = agnxtedge(g, e, n);
175 if (agcontains(subg, e))
176 continue;
177
178 /* canonicalize edge */
179 e = AGMKOUT(e);
180 /* short/flat multi edges */
181 if (mergeable(prev, e)) {
182 if (ND_rank(agtail(e)) == ND_rank(aghead(e)))
183 ED_to_virt(e) = prev;
184 else
185 ED_to_virt(e) = NULL;
186 if (ED_to_virt(prev) == NULL)
187 continue; /* internal edge */
188 ED_to_virt(e) = NULL;
189 merge_chain(subg, e, ED_to_virt(prev), false);
191 continue;
192 }
193
194 /* flat edges */
195 if (ND_rank(agtail(e)) == ND_rank(aghead(e))) {
196 edge_t* fe;
197 if ((fe = find_flat_edge(agtail(e), aghead(e))) == NULL) {
198 flat_edge(g, e);
199 prev = e;
200 } else if (e != fe) {
202 if (!ED_to_virt(e)) merge_oneway(e, fe);
203 }
204 continue;
205 }
206
207 /* forward edges */
208 if (ND_rank(aghead(e)) > ND_rank(agtail(e))) {
210 prev = e;
211 continue;
212 }
213
214 /* backward edges */
215 else {
216/*
217I think that make_interclust_chain should create call other_edge(e) anyway
218 if (agcontains(subg,agtail(e))
219 && agfindedge(g,aghead(e),agtail(e))) other_edge(e);
220*/
222 prev = e;
223 }
224 }
225 }
226}
227
228static void
230{
231 int i, d, r, pos, ipos;
232 node_t *v;
233 graph_t *root;
234
235 assert(GD_minrank(subg) <= GD_maxrank(subg) && "corrupted rank bounds");
236
237 root = dot_root(subg);
238 if (GD_minrank(subg) > 0)
239 GD_rank(root)[GD_minrank(subg) - 1].valid = false;
240 for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
241 d = GD_rank(subg)[r].n;
242 ipos = pos = ND_order(GD_rankleader(subg)[r]);
243 make_slots(root, r, pos, d);
244 for (i = 0; i < GD_rank(subg)[r].n; i++) {
245 v = GD_rank(root)[r].v[pos] = GD_rank(subg)[r].v[i];
246 ND_order(v) = pos++;
247 /* real nodes automatically have v->root = root graph */
248 if (ND_node_type(v) == VIRTUAL)
249 v->root = agroot(root);
250 delete_fast_node(subg, v);
251 fast_node(root, v);
252 }
253 GD_rank(subg)[r].v = GD_rank(root)[r].v + ipos;
254 GD_rank(root)[r].valid = false;
255 }
256 if (r < GD_maxrank(root))
257 GD_rank(root)[r].valid = false;
258 GD_expanded(subg) = true;
259}
260
261static void
263{
264 int r;
265 node_t *v;
266 edge_t *e;
267
268 for (r = GD_minrank(g); r <= GD_maxrank(g); r++) {
269 v = GD_rankleader(g)[r];
270
271 /* remove the entire chain */
272 while ((e = ND_out(v).list[0])) {
274 free(e->base.data);
275 free(e);
276 }
277 while ((e = ND_in(v).list[0])) {
279 free(e);
280 }
282 free(ND_in(v).list);
283 free(ND_out(v).list);
284 free(v->base.data);
285 free(v);
286 GD_rankleader(g)[r] = NULL;
287 }
288}
289
290/* delete virtual nodes of a cluster, and install real nodes or sub-clusters */
292{
293 /* build internal structure of the cluster */
294 class2(subg);
295 GD_comp(subg).size = 1;
296 GD_comp(subg).list[0] = GD_nlist(subg);
297 allocate_ranks(subg);
298 ints_t scratch = {0};
299 build_ranks(subg, 0, &scratch);
300 ints_free(&scratch);
301 merge_ranks(subg);
302
303 /* build external structure of the cluster */
304 interclexp(subg);
305 remove_rankleaders(subg);
306}
307
308/* this function marks every node in <g> with its top-level cluster under <g> */
310{
311 int c;
312 node_t *n, *nn, *vn;
313 edge_t *orig, *e;
314 graph_t *clust;
315
316 /* remove sub-clusters below this level */
317 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
318 if (ND_ranktype(n) == CLUSTER)
319 UF_singleton(n);
320 ND_clust(n) = NULL;
321 }
322
323 for (c = 1; c <= GD_n_cluster(g); c++) {
324 clust = GD_clust(g)[c];
325 for (n = agfstnode(clust); n; n = nn) {
326 nn = agnxtnode(clust,n);
327 if (ND_ranktype(n) != NORMAL) {
329 "%s was already in a rankset, deleted from cluster %s\n",
330 agnameof(n), agnameof(g));
331 agdelete(clust,n);
332 continue;
333 }
334 UF_setname(n, GD_leader(clust));
335 ND_clust(n) = clust;
336 ND_ranktype(n) = CLUSTER;
337
338 /* here we mark the vnodes of edges in the cluster */
339 for (orig = agfstout(clust, n); orig;
340 orig = agnxtout(clust, orig)) {
341 if ((e = ED_to_virt(orig))) {
342 while (e && ND_node_type(vn =aghead(e)) == VIRTUAL) {
343 ND_clust(vn) = clust;
344 e = ND_out(aghead(e)).list[0];
345 /* trouble if concentrators and clusters are mixed */
346 }
347 }
348 }
349 }
350 }
351}
352
354{
355 int r;
356 node_t *v, *prev, *rl;
357 edge_t *e;
358
359 prev = NULL;
360 GD_rankleader(subg) = gv_calloc(GD_maxrank(subg) + 2, sizeof(node_t*));
361 for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
362 v = GD_rankleader(subg)[r] = virtual_node(g);
363 ND_rank(v) = r;
364 ND_ranktype(v) = CLUSTER;
365 ND_clust(v) = subg;
366 if (prev) {
367 e = virtual_edge(prev, v, NULL);
368 ED_xpenalty(e) *= CL_CROSS;
369 }
370 prev = v;
371 }
372
373 /* set the counts on virtual edges of the cluster skeleton */
374 for (v = agfstnode(subg); v; v = agnxtnode(subg, v)) {
375 rl = GD_rankleader(subg)[ND_rank(v)];
376 ND_UF_size(rl)++;
377 for (e = agfstout(subg, v); e; e = agnxtout(subg, e)) {
378 for (r = ND_rank(agtail(e)); r < ND_rank(aghead(e)); r++) {
379 ED_count(ND_out(rl).list[0])++;
380 }
381 }
382 }
383 for (r = GD_minrank(subg); r <= GD_maxrank(subg); r++) {
384 rl = GD_rankleader(subg)[r];
385 if (ND_UF_size(rl) > 1)
386 ND_UF_size(rl)--;
387 }
388}
389
390void install_cluster(graph_t *g, node_t *n, int pass, queue_t *q) {
391 int r;
392 graph_t *clust;
393
394 clust = ND_clust(n);
395 if (GD_installed(clust) != pass + 1) {
396 for (r = GD_minrank(clust); r <= GD_maxrank(clust); r++)
397 install_in_rank(g, GD_rankleader(clust)[r]);
398 for (r = GD_minrank(clust); r <= GD_maxrank(clust); r++)
399 enqueue_neighbors(q, GD_rankleader(clust)[r], pass);
400 GD_installed(clust) = pass + 1;
401 }
402}
403
404static void mark_lowcluster_basic(Agraph_t * g);
406{
407 Agnode_t *n, *vn;
408 Agedge_t *orig, *e;
409
410 /* first, zap any previous cluster labelings */
411 for (n = agfstnode(root); n; n = agnxtnode(root, n)) {
412 ND_clust(n) = NULL;
413 for (orig = agfstout(root, n); orig; orig = agnxtout(root, orig)) {
414 if ((e = ED_to_virt(orig))) {
415 while (e && (ND_node_type(vn = aghead(e))) == VIRTUAL) {
416 ND_clust(vn) = NULL;
417 e = ND_out(aghead(e)).list[0];
418 }
419 }
420 }
421 }
422
423 /* do the recursion */
425}
426
428{
429 Agraph_t *clust;
430 Agnode_t *n, *vn;
431 Agedge_t *orig, *e;
432 int c;
433
434 for (c = 1; c <= GD_n_cluster(g); c++) {
435 clust = GD_clust(g)[c];
437 }
438 /* see what belongs to this graph that wasn't already marked */
439 for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
440 if (ND_clust(n) == NULL)
441 ND_clust(n) = g;
442 for (orig = agfstout(g, n); orig; orig = agnxtout(g, orig)) {
443 if ((e = ED_to_virt(orig))) {
444 while (e && (ND_node_type(vn = aghead(e))) == VIRTUAL) {
445 if (ND_clust(vn) == NULL)
446 ND_clust(vn) = g;
447 e = ND_out(aghead(e)).list[0];
448 }
449 }
450 }
451 }
452}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
void merge_chain(graph_t *g, edge_t *e, edge_t *f, bool update_count)
Definition class2.c:135
void class2(graph_t *g)
Definition class2.c:160
bool mergeable(edge_t *e, edge_t *f)
Definition class2.c:155
void UF_setname(node_t *u, node_t *v)
Definition utils.c:144
void UF_singleton(node_t *u)
Definition utils.c:137
#define NORMAL
Definition const.h:24
#define VIRTUAL
Definition const.h:25
#define CLUSTER_EDGE
Definition const.h:29
#define CLUSTER
Definition const.h:40
#define CL_CROSS
Definition const.h:153
Agraph_t * dot_root(void *p)
Definition dotinit.c:494
void other_edge(Agedge_t *)
Definition fastgr.c:115
void delete_fast_node(Agraph_t *, Agnode_t *)
Definition fastgr.c:191
Agedge_t * virtual_edge(Agnode_t *, Agnode_t *, Agedge_t *)
Definition fastgr.c:172
void safe_other_edge(Agedge_t *)
Definition fastgr.c:120
void allocate_ranks(Agraph_t *)
Definition mincross.c:1151
void flat_edge(Agraph_t *, Agedge_t *)
Definition fastgr.c:217
void fast_node(Agraph_t *, Agnode_t *)
Definition fastgr.c:177
void delete_fast_edge(Agedge_t *)
Definition fastgr.c:108
void merge_oneway(Agedge_t *, Agedge_t *)
Definition fastgr.c:291
int ports_eq(edge_t *, edge_t *)
Definition position.c:1013
Agedge_t * find_flat_edge(Agnode_t *, Agnode_t *)
Definition fastgr.c:54
void install_in_rank(Agraph_t *, Agnode_t *)
Definition mincross.c:1181
void enqueue_neighbors(queue_t *q, node_t *n0, int pass)
Definition mincross.c:1296
Agedge_t * find_fast_edge(Agnode_t *, Agnode_t *)
Definition fastgr.c:41
void build_ranks(Agraph_t *, int, ints_t *)
Definition mincross.c:1229
Agnode_t * virtual_node(Agraph_t *)
Definition fastgr.c:202
expr procedure type
Definition exparse.y:211
void free(void *)
node NULL
Definition grammar.y:149
#define AGMKOUT(e)
Definition cgraph.h:883
#define ED_xpenalty(e)
Definition types.h:601
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:23
#define ED_count(e)
Definition types.h:580
#define agtail(e)
Definition cgraph.h:889
#define ED_edge_type(e)
Definition types.h:582
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
#define ED_to_virt(e)
Definition types.h:599
void agwarningf(const char *fmt,...)
Definition agerror.c:173
#define GD_minrank(g)
Definition types.h:384
#define GD_maxrank(g)
Definition types.h:382
#define GD_clust(g)
Definition types.h:360
#define GD_installed(g)
Definition types.h:373
#define GD_rank(g)
Definition types.h:395
#define GD_nlist(g)
Definition types.h:393
#define GD_n_cluster(g)
Definition types.h:389
#define GD_expanded(g)
Definition types.h:364
#define GD_leader(g)
Definition types.h:375
#define GD_comp(g)
Definition types.h:362
#define GD_rankleader(g)
Definition types.h:396
#define ND_rank(n)
Definition types.h:523
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
#define ND_clust(n)
Definition types.h:489
#define ND_rw(n)
Definition types.h:525
#define ND_node_type(n)
Definition types.h:511
#define ND_lw(n)
Definition types.h:506
#define ND_order(n)
Definition types.h:513
#define ND_UF_size(n)
Definition types.h:487
#define ND_ranktype(n)
Definition types.h:524
#define ND_in(n)
Definition types.h:501
#define ND_out(n)
Definition types.h:515
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:158
int agdelete(Agraph_t *g, void *obj)
deletes object. Equivalent to agclose, agdelnode, and agdeledge for obj being a graph,...
Definition obj.c:19
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:234
Agraph_t * agroot(void *obj)
Definition obj.c:167
$2 u p prev
Definition htmlparse.y:495
static void mark_lowcluster_basic(Agraph_t *g)
Definition cluster.c:427
void install_cluster(graph_t *g, node_t *n, int pass, queue_t *q)
Definition cluster.c:390
void expand_cluster(graph_t *subg)
Definition cluster.c:291
static void merge_ranks(graph_t *subg)
Definition cluster.c:229
static void map_path(node_t *from, node_t *to, edge_t *orig, edge_t *ve, int type)
Definition cluster.c:76
static void make_slots(graph_t *root, int r, int pos, int d)
Definition cluster.c:32
static void remove_rankleaders(graph_t *g)
Definition cluster.c:262
static void interclexp(graph_t *subg)
Definition cluster.c:162
static node_t * clone_vn(graph_t *g, node_t *vn)
Definition cluster.c:59
static node_t * map_interclust_node(node_t *n)
Definition cluster.c:19
void mark_lowclusters(Agraph_t *root)
Definition cluster.c:405
void build_skeleton(graph_t *g, graph_t *subg)
Definition cluster.c:353
void mark_clusters(graph_t *g)
Definition cluster.c:309
static void make_interclust_chain(node_t *from, node_t *to, edge_t *orig)
Definition cluster.c:145
generic first-in-first-out buffer (queue)
Agobj_t base
Definition cgraph.h:269
Agraph_t * root
Definition cgraph.h:261
Agobj_t base
Definition cgraph.h:260
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
graph or subgraph
Definition cgraph.h:425