Graphviz 13.0.0~dev.20241220.2304
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 <cgraph/list.h>
12#include <neatogen/digcola.h>
13#include <stdint.h>
14#include <util/alloc.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
89DEFINE_LIST(ints, int)
90
91void
92constrained_majorization_new_with_gaps(CMajEnv * e, float *b,
93 float **coords,
94 int cur_axis, int max_iterations,
95 float levels_gap)
96{
97 float *place = coords[cur_axis];
98 int n = e->n;
99 float **lap = e->A;
100 int *ordering = e->ordering;
101 int *levels = e->levels;
102 int num_levels = e->num_levels;
103 float new_place_i;
104 bool converged = false;
105 float upper_bound, lower_bound;
106 int node;
107 int left, right;
108 float cur_place;
109 float des_place_block;
110 float block_deg;
111 float toBlockConnectivity;
112 float *lap_node;
113 float *desired_place;
114 float *prefix_desired_place;
115 float *suffix_desired_place;
116 int first_next_level;
117 int level = -1, max_in_level = 0;
118 float *gap;
119 float target_place;
120
121 if (max_iterations <= 0) {
122 return;
123 }
124
125 ensureMonotonicOrderingWithGaps(place, n, ordering, levels, num_levels,
126 levels_gap);
127 /* it is important that in 'ordering' nodes are always sorted by layers,
128 * and within a layer by 'place'
129 */
130
131 /* the desired place of each individual node in the current block */
132 desired_place = e->fArray1;
133 /* the desired place of each prefix of current block */
134 prefix_desired_place = e->fArray2;
135 /* the desired place of each suffix of current block */
136 suffix_desired_place = e->fArray3;
137 /* current block (nodes connected by active constraints) */
138 ints_t block = {0};
139
140 int *lev = gv_calloc(n, sizeof(int)); // level of each node
141 for (int i = 0; i < n; i++) {
142 if (i >= max_in_level) {
143 /* we are entering a new level */
144 level++;
145 if (level == num_levels) {
146 /* last_level */
147 max_in_level = n;
148 } else {
149 max_in_level = levels[level];
150 }
151 }
152 node = ordering[i];
153 lev[node] = level;
154 }
155
156 /* displacement of block's nodes from block's reference point */
157 gap = e->fArray4;
158
159 for (int counter = 0; counter < max_iterations && !converged; counter++) {
160 converged = true;
161 lower_bound = -1e9; /* no lower bound for first level */
162 for (left = 0; left < n; left = right) {
163 double max_movement;
164 double movement;
165 float prefix_des_place, suffix_des_place;
166 /* compute a block 'ordering[left]...ordering[right-1]' of
167 * nodes connected with active constraints
168 */
169 cur_place = place[ordering[left]];
170 target_place = cur_place;
171 gap[ordering[left]] = 0;
172 for (right = left + 1; right < n; right++) {
173 if (lev[right] > lev[right - 1]) {
174 /* we are entering a new level */
175 target_place += levels_gap; /* note that 'levels_gap' may be negative */
176 }
177 node = ordering[right];
178 /* not sure if this is better than 'place[node]!=target_place' */
179 if (fabs(place[node] - target_place) > 1e-9) {
180 break;
181 }
182 gap[node] = place[node] - cur_place;
183 }
184
185 /* compute desired place of block's reference point according
186 * to each node in the block:
187 */
188 for (int i = left; i < right; i++) {
189 node = ordering[i];
190 new_place_i = -b[node];
191 lap_node = lap[node];
192 for (int j = 0; j < n; j++) {
193 if (j == node) {
194 continue;
195 }
196 new_place_i += lap_node[j] * place[j];
197 }
198 desired_place[node] =
199 new_place_i / (-lap_node[node]) - gap[node];
200 }
201
202 /* reorder block by levels, and within levels
203 * by "relaxed" desired position
204 */
205 ints_clear(&block);
206 first_next_level = 0;
207 for (int i = left; i < right; i = first_next_level) {
208 level = lev[ordering[i]];
209 if (level == num_levels) {
210 /* last_level */
211 first_next_level = right;
212 } else {
213 first_next_level = MIN(right, levels[level]);
214 }
215
216 /* First, collect all nodes with desired places smaller
217 * than current place
218 */
219 for (int j = i; j < first_next_level; j++) {
220 node = ordering[j];
221 if (desired_place[node] < cur_place) {
222 ints_append(&block, node);
223 }
224 }
225 /* Second, collect all nodes with desired places equal
226 * to current place
227 */
228 for (int j = i; j < first_next_level; j++) {
229 node = ordering[j];
230 if (desired_place[node] == cur_place) {
231 ints_append(&block, node);
232 }
233 }
234 /* Third, collect all nodes with desired places greater
235 * than current place
236 */
237 for (int j = i; j < first_next_level; j++) {
238 node = ordering[j];
239 if (desired_place[node] > cur_place) {
240 ints_append(&block, node);
241 }
242 }
243 }
244
245 /* loop through block and compute desired places of its prefixes */
246 des_place_block = 0;
247 block_deg = 0;
248 for (size_t i = 0; i < ints_size(&block); i++) {
249 node = ints_get(&block, i);
250 toBlockConnectivity = 0;
251 lap_node = lap[node];
252 for (size_t j = 0; j < i; j++) {
253 toBlockConnectivity -= lap_node[ints_get(&block, j)];
254 }
255 toBlockConnectivity *= 2;
256 /* update block stats */
257 des_place_block =
258 (block_deg * des_place_block +
259 (-lap_node[node]) * desired_place[node] +
260 toBlockConnectivity * cur_place) / (block_deg -
261 lap_node[node] +
262 toBlockConnectivity);
263 prefix_desired_place[i] = des_place_block;
264 block_deg += toBlockConnectivity - lap_node[node];
265 }
266
267 if (n >= 0 && ints_size(&block) == (size_t)n) {
268 /* fix is needed since denominator was 0 in this case */
269 prefix_desired_place[n - 1] = cur_place; /* a "neutral" value */
270 }
271
272 /* loop through block and compute desired places of its suffixes */
273 des_place_block = 0;
274 block_deg = 0;
275 for (size_t i = ints_size(&block) - 1; i != SIZE_MAX; i--) {
276 node = ints_get(&block, i);
277 toBlockConnectivity = 0;
278 lap_node = lap[node];
279 for (size_t j = i + 1; j < ints_size(&block); j++) {
280 toBlockConnectivity -= lap_node[ints_get(&block, j)];
281 }
282 toBlockConnectivity *= 2;
283 /* update block stats */
284 des_place_block =
285 (block_deg * des_place_block +
286 (-lap_node[node]) * desired_place[node] +
287 toBlockConnectivity * cur_place) / (block_deg -
288 lap_node[node] +
289 toBlockConnectivity);
290 suffix_desired_place[i] = des_place_block;
291 block_deg += toBlockConnectivity - lap_node[node];
292 }
293
294 if (n >= 0 && ints_size(&block) == (size_t)n) {
295 /* fix is needed since denominator was 0 in this case */
296 suffix_desired_place[0] = cur_place; /* a "neutral" value? */
297 }
298
299
300 /* now, find best place to split block */
301 size_t best_i = SIZE_MAX;
302 max_movement = 0;
303 for (size_t i = 0; i < ints_size(&block); i++) {
304 suffix_des_place = suffix_desired_place[i];
305 prefix_des_place =
306 i > 0 ? prefix_desired_place[i - 1] : suffix_des_place;
307 /* limit moves to ensure that the prefix is placed before the suffix */
308 if (suffix_des_place < prefix_des_place) {
309 if (suffix_des_place < cur_place) {
310 if (prefix_des_place > cur_place) {
311 prefix_des_place = cur_place;
312 }
313 suffix_des_place = prefix_des_place;
314 } else if (prefix_des_place > cur_place) {
315 prefix_des_place = suffix_des_place;
316 }
317 }
318 movement =
319 (float)(ints_size(&block) - i) * fabs(suffix_des_place - cur_place) +
320 (float)i * fabs(prefix_des_place - cur_place);
321 if (movement > max_movement) {
322 max_movement = movement;
323 best_i = i;
324 }
325 }
326 /* Actually move prefix and suffix */
327 if (best_i != SIZE_MAX) {
328 suffix_des_place = suffix_desired_place[best_i];
329 prefix_des_place =
330 best_i >
331 0 ? prefix_desired_place[best_i -
332 1] : suffix_des_place;
333
334 /* compute right border of feasible move */
335 if (right >= n) {
336 /* no nodes after current block */
337 upper_bound = 1e9;
338 } else {
339 /* notice that we have to deduct 'gap[block[block_len-1]]'
340 * since all computations should be relative to
341 * the block's reference point
342 */
343 if (lev[ordering[right]] > lev[ordering[right - 1]]) {
344 upper_bound =
345 place[ordering[right]] - levels_gap -
346 gap[ints_get(&block, ints_size(&block) - 1)];
347 } else {
348 upper_bound =
349 place[ordering[right]] -
350 gap[ints_get(&block, ints_size(&block) - 1)];
351 }
352 }
353 suffix_des_place = fminf(suffix_des_place, upper_bound);
354 prefix_des_place = fmaxf(prefix_des_place, lower_bound);
355
356 /* limit moves to ensure that the prefix is placed before the suffix */
357 if (suffix_des_place < prefix_des_place) {
358 if (suffix_des_place < cur_place) {
359 if (prefix_des_place > cur_place) {
360 prefix_des_place = cur_place;
361 }
362 suffix_des_place = prefix_des_place;
363 } else if (prefix_des_place > cur_place) {
364 prefix_des_place = suffix_des_place;
365 }
366 }
367
368 /* move prefix: */
369 for (size_t i = 0; i < best_i; i++) {
370 place[ints_get(&block, i)] = prefix_des_place + gap[ints_get(&block, i)];
371 }
372 /* move suffix: */
373 for (size_t i = best_i; i < ints_size(&block); i++) {
374 place[ints_get(&block, i)] = suffix_des_place + gap[ints_get(&block, i)];
375 }
376
377
378 /* compute lower bound for next block */
379 if (right < n
380 && lev[ordering[right]] > lev[ordering[right - 1]]) {
381 lower_bound = place[ints_get(&block, ints_size(&block) - 1)] + levels_gap;
382 } else {
383 lower_bound = place[ints_get(&block, ints_size(&block) - 1)];
384 }
385
386
387 /* reorder 'ordering' to reflect change of places
388 * Note that it is enough to reorder the level where
389 * the split was done
390 */
391 for (int i = left; i < right; i++) {
392 ordering[i] = ints_get(&block, i - (size_t)left);
393 }
394 converged &= equals(prefix_des_place, cur_place)
395 && equals(suffix_des_place, cur_place);
396
397
398 } else {
399 /* no movement */
400 /* compute lower bound for next block */
401 if (right < n
402 && lev[ordering[right]] > lev[ordering[right - 1]]) {
403 lower_bound = place[ints_get(&block, ints_size(&block) - 1)] + levels_gap;
404 } else {
405 lower_bound = place[ints_get(&block, ints_size(&block) - 1)];
406 }
407 }
408 }
409 orthog1f(n, place); /* for numerical stability, keep ||place|| small */
410 }
411 free(lev);
412 ints_free(&block);
413}
414
415void deleteCMajEnv(CMajEnv * e)
416{
417 free(e->A[0]);
418 free(e->A);
419 free(e->fArray1);
420 free(e->fArray2);
421 free(e->fArray3);
422 free(e->fArray4);
423 free(e);
424}
425
426CMajEnv *initConstrainedMajorization(float *packedMat, int n,
427 int *ordering, int *levels,
428 int num_levels)
429{
430 CMajEnv *e = gv_alloc(sizeof(CMajEnv));
431 e->n = n;
432 e->ordering = ordering;
433 e->levels = levels;
434 e->num_levels = num_levels;
435 e->A = unpackMatrix(packedMat, n);
436 e->fArray1 = gv_calloc(n, sizeof(float));
437 e->fArray2 = gv_calloc(n, sizeof(float));
438 e->fArray3 = gv_calloc(n, sizeof(float));
439 e->fArray4 = gv_calloc(n, sizeof(float));
440 return e;
441}
442#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:79
#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
#define DEFINE_LIST(name, type)
Definition list.h:26
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