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