Graphviz 13.1.3~dev.20250829.1031
Loading...
Searching...
No Matches
quad_prog_solve.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 <neatogen/digcola.h>
12#include <stdint.h>
13#include <util/alloc.h>
14#include <util/list.h>
15#ifdef DIGCOLA
16#include <math.h>
17#include <stdbool.h>
18#include <stdlib.h>
19#include <time.h>
20#include <stdio.h>
21#include <float.h>
22#include <assert.h>
23#include <neatogen/matrix_ops.h>
24#include <neatogen/kkutils.h>
26
28static bool equals(float a, float b) {
29 const float TOLERANCE = 1e-2;
30 return fabsf(a - b) < TOLERANCE;
31}
32
33float **unpackMatrix(float *packedMat, int n)
34{
35 int i, j, k;
36
37 float **mat = gv_calloc(n, sizeof(float *));
38 mat[0] = gv_calloc(n * n, sizeof(float));
39 set_vector_valf(n * n, 0, mat[0]);
40 for (i = 1; i < n; i++) {
41 mat[i] = mat[0] + i * n;
42 }
43
44 for (i = 0, k = 0; i < n; i++) {
45 for (j = i; j < n; j++, k++) {
46 mat[j][i] = mat[i][j] = packedMat[k];
47 }
48 }
49 return mat;
50}
51
52static void
53ensureMonotonicOrderingWithGaps(float *place, int n, int *ordering,
54 int *levels, int num_levels,
55 float levels_gap)
56{
57 /* ensure that levels are separated in the initial layout and that
58 * places are monotonic increasing within layer
59 */
60
61 int i;
62 int node, level, max_in_level;
63 float lower_bound = -1e9f;
64
65 level = -1;
66 max_in_level = 0;
67 for (i = 0; i < n; i++) {
68 if (i >= max_in_level) {
69 /* we are entering a new level */
70 level++;
71 if (level == num_levels) {
72 /* last_level */
73 max_in_level = n;
74 } else {
75 max_in_level = levels[level];
76 }
77 lower_bound =
78 i > 0 ? place[ordering[i - 1]] + levels_gap : -1e9f;
79 quicksort_placef(place, ordering, i, max_in_level - 1);
80 }
81
82 node = ordering[i];
83 if (place[node] < lower_bound) {
84 place[node] = lower_bound;
85 }
86 }
87}
88
89void
90constrained_majorization_new_with_gaps(CMajEnv * e, float *b,
91 float **coords,
92 int cur_axis, int max_iterations,
93 float levels_gap)
94{
95 float *place = coords[cur_axis];
96 int n = e->n;
97 float **lap = e->A;
98 int *ordering = e->ordering;
99 int *levels = e->levels;
100 int num_levels = e->num_levels;
101 float new_place_i;
102 bool converged = false;
103 float upper_bound, lower_bound;
104 int node;
105 int left, right;
106 float cur_place;
107 float des_place_block;
108 float block_deg;
109 float toBlockConnectivity;
110 float *lap_node;
111 float *desired_place;
112 float *prefix_desired_place;
113 float *suffix_desired_place;
114 int first_next_level;
115 int level = -1, max_in_level = 0;
116 float *gap;
117 float target_place;
118
119 if (max_iterations <= 0) {
120 return;
121 }
122
123 ensureMonotonicOrderingWithGaps(place, n, ordering, levels, num_levels,
124 levels_gap);
125 /* it is important that in 'ordering' nodes are always sorted by layers,
126 * and within a layer by 'place'
127 */
128
129 /* the desired place of each individual node in the current block */
130 desired_place = e->fArray1;
131 /* the desired place of each prefix of current block */
132 prefix_desired_place = e->fArray2;
133 /* the desired place of each suffix of current block */
134 suffix_desired_place = e->fArray3;
135 /* current block (nodes connected by active constraints) */
136 LIST(int) block = {0};
137
138 int *lev = gv_calloc(n, sizeof(int)); // level of each node
139 for (int i = 0; i < n; i++) {
140 if (i >= max_in_level) {
141 /* we are entering a new level */
142 level++;
143 if (level == num_levels) {
144 /* last_level */
145 max_in_level = n;
146 } else {
147 max_in_level = levels[level];
148 }
149 }
150 node = ordering[i];
151 lev[node] = level;
152 }
153
154 /* displacement of block's nodes from block's reference point */
155 gap = e->fArray4;
156
157 for (int counter = 0; counter < max_iterations && !converged; counter++) {
158 converged = true;
159 lower_bound = -1e9; /* no lower bound for first level */
160 for (left = 0; left < n; left = right) {
161 double max_movement;
162 double movement;
163 float prefix_des_place, suffix_des_place;
164 /* compute a block 'ordering[left]...ordering[right-1]' of
165 * nodes connected with active constraints
166 */
167 cur_place = place[ordering[left]];
168 target_place = cur_place;
169 gap[ordering[left]] = 0;
170 for (right = left + 1; right < n; right++) {
171 if (lev[right] > lev[right - 1]) {
172 /* we are entering a new level */
173 target_place += levels_gap; /* note that 'levels_gap' may be negative */
174 }
175 node = ordering[right];
176 /* not sure if this is better than 'place[node]!=target_place' */
177 if (fabs(place[node] - target_place) > 1e-9) {
178 break;
179 }
180 gap[node] = place[node] - cur_place;
181 }
182
183 /* compute desired place of block's reference point according
184 * to each node in the block:
185 */
186 for (int i = left; i < right; i++) {
187 node = ordering[i];
188 new_place_i = -b[node];
189 lap_node = lap[node];
190 for (int j = 0; j < n; j++) {
191 if (j == node) {
192 continue;
193 }
194 new_place_i += lap_node[j] * place[j];
195 }
196 desired_place[node] =
197 new_place_i / (-lap_node[node]) - gap[node];
198 }
199
200 /* reorder block by levels, and within levels
201 * by "relaxed" desired position
202 */
204 first_next_level = 0;
205 for (int i = left; i < right; i = first_next_level) {
206 level = lev[ordering[i]];
207 if (level == num_levels) {
208 /* last_level */
209 first_next_level = right;
210 } else {
211 first_next_level = MIN(right, levels[level]);
212 }
213
214 /* First, collect all nodes with desired places smaller
215 * than current place
216 */
217 for (int j = i; j < first_next_level; j++) {
218 node = ordering[j];
219 if (desired_place[node] < cur_place) {
221 }
222 }
223 /* Second, collect all nodes with desired places equal
224 * to current place
225 */
226 for (int j = i; j < first_next_level; j++) {
227 node = ordering[j];
228 if (desired_place[node] == cur_place) {
230 }
231 }
232 /* Third, collect all nodes with desired places greater
233 * than current place
234 */
235 for (int j = i; j < first_next_level; j++) {
236 node = ordering[j];
237 if (desired_place[node] > cur_place) {
239 }
240 }
241 }
242
243 /* loop through block and compute desired places of its prefixes */
244 des_place_block = 0;
245 block_deg = 0;
246 for (size_t i = 0; i < LIST_SIZE(&block); i++) {
247 node = LIST_GET(&block, i);
248 toBlockConnectivity = 0;
249 lap_node = lap[node];
250 for (size_t j = 0; j < i; j++) {
251 toBlockConnectivity -= lap_node[LIST_GET(&block, j)];
252 }
253 toBlockConnectivity *= 2;
254 /* update block stats */
255 des_place_block =
256 (block_deg * des_place_block +
257 (-lap_node[node]) * desired_place[node] +
258 toBlockConnectivity * cur_place) / (block_deg -
259 lap_node[node] +
260 toBlockConnectivity);
261 prefix_desired_place[i] = des_place_block;
262 block_deg += toBlockConnectivity - lap_node[node];
263 }
264
265 if (n >= 0 && LIST_SIZE(&block) == (size_t)n) {
266 /* fix is needed since denominator was 0 in this case */
267 prefix_desired_place[n - 1] = cur_place; /* a "neutral" value */
268 }
269
270 /* loop through block and compute desired places of its suffixes */
271 des_place_block = 0;
272 block_deg = 0;
273 for (size_t i = LIST_SIZE(&block) - 1; i != SIZE_MAX; i--) {
274 node = LIST_GET(&block, i);
275 toBlockConnectivity = 0;
276 lap_node = lap[node];
277 for (size_t j = i + 1; j < LIST_SIZE(&block); j++) {
278 toBlockConnectivity -= lap_node[LIST_GET(&block, j)];
279 }
280 toBlockConnectivity *= 2;
281 /* update block stats */
282 des_place_block =
283 (block_deg * des_place_block +
284 (-lap_node[node]) * desired_place[node] +
285 toBlockConnectivity * cur_place) / (block_deg -
286 lap_node[node] +
287 toBlockConnectivity);
288 suffix_desired_place[i] = des_place_block;
289 block_deg += toBlockConnectivity - lap_node[node];
290 }
291
292 if (n >= 0 && LIST_SIZE(&block) == (size_t)n) {
293 /* fix is needed since denominator was 0 in this case */
294 suffix_desired_place[0] = cur_place; /* a "neutral" value? */
295 }
296
297
298 /* now, find best place to split block */
299 size_t best_i = SIZE_MAX;
300 max_movement = 0;
301 for (size_t i = 0; i < LIST_SIZE(&block); i++) {
302 suffix_des_place = suffix_desired_place[i];
303 prefix_des_place =
304 i > 0 ? prefix_desired_place[i - 1] : suffix_des_place;
305 /* limit moves to ensure that the prefix is placed before the suffix */
306 if (suffix_des_place < prefix_des_place) {
307 if (suffix_des_place < cur_place) {
308 if (prefix_des_place > cur_place) {
309 prefix_des_place = cur_place;
310 }
311 suffix_des_place = prefix_des_place;
312 } else if (prefix_des_place > cur_place) {
313 prefix_des_place = suffix_des_place;
314 }
315 }
316 movement =
317 (float)(LIST_SIZE(&block) - i) * fabs(suffix_des_place - cur_place) +
318 (float)i * fabs(prefix_des_place - cur_place);
319 if (movement > max_movement) {
320 max_movement = movement;
321 best_i = i;
322 }
323 }
324 /* Actually move prefix and suffix */
325 if (best_i != SIZE_MAX) {
326 suffix_des_place = suffix_desired_place[best_i];
327 prefix_des_place =
328 best_i >
329 0 ? prefix_desired_place[best_i -
330 1] : suffix_des_place;
331
332 /* compute right border of feasible move */
333 if (right >= n) {
334 /* no nodes after current block */
335 upper_bound = 1e9;
336 } else {
337 /* notice that we have to deduct 'gap[block[block_len-1]]'
338 * since all computations should be relative to
339 * the block's reference point
340 */
341 if (lev[ordering[right]] > lev[ordering[right - 1]]) {
342 upper_bound =
343 place[ordering[right]] - levels_gap -
344 gap[LIST_GET(&block, LIST_SIZE(&block) - 1)];
345 } else {
346 upper_bound =
347 place[ordering[right]] -
348 gap[LIST_GET(&block, LIST_SIZE(&block) - 1)];
349 }
350 }
351 suffix_des_place = fminf(suffix_des_place, upper_bound);
352 prefix_des_place = fmaxf(prefix_des_place, lower_bound);
353
354 /* limit moves to ensure that the prefix is placed before the suffix */
355 if (suffix_des_place < prefix_des_place) {
356 if (suffix_des_place < cur_place) {
357 if (prefix_des_place > cur_place) {
358 prefix_des_place = cur_place;
359 }
360 suffix_des_place = prefix_des_place;
361 } else if (prefix_des_place > cur_place) {
362 prefix_des_place = suffix_des_place;
363 }
364 }
365
366 /* move prefix: */
367 for (size_t i = 0; i < best_i; i++) {
368 place[LIST_GET(&block, i)] = prefix_des_place + gap[LIST_GET(&block, i)];
369 }
370 /* move suffix: */
371 for (size_t i = best_i; i < LIST_SIZE(&block); i++) {
372 place[LIST_GET(&block, i)] = suffix_des_place + gap[LIST_GET(&block, i)];
373 }
374
375
376 /* compute lower bound for next block */
377 if (right < n
378 && lev[ordering[right]] > lev[ordering[right - 1]]) {
379 lower_bound = place[LIST_GET(&block, LIST_SIZE(&block) - 1)] + levels_gap;
380 } else {
381 lower_bound = place[LIST_GET(&block, LIST_SIZE(&block) - 1)];
382 }
383
384
385 /* reorder 'ordering' to reflect change of places
386 * Note that it is enough to reorder the level where
387 * the split was done
388 */
389 for (int i = left; i < right; i++) {
390 ordering[i] = LIST_GET(&block, i - (size_t)left);
391 }
392 converged &= equals(prefix_des_place, cur_place)
393 && equals(suffix_des_place, cur_place);
394
395
396 } else {
397 /* no movement */
398 /* compute lower bound for next block */
399 if (right < n
400 && lev[ordering[right]] > lev[ordering[right - 1]]) {
401 lower_bound = place[LIST_GET(&block, LIST_SIZE(&block) - 1)] + levels_gap;
402 } else {
403 lower_bound = place[LIST_GET(&block, LIST_SIZE(&block) - 1)];
404 }
405 }
406 }
407 orthog1f(n, place); /* for numerical stability, keep ||place|| small */
408 }
409 free(lev);
411}
412
413void deleteCMajEnv(CMajEnv * e)
414{
415 free(e->A[0]);
416 free(e->A);
417 free(e->fArray1);
418 free(e->fArray2);
419 free(e->fArray3);
420 free(e->fArray4);
421 free(e);
422}
423
424CMajEnv *initConstrainedMajorization(float *packedMat, int n,
425 int *ordering, int *levels,
426 int num_levels)
427{
428 CMajEnv *e = gv_alloc(sizeof(CMajEnv));
429 e->n = n;
430 e->ordering = ordering;
431 e->levels = levels;
432 e->num_levels = num_levels;
433 e->A = unpackMatrix(packedMat, n);
434 e->fArray1 = gv_calloc(n, sizeof(float));
435 e->fArray2 = gv_calloc(n, sizeof(float));
436 e->fArray3 = gv_calloc(n, sizeof(float));
437 e->fArray4 = gv_calloc(n, sizeof(float));
438 return e;
439}
440#endif /* DIGCOLA */
Memory allocation wrappers that exit on failure.
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 MIN(a, b)
Definition arith.h:28
#define right(i)
Definition closest.c:80
#define left
Definition dthdr.h:12
void free(void *)
#define SIZE_MAX
Definition gmlscan.c:347
void quicksort_placef(float *place, int *ordering, int first, int last)
Definition kkutils.c:140
type-generic dynamically expanding list
#define LIST(type)
Definition list.h:55
#define LIST_SIZE(list)
Definition list.h:80
#define LIST_CLEAR(list)
Definition list.h:249
#define LIST_APPEND(list, item)
Definition list.h:132
#define LIST_FREE(list)
Definition list.h:379
#define LIST_GET(list, index)
Definition list.h:165
void orthog1f(int n, float *vec)
Definition matrix_ops.c:390
void set_vector_valf(int n, float val, float *result)
Definition matrix_ops.c:484
Definition block.h:26