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