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