Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
closest.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/sort.h>
14#include <cgraph/stack.h>
15#include <limits.h>
16#include <neatogen/kkutils.h>
17#include <neatogen/closest.h>
18#include <stdbool.h>
19#include <stdint.h>
20#include <stdlib.h>
21
22/*****************************************
23** This module contains functions that **
24** given a 1-D layout construct a graph **
25** where close nodes in this layout are **
26** adjacent **
27*****************************************/
28
29typedef struct {
30 /* this structure represents two nodes in the 1-D layout */
31 size_t left;
32 size_t right;
33 double dist; /* distance between the nodes in the layout */
34} Pair;
35
36#define LT(p,q) ((p).dist < (q).dist)
37#define EQ(p,q) ((p).dist == (q).dist)
38
39static void push(gv_stack_t *s, Pair x) {
40
41 // copy the item to save it on the stack
42 Pair *copy = gv_alloc(sizeof(x));
43 *copy = x;
44
46}
47
48static bool pop(gv_stack_t *s, Pair *x) {
49
50 // is the stack empty?
51 if (stack_is_empty(s)) {
52 return false;
53 }
54
55 // remove the top and pass it back to the caller
56 Pair *top = stack_pop(s);
57 *x = *top;
58
59 // discard the previously saved copy
60 free(top);
61
62 return true;
63}
64
65#define sub(h,i) (*(Pair*)gv_stack_get((h), (i)))
66
67/* An auxulliary data structure (a heap) for
68 * finding the closest pair in the layout
69 */
70typedef struct {
72 size_t heapSize;
73 size_t maxSize;
74} PairHeap;
75
76#define left(i) (2*(i))
77#define right(i) (2*(i)+1)
78#define parent(i) ((i)/2)
79#define insideHeap(h,i) ((i)<h->heapSize)
80#define greaterPriority(h,i,j) \
81 (LT(h->data[i],h->data[j]) || ((EQ(h->data[i],h->data[j])) && (rand()%2)))
82
83#define exchange(h,i,j) {Pair temp; \
84 temp=h->data[i]; \
85 h->data[i]=h->data[j]; \
86 h->data[j]=temp; \
87}
88
89static void heapify(PairHeap *h, size_t i) {
90 size_t largest;
91 while (1) {
92 size_t l = left(i);
93 size_t r = right(i);
94 if (insideHeap(h, l) && greaterPriority(h, l, i))
95 largest = l;
96 else
97 largest = i;
98 if (insideHeap(h, r) && greaterPriority(h, r, largest))
99 largest = r;
100 if (largest == i)
101 break;
102
103 exchange(h, largest, i);
104 i = largest;
105 }
106}
107
108static void freeHeap(PairHeap * h)
109{
110 free(h->data);
111}
112
113static void initHeap(PairHeap *h, double *place, size_t *ordering, size_t n) {
114 Pair edge;
115
116 h->heapSize = n == 0 ? 0 : (n - 1);
117 h->maxSize = h->heapSize;
118 h->data = gv_calloc(h->maxSize, sizeof(Pair));
119
120 for (size_t i = 0; n != 0 && i < n - 1; i++) {
121 edge.left = ordering[i];
122 edge.right = ordering[i + 1];
123 edge.dist = place[ordering[i + 1]] - place[ordering[i]];
124 h->data[i] = edge;
125 }
126 for (size_t j = (n - 1) / 2; n != 0 && j != SIZE_MAX; j--) {
127 heapify(h, j);
128 }
129}
130
131static bool extractMax(PairHeap * h, Pair * max)
132{
133 if (h->heapSize == 0)
134 return false;
135
136 *max = h->data[0];
137 h->data[0] = h->data[h->heapSize - 1];
138 h->heapSize--;
139 heapify(h, 0);
140 return true;
141}
142
143static void insert(PairHeap * h, Pair edge)
144{
145 size_t i = h->heapSize;
146 if (h->heapSize == h->maxSize) {
147 size_t newSize = h->maxSize * 2;
148 h->data = gv_recalloc(h->data, h->maxSize, newSize, sizeof(Pair));
149 h->maxSize = newSize;
150 }
151 h->heapSize++;
152 h->data[i] = edge;
153 while (i > 0 && greaterPriority(h, i, parent(i))) {
154 exchange(h, i, parent(i));
155 i = parent(i);
156 }
157}
158
159static int cmp(const void *a, const void *b, void *context) {
160 const size_t *x = a;
161 const size_t *y = b;
162 const double *place = context;
163
164 if (place[*x] < place[*y]) {
165 return -1;
166 }
167 if (place[*x] > place[*y]) {
168 return 1;
169 }
170 return 0;
171}
172
173static void find_closest_pairs(double *place, size_t n, int num_pairs,
174 gv_stack_t* pairs_stack) {
175 /* Fill the stack 'pairs_stack' with 'num_pairs' closest pairs int the 1-D layout 'place' */
177 size_t *left = gv_calloc(n, sizeof(size_t));
178 size_t *right = gv_calloc(n, sizeof(size_t));
179 Pair pair = {0}, new_pair;
180
181 /* Order the nodes according to their place */
182 size_t *ordering = gv_calloc(n, sizeof(size_t));
183 size_t *inv_ordering = gv_calloc(n, sizeof(size_t));
184
185 for (size_t i = 0; i < n; i++) {
186 ordering[i] = i;
187 }
188 gv_sort(ordering, n, sizeof(ordering[0]), cmp, place);
189 for (size_t i = 0; i < n; i++) {
190 inv_ordering[ordering[i]] = i;
191 }
192
193 /* Initialize heap with all consecutive pairs */
194 initHeap(&heap, place, ordering, n);
195
196 /* store the leftmost and rightmost neighbors of each node that were entered into heap */
197 for (size_t i = 1; i < n; i++) {
198 left[ordering[i]] = ordering[i - 1];
199 }
200 for (size_t i = 0; n != 0 && i < n - 1; i++) {
201 right[ordering[i]] = ordering[i + 1];
202 }
203
204 /* extract the 'num_pairs' closest pairs */
205 for (int i = 0; i < num_pairs; i++) {
206
207 if (!extractMax(&heap, &pair)) {
208 break; /* not enough pairs */
209 }
210 push(pairs_stack, pair);
211 /* insert to heap "descendant" pairs */
212 size_t left_index = inv_ordering[pair.left];
213 size_t right_index = inv_ordering[pair.right];
214 if (left_index > 0) {
215 size_t neighbor = ordering[left_index - 1];
216 if (inv_ordering[right[neighbor]] < right_index) {
217 /* we have a new pair */
218 new_pair.left = neighbor;
219 new_pair.right = pair.right;
220 new_pair.dist = place[pair.right] - place[neighbor];
221 insert(&heap, new_pair);
222 right[neighbor] = pair.right;
223 left[pair.right] = neighbor;
224 }
225 }
226 if (right_index < n - 1) {
227 size_t neighbor = ordering[right_index + 1];
228 if (inv_ordering[left[neighbor]] > left_index) {
229 /* we have a new pair */
230 new_pair.left = pair.left;
231 new_pair.right = neighbor;
232 new_pair.dist = place[neighbor] - place[pair.left];
233 insert(&heap, new_pair);
234 left[neighbor] = pair.left;
235 right[pair.left] = neighbor;
236 }
237 }
238 }
239 free(left);
240 free(right);
241 free(ordering);
242 free(inv_ordering);
243 freeHeap(&heap);
244}
245
246static void add_edge(vtx_data * graph, int u, int v)
247{
248 for (size_t i = 0; i < graph[u].nedges; i++) {
249 if (graph[u].edges[i] == v) {
250 /* edge already exist */
251 return;
252 }
253 }
254 /* add the edge */
255 graph[u].edges[graph[u].nedges++] = v;
256 graph[v].edges[graph[v].nedges++] = u;
257 if (graph[0].ewgts != NULL) {
258 graph[u].ewgts[0]--;
259 graph[v].ewgts[0]--;
260 }
261}
262
263static void construct_graph(size_t n, gv_stack_t *edges_stack,
264 vtx_data **New_graph) {
265 /* construct an unweighted graph using the edges 'edges_stack' */
266 vtx_data *new_graph;
267
268 /* first compute new degrees and nedges; */
269 int *degrees = gv_calloc(n, sizeof(int));
270 size_t top = stack_size(edges_stack);
271 size_t new_nedges = 2 * top + n;
272 Pair pair;
273 int *edges = gv_calloc(new_nedges, sizeof(int));
274 float *weights = gv_calloc(new_nedges, sizeof(float));
275
276 for (size_t i = 0; i < n; i++) {
277 degrees[i] = 1; /* save place for the self loop */
278 }
279 for (size_t i = 0; i < top; i++) {
280 pair = sub(edges_stack, i);
281 degrees[pair.left]++;
282 degrees[pair.right]++;
283 }
284
285 /* copy graph into new_graph: */
286 for (size_t i = 0; i < new_nedges; i++) {
287 weights[i] = 1.0;
288 }
289
290 *New_graph = new_graph = gv_calloc(n, sizeof(vtx_data));
291 for (size_t i = 0; i < n; i++) {
292 new_graph[i].nedges = 1;
293 new_graph[i].ewgts = weights;
294 new_graph[i].edges = edges;
295 assert(i <= INT_MAX);
296 *edges = (int)i; // self loop for Lap
297 *weights = 0; /* self loop weight for Lap */
298 weights += degrees[i];
299 edges += degrees[i]; /* reserve space for possible more edges */
300 }
301
302 free(degrees);
303
304 /* add all edges from stack */
305 while (pop(edges_stack, &pair)) {
306 assert(pair.left <= INT_MAX);
307 assert(pair.right <= INT_MAX);
308 add_edge(new_graph, (int)pair.left, (int)pair.right);
309 }
310}
311
312void
313closest_pairs2graph(double *place, int n, int num_pairs, vtx_data ** graph)
314{
315 /* build a graph with with edges between the 'num_pairs' closest pairs in the 1-D space: 'place' */
316 gv_stack_t pairs_stack = {0};
317 assert(n >= 0);
318 find_closest_pairs(place, (size_t)n, num_pairs, &pairs_stack);
319 construct_graph((size_t)n, &pairs_stack, graph);
320 stack_reset(&pairs_stack);
321}
Agobj_t * copy(Agraph_t *g, Agobj_t *obj)
Definition actions.c:156
Memory allocation wrappers that exit on failure.
static void * gv_recalloc(void *ptr, size_t old_nmemb, size_t new_nmemb, size_t size)
Definition alloc.h:73
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
#define greaterPriority(h, i, j)
Definition closest.c:80
static void initHeap(PairHeap *h, double *place, size_t *ordering, size_t n)
Definition closest.c:113
static void freeHeap(PairHeap *h)
Definition closest.c:108
void closest_pairs2graph(double *place, int n, int num_pairs, vtx_data **graph)
Definition closest.c:313
static void push(gv_stack_t *s, Pair x)
Definition closest.c:39
#define left(i)
Definition closest.c:76
#define parent(i)
Definition closest.c:78
static bool extractMax(PairHeap *h, Pair *max)
Definition closest.c:131
static void add_edge(vtx_data *graph, int u, int v)
Definition closest.c:246
static void heapify(PairHeap *h, size_t i)
Definition closest.c:89
#define right(i)
Definition closest.c:77
#define exchange(h, i, j)
Definition closest.c:83
static void construct_graph(size_t n, gv_stack_t *edges_stack, vtx_data **New_graph)
Definition closest.c:263
static void find_closest_pairs(double *place, size_t n, int num_pairs, gv_stack_t *pairs_stack)
Definition closest.c:173
#define insideHeap(h, i)
Definition closest.c:79
static void insert(PairHeap *h, Pair edge)
Definition closest.c:143
#define sub(h, i)
Definition closest.c:65
static int cmp(const void *a, const void *b, void *context)
Definition closest.c:159
static Agnode_t * pop(void)
Definition ccomps.c:237
void free(void *)
edge
Definition gmlparse.y:279
#define SIZE_MAX
Definition gmlscan.c:347
node NULL
Definition grammar.y:149
Agraph_t * graph(char *name)
Definition gv.cpp:31
static Agedge_t * top(gv_stack_t *sp)
Definition tred.c:71
#define neighbor(t, i, edim, elist)
Definition make_map.h:37
static int num_pairs
Definition pca.c:20
qsort with carried along context
static void gv_sort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
qsort with an extra state parameter, ala qsort_r
Definition sort.h:35
Implementation of a dynamically expanding stack data structure.
static void stack_push(gv_stack_t *stack, void *item)
Definition stack.h:21
static size_t stack_size(const gv_stack_t *stack)
Definition stack.h:13
static void * stack_pop(gv_stack_t *stack)
Definition stack.h:33
static void stack_reset(gv_stack_t *stack)
Definition stack.h:35
static bool stack_is_empty(const gv_stack_t *stack)
Definition stack.h:17
size_t maxSize
Definition closest.c:73
size_t heapSize
Definition closest.c:72
Pair * data
Definition closest.c:71
Definition closest.c:29
size_t right
the right node in the pair
Definition closest.c:32
double dist
Definition closest.c:33
size_t left
the left node in the pair
Definition closest.c:31
float * ewgts
Definition sparsegraph.h:30
unsigned nedges
unsigned * edges
Definition grammar.c:93