Graphviz 16.1.1~dev.20261017.1520
Loading...
Searching...
No Matches
quad_prog_vpsc.c
Go to the documentation of this file.
1
17/**********************************************************
18 *
19 * Solve a quadratic function f(X) = X' e->A X + b X
20 * subject to a set of separation constraints e->cs
21 *
22 * Tim Dwyer, 2006
23 **********************************************************/
24
25#include "config.h"
26
27#include <common/geomprocs.h>
28#include <limits.h>
29#include <neatogen/digcola.h>
30#include <stdbool.h>
31#include <util/alloc.h>
32#include <util/overflow.h>
33#include <util/prisize_t.h>
34#ifdef IPSEPCOLA
35#include <math.h>
36#include <stdlib.h>
37#include <time.h>
38#include <stdio.h>
39#include <float.h>
40#include <assert.h>
41#include <neatogen/matrix_ops.h>
42#include <neatogen/kkutils.h>
43#include <vpsc/csolve_VPSC.h>
46
47/* #define CONMAJ_LOGGING 1 */
48#define quad_prog_tol 1e-4
49
50/*
51 * Use gradient-projection to solve Variable Placement with Separation Constraints problem.
52 */
53int
54constrained_majorization_vpsc(CMajEnvVPSC * e, float *b, float *place,
55 int max_iterations)
56{
57 int counter;
58 float *g, *old_place, *d;
59 /* for laplacian computation need number of real vars and those
60 * dummy vars included in lap
61 */
62 const size_t n = e->nv + e->nldv;
63 bool converged = false;
64#ifdef CONMAJ_LOGGING
65 static int call_no = 0;
66#endif /* CONMAJ_LOGGING */
67
68 if (max_iterations == 0)
69 return 0;
70 g = e->fArray1;
71 old_place = e->fArray2;
72 d = e->fArray3;
73 if (e->m > 0) {
74 for (size_t i = 0; i < n; i++) {
75 setVariableDesiredPos(e->vs[i], place[i]);
76 }
77 satisfyVPSC(e->vpsc);
78 for (size_t i = 0; i < n; i++) {
79 place[i] = getVariablePos(e->vs[i]);
80 }
81 }
82#ifdef CONMAJ_LOGGING
83 float prev_stress = 0;
84 for (size_t i = 0; i < n; i++) {
85 prev_stress += 2 * b[i] * place[i];
86 for (size_t j = 0; j < n; j++) {
87 prev_stress -= e->A[i][j] * place[j] * place[i];
88 }
89 }
90 FILE *logfile = fopen("constrained_majorization_log", "a");
91#endif
92
93 for (counter = 0; counter < max_iterations && !converged; counter++) {
94 float test = 0;
95 float alpha, beta;
96 float numerator = 0, denominator = 0, r;
97 converged = true;
98 /* find steepest descent direction */
99 for (size_t i = 0; i < n; i++) {
100 old_place[i] = place[i];
101 g[i] = 2 * b[i];
102 for (size_t j = 0; j < n; j++) {
103 g[i] -= 2 * e->A[i][j] * place[j];
104 }
105 }
106 for (size_t i = 0; i < n; i++) {
107 numerator += g[i] * g[i];
108 r = 0;
109 for (size_t j = 0; j < n; j++) {
110 r += 2 * e->A[i][j] * g[j];
111 }
112 denominator -= r * g[i];
113 }
114 if (fdivf_overflow(numerator, denominator, &alpha))
115 alpha = 1.0;
116 for (size_t i = 0; i < n; i++) {
117 place[i] -= alpha * g[i];
118 }
119 if (e->m > 0) {
120 /* project to constraint boundary */
121 for (size_t i = 0; i < n; i++) {
122 setVariableDesiredPos(e->vs[i], place[i]);
123 }
124 satisfyVPSC(e->vpsc);
125 for (size_t i = 0; i < n; i++) {
126 place[i] = getVariablePos(e->vs[i]);
127 }
128 }
129 /* set place to the intersection of old_place-g and boundary and
130 * compute d, the vector from intersection pnt to projection pnt
131 */
132 for (size_t i = 0; i < n; i++) {
133 d[i] = place[i] - old_place[i];
134 }
135 /* now compute beta */
136 numerator = 0, denominator = 0;
137 for (size_t i = 0; i < n; i++) {
138 numerator += g[i] * d[i];
139 r = 0;
140 for (size_t j = 0; j < n; j++) {
141 r += 2 * e->A[i][j] * d[j];
142 }
143 denominator += r * d[i];
144 }
145 if (fdivf_overflow(numerator, denominator, &beta))
146 beta = 1.0;
147
148 for (size_t i = 0; i < n; i++) {
149 /* beta > 1.0 takes us back outside the feasible region
150 * beta < 0 clearly not useful and may happen due to numerical imp.
151 */
152 if (beta > 0 && beta < 1.0) {
153 place[i] = old_place[i] + beta * d[i];
154 }
155 test += fabsf(place[i] - old_place[i]);
156 }
157#ifdef CONMAJ_LOGGING
158 float stress = 0;
159 for (size_t i = 0; i < n; i++) {
160 stress += 2 * b[i] * place[i];
161 for (size_t j = 0; j < n; j++) {
162 stress -= e->A[i][j] * place[j] * place[i];
163 }
164 }
165 fprintf(logfile, "%d: stress=%f, test=%f, %s\n", call_no, stress,
166 test, (stress >= prev_stress) ? "No Improvement" : "");
167 prev_stress = stress;
168#endif
169 if (test > quad_prog_tol) {
170 converged = false;
171 }
172 }
173#ifdef CONMAJ_LOGGING
174 call_no++;
175 fclose(logfile);
176#endif
177 return counter;
178}
179
180static DigColaLevel *assign_digcola_levels(const int *ordering, size_t n,
181 int *level_inds,
182 size_t num_divisions);
183
184/*
185 * Set up environment and global constraints (dir-edge constraints, containment constraints
186 * etc).
187 *
188 * diredges: 0=no dir edge constraints
189 * 1=one separation constraint for each edge (in acyclic subgraph)
190 * 2=DiG-CoLa level constraints
191 */
192CMajEnvVPSC *initCMajVPSC(int n, float *packedMat, vtx_data * graph,
193 ipsep_options * opt, int diredges)
194{
195 /* nv is the number of real nodes */
196 CMajEnvVPSC *e = gv_alloc(sizeof(CMajEnvVPSC));
197 e->A = NULL;
198 /* if we have clusters then we'll need two constraints for each var in
199 * a cluster */
200 e->nldv = 2 * opt->clusters.nclusters;
201 e->nv = n - e->nldv;
202 e->ndv = 0;
203
204 e->gcs = NULL;
205 e->vs = gv_calloc(n, sizeof(Variable*));
206 for (int i = 0; i < n; i++) {
207 e->vs[i] = newVariable(i, 1.0, 1.0);
208 }
209 e->gm = 0;
210 if (diredges == 1) {
211 if (Verbose)
212 fprintf(stderr, " generate edge constraints...\n");
213 for (size_t i = 0; i < e->nv; i++) {
214 for (size_t j = 1; j < graph[i].nedges; j++) {
215 if (graph[i].edists[j] > 0) {
216 e->gm++;
217 }
218 }
219 }
220 e->gcs = newConstraints(e->gm);
221 e->gm = 0;
222 for (size_t i = 0; i < e->nv; i++) {
223 for (size_t j = 1; j < graph[i].nedges; j++) {
224 const size_t u = i;
225 int v = graph[i].edges[j];
226 if (graph[i].edists[j] > 0) {
227 e->gcs[e->gm++] =
228 newConstraint(e->vs[u], e->vs[v], opt->edge_gap);
229 }
230 }
231 }
232 } else if (diredges == 2) {
233 int *ordering = NULL, *ls = NULL, cvar;
234 double halfgap;
235 DigColaLevel *levels;
236 Variable **vs = e->vs;
237 /* e->ndv is the number of dummy variables required, one for each boundary */
238 if (compute_hierarchy(graph, e->nv, 1e-2, 1e-1, NULL, &ordering, &ls,
239 &e->ndv)) return NULL;
240 levels = assign_digcola_levels(ordering, e->nv, ls, e->ndv);
241 free(ordering);
242 if (Verbose)
243 fprintf(stderr, "Found %" PRISIZE_T " DiG-CoLa boundaries\n", e->ndv);
244 e->gm =
245 get_num_digcola_constraints(levels, e->ndv + 1) + e->ndv - 1;
246 e->gcs = newConstraints(e->gm);
247 e->gm = 0;
248 e->vs = gv_calloc(n + e->ndv, sizeof(Variable*));
249 for (int i = 0; i < n; i++) {
250 e->vs[i] = vs[i];
251 }
252 free(vs);
253 /* create dummy vars */
254 for (size_t i = 0; i < e->ndv; i++) {
255 /* dummy vars should have 0 weight */
256 cvar = n + i;
257 e->vs[cvar] = newVariable(cvar, 1.0, 0.000001);
258 }
259 halfgap = opt->edge_gap;
260 for (size_t i = 0; i < e->ndv; i++) {
261 cvar = n + i;
262 /* outgoing constraints for each var in level below boundary */
263 for (int j = 0; j < levels[i].num_nodes; j++) {
264 e->gcs[e->gm++] =
265 newConstraint(e->vs[levels[i].nodes[j]], e->vs[cvar],
266 halfgap);
267 }
268 /* incoming constraints for each var in level above boundary */
269 for (int j = 0; j < levels[i + 1].num_nodes; j++) {
270 e->gcs[e->gm++] =
271 newConstraint(e->vs[cvar],
272 e->vs[levels[i + 1].nodes[j]], halfgap);
273 }
274 }
275 /* constraints between adjacent boundary dummy vars */
276 for (size_t i = 0; i < e->ndv - 1; i++) {
277 e->gcs[e->gm++] =
278 newConstraint(e->vs[n + i], e->vs[n + i + 1], 0);
279 }
280 }
281 if (opt->clusters.nclusters > 0) {
282 Constraint **ecs = e->gcs;
283 const size_t nConCs = 2 * opt->clusters.nvars;
284 e->gcs = newConstraints(e->gm + nConCs);
285 for (size_t i = 0; i < e->gm; i++) {
286 e->gcs[i] = ecs[i];
287 }
288 if (ecs != NULL)
289 deleteConstraints(0, ecs);
290 for (size_t i = 0; i < opt->clusters.nclusters; i++) {
291 for (size_t j = 0; j < opt->clusters.clustersizes[i]; j++) {
292 Variable *v = e->vs[opt->clusters.clusters[i][j]];
293 Variable *cl = e->vs[e->nv + 2 * i];
294 Variable *cr = e->vs[e->nv + 2 * i + 1];
295 e->gcs[e->gm++] = newConstraint(cl, v, 0);
296 e->gcs[e->gm++] = newConstraint(v, cr, 0);
297 }
298 }
299 }
300
301 e->m = 0;
302 e->cs = NULL;
303 if (e->gm > 0) {
304 e->vpsc = newIncVPSC(n + e->ndv, e->vs, e->gm, e->gcs);
305 e->m = e->gm;
306 e->cs = e->gcs;
307 }
308 if (packedMat != NULL) {
309 e->A = unpackMatrix(packedMat, n);
310 }
311
312 e->fArray1 = gv_calloc(n, sizeof(float));
313 e->fArray2 = gv_calloc(n, sizeof(float));
314 e->fArray3 = gv_calloc(n, sizeof(float));
315 if (Verbose)
316 fprintf(stderr,
317 " initCMajVPSC done: %" PRISIZE_T " global constraints generated.\n",
318 e->m);
319 return e;
320}
321
322void deleteCMajEnvVPSC(CMajEnvVPSC * e)
323{
324 if (e->A != NULL) {
325 free(e->A[0]);
326 free(e->A);
327 }
328 if (e->m > 0) {
329 deleteVPSC(e->vpsc);
330 if (e->cs != e->gcs && e->gcs != NULL)
331 deleteConstraints(0, e->gcs);
332 deleteConstraints(e->m, e->cs);
333 for (size_t i = 0; i < e->nv + e->nldv + e->ndv; i++) {
334 deleteVariable(e->vs[i]);
335 }
336 free(e->vs);
337 }
338 free(e->fArray1);
339 free(e->fArray2);
340 free(e->fArray3);
341 free(e);
342}
343
344/* generate non-overlap constraints inside each cluster, including dummy
345 * nodes at bounds of cluster
346 * generate constraints again for top level nodes and clusters treating
347 * clusters as rectangles of dim (l,r,b,t)
348 * for each cluster map in-constraints to l out-constraints to r
349 *
350 * For now, we'll keep the global containment constraints already
351 * generated for each cluster, and simply generate non-overlap constraints
352 * for all nodes and then an additional set of non-overlap constraints for
353 * clusters that we'll map back to the dummy vars as above.
354 */
355void generateNonoverlapConstraints(CMajEnvVPSC * e,
356 float nsizeScale,
357 float **coords,
358 int k,
359 bool transitiveClosure,
360 ipsep_options * opt)
361{
362 Constraint **csol, **csolptr;
363 int mol = 0;
364 size_t n = e->nv + e->nldv;
365 boxf* bb = gv_calloc(n, sizeof(boxf));
366 bool genclusters = opt->clusters.nclusters > 0;
367 if (genclusters) {
368 /* n is the number of real variables, not dummy cluster vars */
369 n -= 2 * opt->clusters.nclusters;
370 }
371 if (k == 0) {
372 /* grow a bit in the x dimension, so that if overlap is resolved
373 * horizontally then it won't be considered overlapping vertically
374 */
375 nsizeScale *= 1.0001f;
376 }
377 for (size_t i = 0; i < n; i++) {
378 bb[i].LL.x =
379 coords[0][i] - nsizeScale * opt->nsize[i].x / 2.0 -
380 opt->gap.x / 2.0;
381 bb[i].UR.x =
382 coords[0][i] + nsizeScale * opt->nsize[i].x / 2.0 +
383 opt->gap.x / 2.0;
384 bb[i].LL.y =
385 coords[1][i] - nsizeScale * opt->nsize[i].y / 2.0 -
386 opt->gap.y / 2.0;
387 bb[i].UR.y =
388 coords[1][i] + nsizeScale * opt->nsize[i].y / 2.0 +
389 opt->gap.y / 2.0;
390 }
391 if (genclusters) {
392 Constraint ***cscl = gv_calloc(opt->clusters.nclusters + 1,
393 sizeof(Constraint**));
394 int* cm = gv_calloc(opt->clusters.nclusters + 1, sizeof(int));
395 for (size_t i = 0; i < opt->clusters.nclusters; i++) {
396 const size_t cn = opt->clusters.clustersizes[i];
397 Variable** cvs = gv_calloc(cn + 2, sizeof(Variable*));
398 boxf* cbb = gv_calloc(cn + 2, sizeof(boxf));
399 /* compute cluster bounding bb */
400 boxf container;
401 container.LL.x = container.LL.y = DBL_MAX;
402 container.UR.x = container.UR.y = -DBL_MAX;
403 for (size_t j = 0; j < cn; j++) {
404 const size_t iv = opt->clusters.clusters[i][j];
405 cvs[j] = e->vs[iv];
406 B2BF(bb[iv], cbb[j]);
407 EXPANDBB(&container, bb[iv]);
408 }
409 B2BF(container, opt->clusters.bb[i]);
410 cvs[cn] = e->vs[n + 2 * i];
411 cvs[cn + 1] = e->vs[n + 2 * i + 1];
412 B2BF(container, cbb[cn]);
413 B2BF(container, cbb[cn + 1]);
414 if (k == 0) {
415 cbb[cn].UR.x = container.LL.x + 0.0001;
416 cbb[cn + 1].LL.x = container.UR.x - 0.0001;
417 cm[i] =
418 genXConstraints(cn + 2, cbb, cvs, &cscl[i],
419 transitiveClosure);
420 } else {
421 cbb[cn].UR.y = container.LL.y + 0.0001;
422 cbb[cn + 1].LL.y = container.UR.y - 0.0001;
423 cm[i] = genYConstraints(cn + 2, cbb, cvs, &cscl[i]);
424 }
425 mol += cm[i];
426 free (cvs);
427 free (cbb);
428 }
429 /* generate top level constraints */
430 {
431 const size_t cn = opt->clusters.ntoplevel + opt->clusters.nclusters;
432 Variable** cvs = gv_calloc(cn, sizeof(Variable*));
433 boxf* cbb = gv_calloc(cn, sizeof(boxf));
434 for (size_t i = 0; i < opt->clusters.ntoplevel; i++) {
435 const size_t iv = opt->clusters.toplevel[i];
436 cvs[i] = e->vs[iv];
437 B2BF(bb[iv], cbb[i]);
438 }
439 /* make dummy variables for clusters */
440 for (size_t i = opt->clusters.ntoplevel; i < cn; i++) {
441 assert(i <= INT_MAX);
442 cvs[i] = newVariable(123 + (int)i, 1, 1);
443 const size_t j = i - opt->clusters.ntoplevel;
444 B2BF(opt->clusters.bb[j], cbb[i]);
445 }
446 if (k == 0) {
447 const size_t i = opt->clusters.nclusters;
448 cm[i] =
449 genXConstraints(cn, cbb, cvs, &cscl[i],
450 transitiveClosure);
451 } else {
452 const size_t i = opt->clusters.nclusters;
453 cm[i] = genYConstraints(cn, cbb, cvs, &cscl[i]);
454 }
455 /* remap constraints from tmp dummy vars to cluster l and r vars */
456 for (size_t i = opt->clusters.ntoplevel; i < cn; i++) {
457 double dgap;
458 const size_t j = i - opt->clusters.ntoplevel;
459 /* dgap is the change in required constraint gap.
460 * since we are going from a source rectangle the size
461 * of the cluster bounding box to a zero width (in x dim,
462 * zero height in y dim) rectangle, the change will be
463 * half the bb width.
464 */
465 if (k == 0) {
466 dgap = -(cbb[i].UR.x - cbb[i].LL.x) / 2.0;
467 } else {
468 dgap = -(cbb[i].UR.y - cbb[i].LL.y) / 2.0;
469 }
470 remapInConstraints(cvs[i], e->vs[n + 2 * j], dgap);
471 remapOutConstraints(cvs[i], e->vs[n + 2 * j + 1], dgap);
472 /* there may be problems with cycles between
473 * cluster non-overlap and diredge constraints,
474 * to resolve:
475 *
476 * for each constraint c:v->cvs[i]:
477 * if exists diredge constraint u->v where u in c:
478 * remap v->cl to cr->v (gap = height(v)/2)
479 *
480 * in = getInConstraints(cvs[i])
481 * for(c : in) {
482 * assert(c.right==cvs[i]);
483 * vin = getOutConstraints(v=c.left)
484 * for(d : vin) {
485 * if(d.left.cluster==i):
486 * tmp = d.left
487 * d.left = d.right
488 * d.right = tmp
489 * d.gap = height(d.right)/2
490 * }
491 * }
492 *
493 */
494 deleteVariable(cvs[i]);
495 }
496 mol += cm[opt->clusters.nclusters];
497 free (cvs);
498 free (cbb);
499 }
500 csolptr = csol = newConstraints(mol);
501 for (size_t i = 0; i < opt->clusters.nclusters + 1; i++) {
502 /* copy constraints into csol */
503 for (int j = 0; j < cm[i]; j++) {
504 *csolptr++ = cscl[i][j];
505 }
506 deleteConstraints(0, cscl[i]);
507 }
508 free (cscl);
509 free (cm);
510 } else {
511 if (k == 0) {
512 mol = genXConstraints(n, bb, e->vs, &csol, transitiveClosure);
513 } else {
514 mol = genYConstraints(n, bb, e->vs, &csol);
515 }
516 }
517 /* remove constraints from previous iteration */
518 if (e->m > 0) {
519 /* can't reuse instance of VPSC when constraints change! */
520 deleteVPSC(e->vpsc);
521 for (size_t i = e->gm; i < e->m; i++) {
522 /* delete previous overlap constraints */
523 deleteConstraint(e->cs[i]);
524 }
525 /* just delete the array, not the elements */
526 if (e->cs != e->gcs)
527 deleteConstraints(0, e->cs);
528 }
529 /* if we have no global constraints then the overlap constraints
530 * are all we have to worry about.
531 * Otherwise, we have to copy the global and overlap constraints
532 * into the one array
533 */
534 if (e->gm == 0) {
535 e->m = mol;
536 e->cs = csol;
537 } else {
538 e->m = mol + e->gm;
539 e->cs = newConstraints(e->m);
540 for (size_t i = 0; i < e->m; i++) {
541 if (i < e->gm) {
542 e->cs[i] = e->gcs[i];
543 } else {
544 e->cs[i] = csol[i - e->gm];
545 }
546 }
547 /* just delete the array, not the elements */
548 deleteConstraints(0, csol);
549 }
550 if (Verbose)
551 fprintf(stderr, " generated %" PRISIZE_T " constraints\n", e->m);
552 e->vpsc = newIncVPSC(e->nv + e->nldv + e->ndv, e->vs, e->m, e->cs);
553 free (bb);
554}
555
556/*
557 * Statically remove overlaps, that is remove all overlaps by moving each node as
558 * little as possible.
559 */
560void removeoverlaps(int n, float **coords, ipsep_options * opt)
561{
562 int i;
563 CMajEnvVPSC *e = initCMajVPSC(n, NULL, NULL, opt, 0);
564 generateNonoverlapConstraints(e, 1.0, coords, 0, true, opt);
565 solveVPSC(e->vpsc);
566 for (i = 0; i < n; i++) {
567 coords[0][i] = getVariablePos(e->vs[i]);
568 }
569 generateNonoverlapConstraints(e, 1.0, coords, 1, false, opt);
570 solveVPSC(e->vpsc);
571 for (i = 0; i < n; i++) {
572 coords[1][i] = getVariablePos(e->vs[i]);
573 }
574 deleteCMajEnvVPSC(e);
575}
576
577/*
578 unpack the "ordering" array into an array of DigColaLevel
579*/
580static DigColaLevel *assign_digcola_levels(const int *ordering, size_t n,
581 int *level_inds,
582 size_t num_divisions) {
583 int j;
584 DigColaLevel *l = gv_calloc(num_divisions + 1, sizeof(DigColaLevel));
585 /* first level */
586 l[0].num_nodes = level_inds[0];
587 l[0].nodes = gv_calloc(l[0].num_nodes, sizeof(int));
588 for (int i = 0; i < l[0].num_nodes; i++) {
589 l[0].nodes[i] = ordering[i];
590 }
591 /* second through second last level */
592 for (size_t i = 1; i < num_divisions; i++) {
593 l[i].num_nodes = level_inds[i] - level_inds[i - 1];
594 l[i].nodes = gv_calloc(l[i].num_nodes, sizeof(int));
595 for (j = 0; j < l[i].num_nodes; j++) {
596 l[i].nodes[j] = ordering[level_inds[i - 1] + j];
597 }
598 }
599 /* last level */
600 if (num_divisions > 0) {
601 l[num_divisions].num_nodes = n - level_inds[num_divisions - 1];
602 l[num_divisions].nodes = gv_calloc(l[num_divisions].num_nodes, sizeof(int));
603 for (int i = 0; i < l[num_divisions].num_nodes; i++) {
604 l[num_divisions].nodes[i] =
605 ordering[level_inds[num_divisions - 1] + i];
606 }
607 }
608 return l;
609}
610
611/*********************
612get number of separation constraints based on the number of nodes in each level
613ie, num_sep_constraints = sum_i^{num_levels-1} (|L[i]|+|L[i+1]|)
614**********************/
615int get_num_digcola_constraints(DigColaLevel *levels, size_t num_levels) {
616 int nc = 0;
617 for (size_t i = 1; i < num_levels; i++) {
618 nc += levels[i].num_nodes + levels[i - 1].num_nodes;
619 }
620 nc += levels[0].num_nodes + levels[num_levels - 1].num_nodes;
621 return nc;
622}
623
624#endif /* IPSEPCOLA */
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
void setVariableDesiredPos(Variable *v, double desiredPos)
int genYConstraints(size_t n, boxf *bb, Variable **vs, Constraint ***cs)
Variable * newVariable(int id, double desiredPos, double weight)
Bridge for C programs to access solve_VPSC (which is in C++)
Constraint ** newConstraints(int m)
void deleteConstraint(Constraint *c)
VPSC * newIncVPSC(int n, Variable *vs[], int m, Constraint *cs[])
void deleteVariable(Variable *v)
Constraint * newConstraint(Variable *left, Variable *right, double gap)
void remapInConstraints(Variable *u, Variable *v, double dgap)
void remapOutConstraints(Variable *u, Variable *v, double dgap)
double getVariablePos(const Variable *v)
void deleteConstraints(size_t m, Constraint **cs)
void satisfyVPSC(VPSC *vpsc)
void deleteVPSC(VPSC *vpsc)
void solveVPSC(VPSC *vpsc)
int genXConstraints(size_t n, boxf *bb, Variable **vs, Constraint ***cs, bool transitiveClosure)
#define B2BF(b, bf)
Definition geom.h:69
geometric functions (e.g. on points and boxes)
#define EXPANDBB(b0, b1)
Definition geomprocs.h:65
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
Agraph_t * graph(char *name)
Definition gv.cpp:34
arithmetic overflow helpers
static bool fdivf_overflow(float n, float d, float *res)
Definition overflow.h:162
#define PRISIZE_T
Definition prisize_t.h:25
#define alpha
Definition shapes.c:4034
A constraint determines a minimum or exact spacing required between two variables.
Definition constraint.h:25
Definition geom.h:41
pointf UR
Definition geom.h:41
pointf LL
Definition geom.h:41
double x
Definition geom.h:29
double y
Definition geom.h:29