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 | * http://www.eclipse.org/legal/epl-v10.html |
7 | * |
8 | * Contributors: See CVS logs. Details at http://www.graphviz.org/ |
9 | *************************************************************************/ |
10 | |
11 | #include "types.h" |
12 | #include "globals.h" |
13 | #include "general.h" |
14 | #include "time.h" |
15 | #include "SparseMatrix.h" |
16 | #include "vector.h" |
17 | #include "edge_bundling.h" |
18 | #include "ink.h" |
19 | #include "agglomerative_bundling.h" |
20 | #include "nearest_neighbor_graph.h" |
21 | |
22 | #if OPENGL |
23 | #include "gl.h" |
24 | extern pedge *edges_global; |
25 | extern int nedges_global; |
26 | #endif |
27 | |
28 | enum {DEBUG=0}; |
29 | |
30 | static Agglomerative_Ink_Bundling Agglomerative_Ink_Bundling_init(SparseMatrix A, pedge *edges, int level){ |
31 | Agglomerative_Ink_Bundling grid; |
32 | int n = A->n, i; |
33 | |
34 | assert(SparseMatrix_is_symmetric(A, TRUE)); |
35 | |
36 | if (!A) return NULL; |
37 | assert(A->m == n); |
38 | grid = MALLOC(sizeof(struct Agglomerative_Ink_Bundling_struct)); |
39 | grid->level = level; |
40 | grid->n = n; |
41 | grid->A = A; |
42 | grid->P = NULL; |
43 | grid->R0 = NULL; |
44 | grid->R = NULL; |
45 | grid->next = NULL; |
46 | grid->prev = NULL; |
47 | grid->inks = MALLOC(sizeof(real)*(A->m)); |
48 | grid->edges = edges; |
49 | grid->delete_top_level_A = 0; |
50 | grid->total_ink = -1; |
51 | if (level == 0){ |
52 | real total_ink = 0; |
53 | for (i = 0; i < n; i++) { |
54 | (grid->inks)[i] = ink1(edges[i]); |
55 | total_ink += (grid->inks)[i]; |
56 | } |
57 | grid->total_ink = total_ink; |
58 | } |
59 | return grid; |
60 | } |
61 | |
62 | static void Agglomerative_Ink_Bundling_delete(Agglomerative_Ink_Bundling grid){ |
63 | if (!grid) return; |
64 | if (grid->A){ |
65 | if (grid->level == 0) { |
66 | if (grid->delete_top_level_A) SparseMatrix_delete(grid->A); |
67 | } else { |
68 | SparseMatrix_delete(grid->A); |
69 | } |
70 | } |
71 | SparseMatrix_delete(grid->P); |
72 | /* on level 0, R0 = NULL, on level 1, R0 = R */ |
73 | if (grid->level > 1) SparseMatrix_delete(grid->R0); |
74 | SparseMatrix_delete(grid->R); |
75 | FREE(grid->inks); |
76 | |
77 | Agglomerative_Ink_Bundling_delete(grid->next); |
78 | FREE(grid); |
79 | } |
80 | |
81 | static Agglomerative_Ink_Bundling Agglomerative_Ink_Bundling_establish(Agglomerative_Ink_Bundling grid, int *pick, real angle_param, real angle){ |
82 | /* pick is a work array of dimension n, with n the total number of original edges */ |
83 | int *matching; |
84 | SparseMatrix A = grid->A; |
85 | int n = grid->n, level = grid->level, nc = 0; |
86 | int *ia = A->ia, *ja = A->ja; |
87 | // real *a; |
88 | int i, j, k, jj, jc, jmax, ni, nj, npicks; |
89 | int *mask; |
90 | pedge *edges = grid->edges; |
91 | real *inks = grid->inks, *cinks, inki, inkj; |
92 | real gain, maxgain, minink, total_gain = 0; |
93 | int *ip = NULL, *jp = NULL, ie; |
94 | Vector *cedges;/* a table listing the content of bundled edges in the coarsen grid. |
95 | cedges[i] contain the list of origonal edges that make up the bundle i in the next level */ |
96 | real ink0, ink1, grand_total_ink = 0, grand_total_gain = 0; |
97 | point_t meet1, meet2; |
98 | |
99 | if (Verbose > 1) fprintf(stderr,"level ===================== %d, n = %d\n" ,grid->level, n); |
100 | cedges = MALLOC(sizeof(Vector)*n); |
101 | cinks = MALLOC(sizeof(real)*n); |
102 | for (i = 0; i < n; i++) cedges[i] = Vector_new(1, sizeof(int), NULL); |
103 | |
104 | if (grid->level > 0){ |
105 | ip = grid->R0->ia; |
106 | jp = grid->R0->ja; |
107 | } |
108 | |
109 | matching = MALLOC(sizeof(int)*n); |
110 | mask = MALLOC(sizeof(real)*n); |
111 | for (i = 0; i < n; i++) mask[i] = -1; |
112 | |
113 | assert(n == A->n); |
114 | for (i = 0; i < n; i++) matching[i] = UNMATCHED; |
115 | |
116 | // a = (real*) A->a; |
117 | for (i = 0; i < n; i++){ |
118 | if (matching[i] != UNMATCHED) continue; |
119 | |
120 | /* find the best matching in ink saving */ |
121 | maxgain = 0; |
122 | minink = -1; |
123 | jmax = -1; |
124 | for (j = ia[i]; j < ia[i+1]; j++){ |
125 | jj = ja[j]; |
126 | if (jj == i) continue; |
127 | |
128 | /* ink saving of merging i and j */ |
129 | if ((jc=matching[jj]) == UNMATCHED){ |
130 | /* neither i nor jj are matched */ |
131 | inki = inks[i]; inkj = inks[jj]; |
132 | if (ip && jp){/* not the first level */ |
133 | ni = (ip[i+1] - ip[i]);/* number of edges represented by i */ |
134 | nj = (ip[jj+1] - ip[jj]);/* number of edges represented by jj */ |
135 | MEMCPY(pick, &(jp[ip[i]]), sizeof(int)*ni); |
136 | MEMCPY(pick+ni, &(jp[ip[jj]]), sizeof(int)*nj); |
137 | } else {/* first level */ |
138 | pick[0] = i; pick[1] = jj; |
139 | ni = nj = 1; |
140 | } |
141 | if (Verbose && DEBUG) fprintf(stderr, "ink(%d)=%f, ink(%d)=%f" , i, inki, jj, inkj); |
142 | } else { |
143 | /* j is already matched. Its content is on cedges[jc] */ |
144 | inki = inks[i]; inkj = cinks[jc]; |
145 | if (Verbose && DEBUG) fprintf(stderr, "ink(%d)=%f, ink(%d->%d)=%f" , i, inki, jj, jc, inkj); |
146 | if (ip) { |
147 | ni = (ip[i+1] - ip[i]);/* number of edges represented by i */ |
148 | MEMCPY(pick, &(jp[ip[i]]), sizeof(int)*ni); |
149 | } else { |
150 | ni = 1; pick[0] = i; |
151 | } |
152 | nj = Vector_get_length(cedges[jc]); |
153 | npicks = ni; |
154 | for (k = 0; k < nj; k++) { |
155 | pick[npicks++] = *((int*) Vector_get(cedges[jc], k)); |
156 | } |
157 | } |
158 | |
159 | npicks = ni + nj; |
160 | ink1 = ink(edges, npicks, pick, &ink0, &meet1, &meet2, angle_param, angle); |
161 | if (Verbose && DEBUG) { |
162 | fprintf(stderr,", if merging {" ); |
163 | for (k = 0; k < npicks; k++) fprintf(stderr,"%d," , pick[k]); |
164 | fprintf(stderr,"}, " ); |
165 | fprintf(stderr, " ink0=%f, ink1=%f" , inki+inkj, ink1); |
166 | } |
167 | |
168 | gain = inki + inkj - ink1; |
169 | if (Verbose && DEBUG) fprintf(stderr, " gain=%f" , gain); |
170 | if (gain > maxgain){ |
171 | maxgain = gain; |
172 | minink = ink1; |
173 | jmax = jj; |
174 | if (Verbose && DEBUG) fprintf(stderr, "maxgain=%f" , maxgain); |
175 | } |
176 | if (Verbose && DEBUG) fprintf(stderr, "\n" ); |
177 | |
178 | |
179 | |
180 | } |
181 | |
182 | |
183 | /* now merge i and jmax */ |
184 | if (maxgain > 0){ |
185 | /* a good bundling of i and another edge jmax is found */ |
186 | total_gain += maxgain; |
187 | jc = matching[jmax]; |
188 | if (jc == UNMATCHED){/* i and j both unmatched. Add j in the table first */ |
189 | if (Verbose && DEBUG) printf("maxgain=%f, merge %d with best edge: %d to form coarsen edge %d. Ink=%f\n" ,maxgain, i, jmax, nc, minink); |
190 | matching[i] = matching[jmax] = nc; |
191 | if (ip){ |
192 | for (k = ip[jmax]; k < ip[jmax+1]; k++) { |
193 | ie = jp[k]; |
194 | Vector_add(cedges[nc], (void*) (&ie)); |
195 | } |
196 | } else { |
197 | Vector_add(cedges[nc], (void*) (&jmax)); |
198 | } |
199 | jc = nc; |
200 | nc++; |
201 | } else {/*j is already matched */ |
202 | if (Verbose && DEBUG) printf("maxgain=%f, merge %d with existing cluster %d\n" ,maxgain, i, jc); |
203 | matching[i] = jc; |
204 | grand_total_ink -= cinks[jc];/* ink of cluster jc was already added, and will be added again as part of a larger cluster with i, so dicount */ |
205 | } |
206 | } else {/*i can not match/bundle successfully */ |
207 | if (Verbose && DEBUG) fprintf(stderr, "no gain in bundling node %d\n" ,i); |
208 | assert(maxgain <= 0); |
209 | matching[i] = nc; |
210 | jc = nc; |
211 | minink = inks[i]; |
212 | nc++; |
213 | } |
214 | |
215 | |
216 | /* add i to the appropriate table */ |
217 | if (ip){ |
218 | for (k = ip[i]; k < ip[i+1]; k++) { |
219 | ie = jp[k]; |
220 | Vector_add(cedges[jc], (void*) (&ie)); |
221 | } |
222 | } else { |
223 | Vector_add(cedges[jc], (void*) (&i)); |
224 | } |
225 | cinks[jc] = minink; |
226 | grand_total_ink += minink; |
227 | grand_total_gain += maxgain; |
228 | |
229 | if (Verbose && DEBUG){ |
230 | fprintf(stderr," coarse edge[%d]={" ,jc); |
231 | for (k = 0; k < Vector_get_length(cedges[jc]); k++) { |
232 | fprintf(stderr,"%d," , *((int*) Vector_get(cedges[jc], k))); |
233 | } |
234 | fprintf(stderr,"}, grand_total_gain=%f\n" ,grand_total_gain); |
235 | } |
236 | |
237 | } |
238 | |
239 | if (nc >= 1 && total_gain > 0){ |
240 | /* now set up restriction and prolongation operator */ |
241 | SparseMatrix P, R, R1, R0, B, cA; |
242 | real one = 1.; |
243 | Agglomerative_Ink_Bundling cgrid; |
244 | |
245 | R1 = SparseMatrix_new(nc, n, 1, MATRIX_TYPE_REAL, FORMAT_COORD); |
246 | for (i = 0; i < n; i++){ |
247 | jj = matching[i]; |
248 | SparseMatrix_coordinate_form_add_entries(R1, 1, &jj, &i, &one); |
249 | } |
250 | R = SparseMatrix_from_coordinate_format(R1); |
251 | SparseMatrix_delete(R1); |
252 | P = SparseMatrix_transpose(R); |
253 | B = SparseMatrix_multiply(R, A); |
254 | if (!B) goto RETURN; |
255 | cA = SparseMatrix_multiply(B, P); |
256 | if (!cA) goto RETURN; |
257 | SparseMatrix_delete(B); |
258 | grid->P = P; |
259 | grid->R = R; |
260 | |
261 | level++; |
262 | cgrid = Agglomerative_Ink_Bundling_init(cA, edges, level); |
263 | |
264 | |
265 | /* set up R0!!! */ |
266 | if (grid->R0){ |
267 | R0 = SparseMatrix_multiply(R, grid->R0); |
268 | } else { |
269 | assert(grid->level == 0); |
270 | R0 = R; |
271 | } |
272 | cgrid->R0 = R0; |
273 | cgrid->inks = cinks; |
274 | cgrid->total_ink = grand_total_ink; |
275 | |
276 | if (Verbose > 1) fprintf(stderr,"level %d->%d, edges %d -> %d, ink %f->%f , gain = %f, or %f\n" , grid->level, cgrid->level, grid->n, |
277 | cgrid->n, grid->total_ink, grand_total_ink, grid->total_ink - grand_total_ink, grand_total_gain); |
278 | assert(ABS(grid->total_ink - cgrid->total_ink - grand_total_gain) <= 0.0001*grid->total_ink); |
279 | |
280 | cgrid = Agglomerative_Ink_Bundling_establish(cgrid, pick, angle_param, angle); |
281 | grid->next = cgrid; |
282 | cgrid->prev = grid; |
283 | |
284 | } else { |
285 | if (Verbose > 1) fprintf(stderr,"no more improvement, orig ink = %f, gain = %f, stop and final bundling found\n" , grand_total_ink, grand_total_gain); |
286 | /* no more improvement, stop and final bundling found */ |
287 | for (i = 0; i < n; i++) matching[i] = i; |
288 | } |
289 | |
290 | RETURN: |
291 | FREE(matching); |
292 | for (i = 0; i < n; i++) Vector_delete(cedges[i]); |
293 | FREE(cedges); |
294 | FREE(mask); |
295 | return grid; |
296 | } |
297 | |
298 | |
299 | #ifndef NOTUSED |
300 | static Agglomerative_Ink_Bundling Agglomerative_Ink_Bundling_aggressive_establish(Agglomerative_Ink_Bundling grid, int *pick, real angle_param, real angle){ |
301 | /* this does a true single-linkage clustering: find the edge that gives the best saving, merge, find again. |
302 | As oppose to: find the edge of node i that gives the best ink saving, merge, then do the same for node i+1, etc etc. |
303 | Right now it is implemented as a quick hack to check whether it is worth while: it saves about 3% extra ink on airlines: from |
304 | 59% to 62% |
305 | */ |
306 | /* pick is a work array of dimension n, with n the total number of original edges */ |
307 | int *matching; |
308 | SparseMatrix A = grid->A; |
309 | int n = grid->n, level = grid->level, nc = 0; |
310 | int *ia = A->ia, *ja = A->ja; |
311 | // real *a; |
312 | int i, j, k, jj, jc = -1, jmax, imax, ni, nj, npicks; |
313 | int *mask; |
314 | pedge *edges = grid->edges; |
315 | real *inks = grid->inks, *cinks, inki, inkj; |
316 | real gain, maxgain, minink, total_gain = 0; |
317 | int *ip = NULL, *jp = NULL, ie; |
318 | Vector *cedges;/* a table listing the content of bundled edges in the coarsen grid. |
319 | cedges[i] contain the list of origonal edges that make up the bundle i in the next level */ |
320 | real ink0, ink1, grand_total_ink = 0, grand_total_gain = 0; |
321 | point_t meet1, meet2; |
322 | |
323 | if (Verbose > 1) fprintf(stderr,"level ===================== %d, n = %d\n" ,grid->level, n); |
324 | cedges = MALLOC(sizeof(Vector)*n); |
325 | cinks = MALLOC(sizeof(real)*n); |
326 | for (i = 0; i < n; i++) cedges[i] = Vector_new(1, sizeof(int), NULL); |
327 | |
328 | if (grid->level > 0){ |
329 | ip = grid->R0->ia; |
330 | jp = grid->R0->ja; |
331 | } |
332 | |
333 | matching = MALLOC(sizeof(int)*n); |
334 | mask = MALLOC(sizeof(real)*n); |
335 | for (i = 0; i < n; i++) mask[i] = -1; |
336 | |
337 | assert(n == A->n); |
338 | for (i = 0; i < n; i++) matching[i] = UNMATCHED; |
339 | |
340 | //a = (real*) A->a; |
341 | |
342 | do { |
343 | maxgain = 0; |
344 | imax = -1; |
345 | jmax = -1; |
346 | minink = -1; |
347 | for (i = 0; i < n; i++){ |
348 | if (matching[i] != UNMATCHED) continue; |
349 | |
350 | /* find the best matching in ink saving */ |
351 | for (j = ia[i]; j < ia[i+1]; j++){ |
352 | jj = ja[j]; |
353 | if (jj == i) continue; |
354 | |
355 | /* ink saving of merging i and j */ |
356 | if ((jc=matching[jj]) == UNMATCHED){ |
357 | /* neither i nor jj are matched */ |
358 | inki = inks[i]; inkj = inks[jj]; |
359 | if (ip && jp){/* not the first level */ |
360 | ni = (ip[i+1] - ip[i]);/* number of edges represented by i */ |
361 | nj = (ip[jj+1] - ip[jj]);/* number of edges represented by jj */ |
362 | MEMCPY(pick, &(jp[ip[i]]), sizeof(int)*ni); |
363 | MEMCPY(pick+ni, &(jp[ip[jj]]), sizeof(int)*nj); |
364 | } else {/* first level */ |
365 | pick[0] = i; pick[1] = jj; |
366 | ni = nj = 1; |
367 | } |
368 | if (Verbose && DEBUG) fprintf(stderr, "ink(%d)=%f, ink(%d)=%f" , i, inki, jj, inkj); |
369 | } else { |
370 | /* j is already matched. Its content is on cedges[jc] */ |
371 | inki = inks[i]; inkj = cinks[jc]; |
372 | if (Verbose && DEBUG) fprintf(stderr, "ink(%d)=%f, ink(%d->%d)=%f" , i, inki, jj, jc, inkj); |
373 | if (ip) { |
374 | ni = (ip[i+1] - ip[i]);/* number of edges represented by i */ |
375 | MEMCPY(pick, &(jp[ip[i]]), sizeof(int)*ni); |
376 | } else { |
377 | ni = 1; pick[0] = i; |
378 | } |
379 | nj = Vector_get_length(cedges[jc]); |
380 | npicks = ni; |
381 | for (k = 0; k < nj; k++) { |
382 | pick[npicks++] = *((int*) Vector_get(cedges[jc], k)); |
383 | } |
384 | } |
385 | |
386 | npicks = ni + nj; |
387 | ink1 = ink(edges, npicks, pick, &ink0, &meet1, &meet2, angle_param, angle); |
388 | if (Verbose && DEBUG) { |
389 | fprintf(stderr,", if merging {" ); |
390 | for (k = 0; k < npicks; k++) fprintf(stderr,"%d," , pick[k]); |
391 | fprintf(stderr,"}, " ); |
392 | fprintf(stderr, " ink0=%f, ink1=%f" , inki+inkj, ink1); |
393 | } |
394 | |
395 | gain = inki + inkj - ink1; |
396 | if (Verbose && DEBUG) fprintf(stderr, " gain=%f" , gain); |
397 | if (gain > maxgain){ |
398 | maxgain = gain; |
399 | minink = ink1; |
400 | jmax = jj; |
401 | imax = i; |
402 | if (Verbose && DEBUG) fprintf(stderr, "maxgain=%f" , maxgain); |
403 | } |
404 | if (Verbose && DEBUG) fprintf(stderr, "\n" ); |
405 | |
406 | |
407 | |
408 | } |
409 | } |
410 | |
411 | /* now merge i and jmax */ |
412 | if (maxgain > 0){ |
413 | /* a good bundling of i and another edge jmax is found */ |
414 | total_gain += maxgain; |
415 | jc = matching[jmax]; |
416 | if (jc == UNMATCHED){/* i and j both unmatched. Add j in the table first */ |
417 | if (Verbose && DEBUG) printf("maxgain=%f, merge %d with best edge: %d to form coarsen edge %d. Ink=%f\n" ,maxgain, imax, jmax, nc, minink); |
418 | matching[imax] = matching[jmax] = nc; |
419 | if (ip){ |
420 | for (k = ip[jmax]; k < ip[jmax+1]; k++) { |
421 | ie = jp[k]; |
422 | Vector_add(cedges[nc], (void*) (&ie)); |
423 | } |
424 | } else { |
425 | Vector_add(cedges[nc], (void*) (&jmax)); |
426 | } |
427 | jc = nc; |
428 | nc++; |
429 | } else {/*j is already matched */ |
430 | if (Verbose && DEBUG) printf("maxgain=%f, merge %d with existing cluster %d\n" ,maxgain, i, jc); |
431 | matching[imax] = jc; |
432 | grand_total_ink -= cinks[jc];/* ink of cluster jc was already added, and will be added again as part of a larger cluster with i, so dicount */ |
433 | } |
434 | /* add i to the appropriate table */ |
435 | if (ip){ |
436 | for (k = ip[imax]; k < ip[imax+1]; k++) { |
437 | ie = jp[k]; |
438 | Vector_add(cedges[jc], (void*) (&ie)); |
439 | } |
440 | } else { |
441 | Vector_add(cedges[jc], (void*) (&imax)); |
442 | } |
443 | cinks[jc] = minink; |
444 | grand_total_ink += minink; |
445 | grand_total_gain += maxgain; |
446 | } else {/*i can not match/bundle successfully */ |
447 | if (Verbose && DEBUG) fprintf(stderr, "no gain in bundling node %d\n" ,i); |
448 | for (i = 0; i < n; i++){ |
449 | assert(maxgain <= 0); |
450 | if (matching[i] == UNMATCHED){ |
451 | imax = i; |
452 | matching[imax] = nc; |
453 | jc = nc; |
454 | minink = inks[imax]; |
455 | nc++; |
456 | |
457 | if (ip){ |
458 | for (k = ip[imax]; k < ip[imax+1]; k++) { |
459 | ie = jp[k]; |
460 | Vector_add(cedges[jc], (void*) (&ie)); |
461 | } |
462 | } else { |
463 | Vector_add(cedges[jc], (void*) (&imax)); |
464 | } |
465 | cinks[jc] = minink; |
466 | grand_total_ink += minink; |
467 | grand_total_gain += maxgain; |
468 | |
469 | |
470 | |
471 | |
472 | } |
473 | } |
474 | } |
475 | |
476 | } while (maxgain > 0); |
477 | |
478 | |
479 | if (Verbose && DEBUG){ |
480 | fprintf(stderr," coarse edge[%d]={" ,jc); |
481 | for (k = 0; k < Vector_get_length(cedges[jc]); k++) { |
482 | fprintf(stderr,"%d," , *((int*) Vector_get(cedges[jc], k))); |
483 | } |
484 | fprintf(stderr,"}\n" ); |
485 | } |
486 | |
487 | if (nc >= 1 && total_gain > 0){ |
488 | /* now set up restriction and prolongation operator */ |
489 | SparseMatrix P, R, R1, R0, B, cA; |
490 | real one = 1.; |
491 | Agglomerative_Ink_Bundling cgrid; |
492 | |
493 | R1 = SparseMatrix_new(nc, n, 1, MATRIX_TYPE_REAL, FORMAT_COORD); |
494 | for (i = 0; i < n; i++){ |
495 | jj = matching[i]; |
496 | SparseMatrix_coordinate_form_add_entries(R1, 1, &jj, &i, &one); |
497 | } |
498 | R = SparseMatrix_from_coordinate_format(R1); |
499 | SparseMatrix_delete(R1); |
500 | P = SparseMatrix_transpose(R); |
501 | B = SparseMatrix_multiply(R, A); |
502 | if (!B) goto RETURN; |
503 | cA = SparseMatrix_multiply(B, P); |
504 | if (!cA) goto RETURN; |
505 | SparseMatrix_delete(B); |
506 | grid->P = P; |
507 | grid->R = R; |
508 | |
509 | level++; |
510 | cgrid = Agglomerative_Ink_Bundling_init(cA, edges, level); |
511 | |
512 | |
513 | /* set up R0!!! */ |
514 | if (grid->R0){ |
515 | R0 = SparseMatrix_multiply(R, grid->R0); |
516 | } else { |
517 | assert(grid->level == 0); |
518 | R0 = R; |
519 | } |
520 | cgrid->R0 = R0; |
521 | cgrid->inks = cinks; |
522 | cgrid->total_ink = grand_total_ink; |
523 | |
524 | if (Verbose > 1) fprintf(stderr,"level %d->%d, edges %d -> %d, ink %f->%f , gain = %f, or %f\n" , grid->level, cgrid->level, grid->n, |
525 | cgrid->n, grid->total_ink, grand_total_ink, grid->total_ink - grand_total_ink, grand_total_gain); |
526 | assert(ABS(grid->total_ink - cgrid->total_ink - grand_total_gain) <= 0.0001*grid->total_ink); |
527 | |
528 | cgrid = Agglomerative_Ink_Bundling_aggressive_establish(cgrid, pick, angle_param, angle); |
529 | grid->next = cgrid; |
530 | cgrid->prev = grid; |
531 | |
532 | } else { |
533 | if (Verbose > 1) fprintf(stderr,"no more improvement, orig ink = %f, gain = %f, stop and final bundling found\n" , grand_total_ink, grand_total_gain); |
534 | /* no more improvement, stop and final bundling found */ |
535 | for (i = 0; i < n; i++) matching[i] = i; |
536 | } |
537 | |
538 | RETURN: |
539 | FREE(matching); |
540 | for (i = 0; i < n; i++) Vector_delete(cedges[i]); |
541 | FREE(mask); |
542 | return grid; |
543 | } |
544 | #endif |
545 | |
546 | static Agglomerative_Ink_Bundling Agglomerative_Ink_Bundling_new(SparseMatrix A0, pedge *edges, real angle_param, real angle){ |
547 | /* give a link of edges and their nearest neighbor graph, return a multilevel of edge bundling based on ink saving */ |
548 | Agglomerative_Ink_Bundling grid; |
549 | int *pick; |
550 | SparseMatrix A = A0; |
551 | |
552 | if (!SparseMatrix_is_symmetric(A, FALSE) || A->type != MATRIX_TYPE_REAL){ |
553 | A = SparseMatrix_get_real_adjacency_matrix_symmetrized(A); |
554 | } |
555 | grid = Agglomerative_Ink_Bundling_init(A, edges, 0); |
556 | |
557 | pick = MALLOC(sizeof(int)*A0->m); |
558 | |
559 | //grid = Agglomerative_Ink_Bundling_aggressive_establish(grid, pick, angle_param, angle); |
560 | grid = Agglomerative_Ink_Bundling_establish(grid, pick, angle_param, angle); |
561 | FREE(pick); |
562 | |
563 | if (A != A0) grid->delete_top_level_A = TRUE;/* be sure to clean up later */ |
564 | |
565 | return grid; |
566 | } |
567 | |
568 | static pedge* agglomerative_ink_bundling_internal(int dim, SparseMatrix A, pedge* edges, int nneighbors, int *recurse_level, int MAX_RECURSE_LEVEL, real angle_param, real angle, int open_gl, real *current_ink, real *ink00, int *flag){ |
569 | |
570 | int i, j, jj, k; |
571 | int *ia, *ja; |
572 | int *pick; |
573 | Agglomerative_Ink_Bundling grid, cgrid; |
574 | SparseMatrix R; |
575 | real ink0, ink1; |
576 | point_t meet1, meet2; |
577 | pedge e; |
578 | real TOL = 0.0001, wgt_all; |
579 | clock_t start; |
580 | |
581 | (*recurse_level)++; |
582 | if (Verbose > 1) fprintf(stderr, "agglomerative_ink_bundling_internal, recurse level ------- %d\n" ,*recurse_level); |
583 | |
584 | assert(A->m == A->n); |
585 | |
586 | *flag = 0; |
587 | |
588 | start = clock(); |
589 | grid = Agglomerative_Ink_Bundling_new(A, edges, angle_param, angle); |
590 | if (Verbose > 1) |
591 | fprintf(stderr, "CPU for agglomerative bundling %f\n" , ((real) (clock() - start))/CLOCKS_PER_SEC); |
592 | ink0 = grid->total_ink; |
593 | |
594 | /* find coarsest */ |
595 | cgrid = grid; |
596 | while (cgrid->next){ |
597 | cgrid = cgrid->next; |
598 | } |
599 | ink1 = cgrid->total_ink; |
600 | |
601 | if (*current_ink < 0){ |
602 | *current_ink = *ink00 = ink0; |
603 | if (Verbose > 1) |
604 | fprintf(stderr,"initial total ink = %f\n" ,*current_ink); |
605 | } |
606 | if (ink1 < ink0){ |
607 | *current_ink -= ink0 - ink1; |
608 | } |
609 | |
610 | if (Verbose > 1) fprintf(stderr,"ink: %f->%f, edges: %d->%d, current ink = %f, percentage gain over original = %f\n" , ink0, ink1, grid->n, cgrid->n, *current_ink, (ink0-ink1)/(*ink00)); |
611 | |
612 | /* if no meaningful improvement (0.0001%), out, else rebundle the middle section */ |
613 | if ((ink0-ink1)/(*ink00) < 0.000001 || *recurse_level > MAX_RECURSE_LEVEL) { |
614 | /* project bundles up */ |
615 | R = cgrid->R0; |
616 | if (R){ |
617 | ia = R->ia; |
618 | ja = R->ja; |
619 | for (i = 0; i < R->m; i++){ |
620 | pick = &(ja[ia[i]]); |
621 | |
622 | if (Verbose && DEBUG) fprintf(stderr,"calling ink2...\n" ); |
623 | ink1 = ink(edges, ia[i+1]-ia[i], pick, &ink0, &meet1, &meet2, angle_param, angle); |
624 | if (Verbose && DEBUG) fprintf(stderr,"finish calling ink2...\n" ); |
625 | assert(ABS(ink1 - cgrid->inks[i])<=MAX(TOL, TOL*ink1) && ink1 - ink0 <= TOL); |
626 | wgt_all = 0.; |
627 | if (ia[i+1]-ia[i] > 1){ |
628 | for (j = ia[i]; j < ia[i+1]; j++){ |
629 | /* make this edge 4 points, insert two meeting points at 1 and 2, make 3 the last point */ |
630 | jj = ja[j]; |
631 | edges[jj] = pedge_double(edges[jj]);/* has to call pedge_double twice: from 2 points to 3 points to 5 points. The last point not used, may be |
632 | improved later */ |
633 | e = edges[jj] = pedge_double(edges[jj]); |
634 | |
635 | e->wgts = REALLOC(e->wgts, sizeof(real)*4); |
636 | e->x[1*dim] = meet1.x; |
637 | e->x[1*dim+1] = meet1.y; |
638 | e->x[2*dim] = meet2.x; |
639 | e->x[2*dim+1] = meet2.y; |
640 | e->x[3*dim] = e->x[4*dim]; |
641 | e->x[3*dim+1] = e->x[4*dim+1]; |
642 | e->npoints = 4; |
643 | for (k = 0; k < 3; k++) e->wgts[k] = e->wgt; |
644 | wgt_all += e->wgt; |
645 | |
646 | } |
647 | for (j = ia[i]; j < ia[i+1]; j++){ |
648 | e = edges[ja[j]]; |
649 | e->wgts[1] = wgt_all; |
650 | } |
651 | } |
652 | |
653 | } |
654 | } |
655 | } else { |
656 | pedge *mid_edges, midedge;/* middle section of edges that will be bundled again */ |
657 | real *xx; |
658 | int ne, npp, l; |
659 | SparseMatrix A_mid; |
660 | real eps = 0., wgt, total_wgt = 0; |
661 | |
662 | /* make new edges using meet1 and meet2. |
663 | |
664 | call Agglomerative_Ink_Bundling_new |
665 | |
666 | inherit new edges to old edges |
667 | */ |
668 | |
669 | R = cgrid->R0; |
670 | assert(R && cgrid != grid);/* if ink improved, we should have gone at leat 1 level down! */ |
671 | ia = R->ia; |
672 | ja = R->ja; |
673 | ne = R->m; |
674 | mid_edges = MALLOC(sizeof(pedge)*ne); |
675 | xx = MALLOC(sizeof(real)*4*ne); |
676 | for (i = 0; i < R->m; i++){ |
677 | pick = &(ja[ia[i]]); |
678 | wgt = 0.; |
679 | for (j = ia[i]; j < ia[i+1]; j++) wgt += edges[j]->wgt; |
680 | total_wgt += wgt; |
681 | if (Verbose && DEBUG) fprintf(stderr,"calling ink3...\n" ); |
682 | ink1 = ink(edges, ia[i+1]-ia[i], pick, &ink0, &meet1, &meet2, angle_param, angle); |
683 | if (Verbose && DEBUG) fprintf(stderr,"done calling ink3...\n" ); |
684 | assert(ABS(ink1 - cgrid->inks[i])<=MAX(TOL, TOL*ink1) && ink1 - ink0 <= TOL); |
685 | xx[i*4 + 0] = meet1.x; |
686 | xx[i*4 + 1] = meet1.y; |
687 | xx[i*4 + 2] = meet2.x; |
688 | xx[i*4 + 3] = meet2.y; |
689 | mid_edges[i] = pedge_wgt_new(2, dim, &(xx[i*4]), wgt); |
690 | //mid_edges[i] = pedge_wgt_new(2, dim, &(xx[i*4]), 1.); |
691 | |
692 | } |
693 | |
694 | A_mid = nearest_neighbor_graph(ne, MIN(nneighbors, ne), 4, xx, eps); |
695 | |
696 | agglomerative_ink_bundling_internal(dim, A_mid, mid_edges, nneighbors, recurse_level, MAX_RECURSE_LEVEL, angle_param, angle, open_gl, current_ink, ink00, flag); |
697 | SparseMatrix_delete(A_mid); |
698 | FREE(xx); |
699 | |
700 | /* patching edges with the new mid-section */ |
701 | for (i = 0; i < R->m; i++){ |
702 | pick = &(ja[ia[i]]); |
703 | midedge = mid_edges[i]; |
704 | npp = midedge->npoints + 2; |
705 | for (j = ia[i]; j < ia[i+1]; j++){ |
706 | jj = ja[j]; |
707 | e = edges[jj] = pedge_wgts_realloc(edges[jj], npp); |
708 | |
709 | assert(e->npoints = 2); |
710 | for (l = 0; l < dim; l++){/* move the second point to the last */ |
711 | e->x[(npp - 1)*dim+l] = e->x[1*dim+l]; |
712 | } |
713 | |
714 | for (k = 0; k < midedge->npoints; k++){ |
715 | for (l = 0; l < dim; l++){ |
716 | e->x[(k+1)*dim+l] = midedge->x[k*dim+l]; |
717 | } |
718 | if (k < midedge->npoints - 1){ |
719 | if (midedge->wgts){ |
720 | e->wgts[(k+1)] = midedge->wgts[k]; |
721 | } else { |
722 | e->wgts[(k+1)] = midedge->wgt; |
723 | } |
724 | } |
725 | } |
726 | e->wgts[npp - 2] = e->wgts[0];/* the last interval take from the 1st interval */ |
727 | |
728 | |
729 | e->npoints = npp; |
730 | } |
731 | #ifdef OPENGL |
732 | if (open_gl & FALSE){ |
733 | nedges_global = grid->n; |
734 | edges_global = edges; |
735 | drawScene(); |
736 | // waitie(5./R->m); |
737 | } |
738 | #endif |
739 | } |
740 | |
741 | for (i = 0; i < ne; i++) pedge_delete(mid_edges[i]); |
742 | |
743 | } |
744 | |
745 | |
746 | #ifdef OPENGL |
747 | if (open_gl){ |
748 | nedges_global = grid->n; |
749 | edges_global = edges; |
750 | drawScene(); |
751 | // waitie(5./R->m); |
752 | } |
753 | #endif |
754 | |
755 | Agglomerative_Ink_Bundling_delete(grid); |
756 | |
757 | return edges; |
758 | } |
759 | |
760 | |
761 | pedge* agglomerative_ink_bundling(int dim, SparseMatrix A, pedge* edges, int nneighbor, int MAX_RECURSE_LEVEL, real angle_param, real angle, int open_gl, int *flag){ |
762 | int recurse_level = 0; |
763 | real current_ink = -1, ink0; |
764 | pedge *edges2; |
765 | |
766 | ink_count = 0; |
767 | edges2 = agglomerative_ink_bundling_internal(dim, A, edges, nneighbor, &recurse_level, MAX_RECURSE_LEVEL, angle_param, angle, open_gl, ¤t_ink, &ink0, flag); |
768 | |
769 | |
770 | if (Verbose > 1) |
771 | fprintf(stderr,"initial total ink = %f, final total ink = %f, inksaving = %f percent, total ink_calc = %f, avg ink_calc per edge = %f\n" , ink0, current_ink, (ink0-current_ink)/ink0, ink_count, ink_count/(real) A->m); |
772 | return edges2; |
773 | } |
774 | |
775 | |