Graphviz 12.0.1~dev.20240716.0800
Loading...
Searching...
No Matches
constrained_majorization_ipsep.c
Go to the documentation of this file.
1
17/**********************************************************
18 * Based on constrained_majorization.c
19 *
20 * Perform stress majorization subject
21 * to separation constraints, for background see the paper:
22 * "IPSep-CoLa: An Incremental Procedure for Separation Constraint Layout of Graphs"
23 * by Tim Dwyer, Yehuda Koren and Kim Marriott
24 *
25 * Available separation constraints so far are:
26 * o Directed edge constraints
27 * o Node non-overlap constraints
28 * o Cluster containment constraints
29 * o Cluster/node non-overlap constraints
30 *
31 * Tim Dwyer, 2006
32 **********************************************************/
33
34#include <cgraph/alloc.h>
35#include <neatogen/digcola.h>
36#include <stdbool.h>
37#ifdef IPSEPCOLA
38#include <math.h>
39#include <stdlib.h>
40#include <time.h>
41#include <stdio.h>
42#include <float.h>
43#include <neatogen/stress.h>
44#include <neatogen/dijkstra.h>
45#include <neatogen/bfs.h>
46#include <neatogen/matrix_ops.h>
47#include <neatogen/kkutils.h>
48#include <neatogen/conjgrad.h>
49#include <vpsc/csolve_VPSC.h>
52#include <neatogen/matrix_ops.h>
53
54#define localConstrMajorIterations 1000
55
56int stress_majorization_cola(vtx_data * graph, /* Input graph in sparse representation */
57 int n, /* Number of nodes */
58 double **d_coords, /* Coordinates of nodes (output layout) */
59 node_t ** nodes, /* Original nodes */
60 int dim, /* Dimemsionality of layout */
61 int model, /* difference model */
62 int maxi, /* max iterations */
63 ipsep_options * opt)
64{
65 int iterations = 0; /* Output: number of iteration of the process */
66
67 /*************************************************
68 ** Computation of full, dense, unrestricted k-D **
69 ** stress minimization by majorization **
70 ** This function imposes HIERARCHY CONSTRAINTS **
71 *************************************************/
72
73 int i, k;
74 float *lap1 = NULL;
75 float *dist_accumulator = NULL;
76 float *tmp_coords = NULL;
77 float **b = NULL;
78 double *degrees = NULL;
79 float *lap2 = NULL;
80 int lap_length;
81 float *f_storage = NULL;
82 float **coords = NULL;
83 int orig_n = n;
84
85 CMajEnvVPSC *cMajEnvHor = NULL;
86 CMajEnvVPSC *cMajEnvVrt = NULL;
87 double y_0;
88 int length;
89 DistType diameter;
90 float *Dij = NULL;
91 float constant_term;
92 int count;
93 double degree;
94 int step;
95 float val;
96 double old_stress, new_stress = 0;
97 bool converged;
98 int len;
99 double nsizeScale = 0;
100 float maxEdgeLen = 0;
101 double max = 1;
102
103 initLayout(n, dim, d_coords, nodes);
104 if (n == 1)
105 return 0;
106
107 for (i = 0; i < n; i++) {
108 for (size_t j = 1; j < graph[i].nedges; j++) {
109 maxEdgeLen = MAX(graph[i].ewgts[j], maxEdgeLen);
110 }
111 }
112
113 /****************************************************
114 ** Compute the all-pairs-shortest-distances matrix **
115 ****************************************************/
116
117 if (maxi == 0)
118 return iterations;
119
120 if (Verbose)
121 start_timer();
122
123 if (model == MODEL_SUBSET) {
124 /* weight graph to separate high-degree nodes */
125 /* and perform slower Dijkstra-based computation */
126 if (Verbose)
127 fprintf(stderr, "Calculating subset model");
129 } else if (model == MODEL_CIRCUIT) {
130 Dij = circuitModel(graph, n);
131 if (!Dij) {
133 "graph is disconnected. Hence, the circuit model\n");
135 "is undefined. Reverting to the shortest path model.\n");
136 }
137 } else if (model == MODEL_MDS) {
138 if (Verbose)
139 fprintf(stderr, "Calculating MDS model");
140 Dij = mdsModel(graph, n);
141 }
142 if (!Dij) {
143 if (Verbose)
144 fprintf(stderr, "Calculating shortest paths");
145 Dij = compute_apsp_packed(graph, n);
146 }
147 if (Verbose) {
148 fprintf(stderr, ": %.2f sec\n", elapsed_sec());
149 fprintf(stderr, "Setting initial positions");
150 start_timer();
151 }
152
153 diameter = -1;
154 length = n + n * (n - 1) / 2;
155 for (i = 0; i < length; i++) {
156 if (Dij[i] > diameter) {
157 diameter = (int) Dij[i];
158 }
159 }
160
161 /* for numerical stability, scale down layout */
162 /* No Jiggling, might conflict with constraints */
163 for (i = 0; i < dim; i++) {
164 for (int j = 0; j < n; j++) {
165 max = fmax(max, fabs(d_coords[i][j]));
166 }
167 }
168 for (i = 0; i < dim; i++) {
169 for (int j = 0; j < n; j++) {
170 d_coords[i][j] *= 10 / max;
171 }
172 }
173
174 /**************************
175 ** Layout initialization **
176 **************************/
177
178 for (i = 0; i < dim; i++) {
179 orthog1(n, d_coords[i]);
180 }
181
182 /* for the y-coords, don't center them, but translate them so y[0]=0 */
183 y_0 = d_coords[1][0];
184 for (i = 0; i < n; i++) {
185 d_coords[1][i] -= y_0;
186 }
187 if (Verbose)
188 fprintf(stderr, ": %.2f sec", elapsed_sec());
189
190 /**************************
191 ** Laplacian computation **
192 **************************/
193
194 lap2 = Dij;
195 lap_length = n + n * (n - 1) / 2;
196 square_vec(lap_length, lap2);
197 /* compute off-diagonal entries */
198 invert_vec(lap_length, lap2);
199
200 if (opt->clusters.nclusters > 0) {
201 int nn = n + opt->clusters.nclusters * 2;
202 int clap_length = nn + nn * (nn - 1) / 2;
203 float *clap = gv_calloc(clap_length, sizeof(float));
204 int c0, c1;
205 float v;
206 c0 = c1 = 0;
207 for (i = 0; i < nn; i++) {
208 for (int j = 0; j < nn - i; j++) {
209 if (i < n && j < n - i) {
210 v = lap2[c0++];
211 } else {
212 /* v=j==1?i%2:0; */
213 if (j == 1 && i % 2 == 1) {
214 v = maxEdgeLen;
215 v *= v;
216 if (v > 0.01f) {
217 v = 1.0f / v;
218 }
219 } else
220 v = 0;
221 }
222 clap[c1++] = v;
223 }
224 }
225 free(lap2);
226 lap2 = clap;
227 n = nn;
228 lap_length = clap_length;
229 }
230 /* compute diagonal entries */
231 count = 0;
232 degrees = gv_calloc(n, sizeof(double));
233 set_vector_val(n, 0, degrees);
234 for (i = 0; i < n - 1; i++) {
235 degree = 0;
236 count++; /* skip main diag entry */
237 for (int j = 1; j < n - i; j++, count++) {
238 val = lap2[count];
239 degree += val;
240 degrees[i + j] -= val;
241 }
242 degrees[i] -= degree;
243 }
244 for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
245 lap2[count] = (float) degrees[i];
246 }
247
248 coords = gv_calloc(dim, sizeof(float *));
249 f_storage = gv_calloc(dim * n, sizeof(float));
250 for (i = 0; i < dim; i++) {
251 coords[i] = f_storage + i * n;
252 for (int j = 0; j < n; j++) {
253 coords[i][j] = j < orig_n ? (float)d_coords[i][j] : 0;
254 }
255 }
256
257 /* compute constant term in stress sum
258 * which is \sum_{i<j} w_{ij}d_{ij}^2
259 */
260 constant_term = (float) (n * (n - 1) / 2);
261
262 /*************************
263 ** Layout optimization **
264 *************************/
265
266 b = gv_calloc(dim, sizeof(float *));
267 b[0] = gv_calloc(dim * n, sizeof(float));
268 for (k = 1; k < dim; k++) {
269 b[k] = b[0] + k * n;
270 }
271
272 tmp_coords = gv_calloc(n, sizeof(float));
273 dist_accumulator = gv_calloc(n, sizeof(float));
274
275 old_stress = DBL_MAX; /* at least one iteration */
276
277 if ((cMajEnvHor = initCMajVPSC(n, lap2, graph, opt, 0)) == NULL) {
278 iterations = -1;
279 goto finish;
280 }
281 if ((cMajEnvVrt = initCMajVPSC(n, lap2, graph, opt, opt->diredges)) == NULL) {
282 iterations = -1;
283 goto finish;
284 }
285
286 lap1 = gv_calloc(lap_length, sizeof(float));
287
288 for (converged = false, iterations = 0;
289 iterations < maxi && !converged; iterations++) {
290
291 /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|) */
292 set_vector_val(n, 0, degrees);
293 sqrt_vecf(lap_length, lap2, lap1);
294 for (count = 0, i = 0; i < n - 1; i++) {
295 len = n - i - 1;
296 /* init 'dist_accumulator' with zeros */
297 set_vector_valf(n, 0, dist_accumulator);
298
299 /* put into 'dist_accumulator' all squared distances
300 * between 'i' and 'i'+1,...,'n'-1
301 */
302 for (k = 0; k < dim; k++) {
303 set_vector_valf(len, coords[k][i], tmp_coords);
304 vectors_mult_additionf(len, tmp_coords, -1, coords[k] + i + 1);
305 square_vec(len, tmp_coords);
306 vectors_additionf(len, tmp_coords, dist_accumulator, dist_accumulator);
307 }
308
309 /* convert to 1/d_{ij} */
310 invert_sqrt_vec(len, dist_accumulator);
311 /* detect overflows */
312 for (int j = 0; j < len; j++) {
313 if (dist_accumulator[j] >= FLT_MAX || dist_accumulator[j] < 0) {
314 dist_accumulator[j] = 0;
315 }
316 }
317
318 count++; /* save place for the main diagonal entry */
319 degree = 0;
320 for (int j = 0; j < len; j++, count++) {
321 val = lap1[count] *= dist_accumulator[j];
322 degree += val;
323 degrees[i + j + 1] -= val;
324 }
325 degrees[i] -= degree;
326 }
327 for (step = n, count = 0, i = 0; i < n; i++, count += step, step--) {
328 lap1[count] = (float) degrees[i];
329 }
330
331 /* Now compute b[] (L^(X(t))*X(t)) */
332 for (k = 0; k < dim; k++) {
333 /* b[k] := lap1*coords[k] */
334 right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
335 }
336
337 /* compute new stress
338 * remember that the Laplacians are negated, so we subtract
339 * instead of add and vice versa
340 */
341 new_stress = 0;
342 for (k = 0; k < dim; k++) {
343 new_stress += vectors_inner_productf(n, coords[k], b[k]);
344 }
345 new_stress *= 2;
346 new_stress += constant_term; /* only after mult by 2 */
347 for (k = 0; k < dim; k++) {
348 right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
349 new_stress -= vectors_inner_productf(n, coords[k], tmp_coords);
350 }
351
352 /* check for convergence */
353 if (Verbose && (iterations % 1 == 0)) {
354 fprintf(stderr, "%.3f ", new_stress);
355 if (iterations % 10 == 0)
356 fprintf(stderr, "\n");
357 }
358 converged = new_stress < old_stress
359 && fabs(new_stress - old_stress) / fabs(old_stress + 1e-10) <
360 Epsilon;
361 /*converged = converged || (iterations>1 && new_stress>old_stress); */
362 /* in first iteration we allowed stress increase, which
363 * might result ny imposing constraints
364 */
365 old_stress = new_stress;
366
367 /* in determining non-overlap constraints we gradually scale up the
368 * size of nodes to avoid local minima
369 */
370 if ((iterations >= maxi - 1 || converged) && opt->noverlap == 1
371 && nsizeScale < 0.999) {
372 nsizeScale += 0.1;
373 if (Verbose)
374 fprintf(stderr, "nsizescale=%f,iterations=%d\n",
375 nsizeScale, iterations);
376 iterations = 0;
377 converged = false;
378 }
379
380
381 /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim'
382 * system of equations, thereby obtaining the new coordinates.
383 * If we use the constraints (given by the var's: 'ordering',
384 * 'levels' and 'num_levels'), we cannot optimize
385 * trace(X'LX)+X'B by simply solving equations, but we have
386 * to use a quadratic programming solver
387 * note: 'lap2' is a packed symmetric matrix, that is its
388 * upper-triangular part is arranged in a vector row-wise
389 * also note: 'lap2' is really the negated laplacian (the
390 * laplacian is -'lap2')
391 */
392
393 if (opt->noverlap == 1 && nsizeScale > 0.001) {
394 generateNonoverlapConstraints(cMajEnvHor, nsizeScale, coords,
395 0,
396 nsizeScale >= 0.5,
397 opt);
398 }
399 if (cMajEnvHor->m > 0) {
400 constrained_majorization_vpsc(cMajEnvHor, b[0], coords[0],
401 localConstrMajorIterations);
402 } else {
403 /* if there are no constraints then use conjugate gradient
404 * optimisation which should be considerably faster
405 */
406 if (conjugate_gradient_mkernel(lap2, coords[0], b[0], n,
407 tolerance_cg, n) < 0) {
408 iterations = -1;
409 goto finish;
410 }
411 }
412 if (opt->noverlap == 1 && nsizeScale > 0.001) {
413 generateNonoverlapConstraints(cMajEnvVrt, nsizeScale, coords,
414 1, false, opt);
415 }
416 if (cMajEnvVrt->m > 0) {
417 if (constrained_majorization_vpsc(cMajEnvVrt, b[1], coords[1],
418 localConstrMajorIterations) < 0) {
419 iterations = -1;
420 goto finish;
421 }
422 } else {
423 conjugate_gradient_mkernel(lap2, coords[1], b[1], n,
424 tolerance_cg, n);
425 }
426 }
427 if (Verbose) {
428 fprintf(stderr, "\nfinal e = %f %d iterations %.2f sec\n",
429 new_stress, iterations, elapsed_sec());
430 }
431 deleteCMajEnvVPSC(cMajEnvHor);
432 deleteCMajEnvVPSC(cMajEnvVrt);
433
434 if (opt->noverlap == 2) {
435 /* fprintf(stderr, "Removing overlaps as post-process...\n"); */
436 removeoverlaps(orig_n, coords, opt);
437 }
438
439finish:
440 if (coords != NULL) {
441 for (i = 0; i < dim; i++) {
442 for (int j = 0; j < orig_n; j++) {
443 d_coords[i][j] = coords[i][j];
444 }
445 }
446 free(coords[0]);
447 free(coords);
448 }
449
450 if (b) {
451 free(b[0]);
452 free(b);
453 }
454 free(tmp_coords);
455 free(dist_accumulator);
456 free(degrees);
457 free(lap2);
458 free(lap1);
459
460 return iterations;
461}
462#endif /* IPSEPCOLA */
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define Epsilon
Definition arcball.h:210
int conjugate_gradient_mkernel(float *A, float *x, float *b, int n, double tol, int max_iterations)
Definition conjgrad.c:161
static double len(glCompPoint p)
Definition glutils.c:150
static int Verbose
Definition gml2gv.c:22
void free(void *)
node NULL
Definition grammar.y:149
void agwarningf(const char *fmt,...)
Definition agerror.c:173
int agerr(agerrlevel_t level, const char *fmt,...)
Definition agerror.c:155
@ AGPREV
Definition cgraph.h:858
Agraph_t * graph(char *name)
Definition gv.cpp:31
void invert_vec(int n, float *vec)
Definition matrix_ops.c:509
void invert_sqrt_vec(int n, float *vec)
Definition matrix_ops.c:529
void vectors_additionf(int n, float *vector1, float *vector2, float *result)
Definition matrix_ops.c:442
void set_vector_valf(int n, float val, float *result)
Definition matrix_ops.c:484
void orthog1(int n, double *vec)
Definition matrix_ops.c:233
void sqrt_vecf(int n, float *source, float *target)
Definition matrix_ops.c:519
void set_vector_val(int n, double val, double *result)
Definition matrix_ops.c:477
void right_mult_with_vector_ff(float *packed_matrix, int n, float *vector, float *result)
Definition matrix_ops.c:409
void vectors_mult_additionf(int n, float *vector1, float alpha, float *vector2)
Definition matrix_ops.c:451
void square_vec(int n, float *vec)
Definition matrix_ops.c:501
double vectors_inner_productf(int n, float *vector1, float *vector2)
Definition matrix_ops.c:466
#define MODEL_SUBSET
Definition neato.h:17
#define MODEL_MDS
Definition neato.h:18
#define MODEL_CIRCUIT
Definition neato.h:16
int DistType
Definition sparsegraph.h:37
float * compute_apsp_artificial_weights_packed(vtx_data *graph, int n)
Definition stress.c:718
float * circuitModel(vtx_data *graph, int nG)
Definition stress.c:168
int initLayout(int n, int dim, double **coords, node_t **nodes)
Definition stress.c:130
float * mdsModel(vtx_data *graph, int nG)
Definition stress.c:667
float * compute_apsp_packed(vtx_data *graph, int n)
Definition stress.c:700
#define tolerance_cg
Definition stress.h:19
double elapsed_sec(void)
Definition timing.c:48
void start_timer(void)
Definition timing.c:43
#define MAX(a, b)
Definition write.c:31