PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/graphviz-cmake/lib/neatogen/constrained_majorization.c

https://bitbucket.org/akristmann/custom_build
C | 496 lines | 363 code | 54 blank | 79 comment | 84 complexity | c09556d3c180f5d9d7abf3b406e460fe MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, EPL-1.0, CPL-1.0, BSD-3-Clause, LGPL-2.1
  1. /* $Id: constrained_majorization.c,v 1.11 2011/01/25 16:30:49 ellson Exp $ $Revision: 1.11 $ */
  2. /* vim:set shiftwidth=4 ts=8: */
  3. /*************************************************************************
  4. * Copyright (c) 2011 AT&T Intellectual Property
  5. * All rights reserved. This program and the accompanying materials
  6. * are made available under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution, and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors: See CVS logs. Details at http://www.graphviz.org/
  11. *************************************************************************/
  12. #include "digcola.h"
  13. #ifdef DIGCOLA
  14. #include <math.h>
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <stdio.h>
  18. #include <float.h>
  19. #include "stress.h"
  20. #include "dijkstra.h"
  21. #include "bfs.h"
  22. #include "matrix_ops.h"
  23. #include "kkutils.h"
  24. #include "conjgrad.h"
  25. #include "quad_prog_solver.h"
  26. #include "matrix_ops.h"
  27. #define localConstrMajorIterations 15
  28. #define levels_sep_tol 1e-1
  29. int
  30. stress_majorization_with_hierarchy(
  31. vtx_data* graph, /* Input graph in sparse representation */
  32. int n, /* Number of nodes */
  33. int nedges_graph, /* Number of edges */
  34. double** d_coords, /* Coordinates of nodes (output layout) */
  35. node_t** nodes, /* Original nodes */
  36. int dim, /* Dimemsionality of layout */
  37. int smart_ini, /* smart initialization */
  38. int model, /* difference model */
  39. int maxi, /* max iterations */
  40. double levels_gap
  41. )
  42. {
  43. int iterations = 0; /* Output: number of iteration of the process */
  44. /*************************************************
  45. ** Computation of full, dense, unrestricted k-D **
  46. ** stress minimization by majorization **
  47. ** This function imposes HIERARCHY CONSTRAINTS **
  48. *************************************************/
  49. int i,j,k;
  50. boolean directionalityExist = FALSE;
  51. float * lap1 = NULL;
  52. float * dist_accumulator = NULL;
  53. float * tmp_coords = NULL;
  54. float ** b = NULL;
  55. #ifdef NONCORE
  56. FILE * fp=NULL;
  57. #endif
  58. double * degrees = NULL;
  59. float * lap2=NULL;
  60. int lap_length;
  61. float * f_storage=NULL;
  62. float ** coords=NULL;
  63. double conj_tol=tolerance_cg; /* tolerance of Conjugate Gradient */
  64. float ** unpackedLap = NULL;
  65. CMajEnv *cMajEnv = NULL;
  66. double y_0;
  67. int length;
  68. DistType diameter;
  69. float * Dij=NULL;
  70. /* to compensate noises, we never consider gaps smaller than 'abs_tol' */
  71. double abs_tol=1e-2;
  72. /* Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap> */
  73. double relative_tol=levels_sep_tol;
  74. int *ordering=NULL, *levels=NULL;
  75. float constant_term;
  76. int count;
  77. double degree;
  78. int step;
  79. float val;
  80. double old_stress, new_stress;
  81. boolean converged;
  82. int len;
  83. int num_levels;
  84. float *hierarchy_boundaries;
  85. if (graph[0].edists!=NULL) {
  86. for (i=0; i<n; i++) {
  87. for (j=1; j<graph[i].nedges; j++) {
  88. directionalityExist = directionalityExist || (graph[i].edists[j]!=0);
  89. }
  90. }
  91. }
  92. if (!directionalityExist) {
  93. return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, nodes, dim, smart_ini, model, maxi);
  94. }
  95. /******************************************************************
  96. ** First, partition nodes into layers: These are our constraints **
  97. ******************************************************************/
  98. if (smart_ini) {
  99. double* x;
  100. double* y;
  101. if (dim>2) {
  102. /* the dim==2 case is handled below */
  103. stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords+1, nodes, dim-1, smart_ini, model, 15);
  104. /* now copy the y-axis into the (dim-1)-axis */
  105. for (i=0; i<n; i++) {
  106. d_coords[dim-1][i] = d_coords[1][i];
  107. }
  108. }
  109. x = d_coords[0]; y = d_coords[1];
  110. compute_y_coords(graph, n, y, n);
  111. compute_hierarchy(graph, n, abs_tol, relative_tol, y, &ordering, &levels, &num_levels);
  112. if (num_levels<=1) {
  113. /* no hierarchy found, use faster algorithm */
  114. return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, nodes, dim, smart_ini, model, maxi);
  115. }
  116. if (levels_gap>0) {
  117. /* ensure that levels are separated in the initial layout */
  118. double displacement = 0;
  119. int stop;
  120. for (i=0; i<num_levels; i++) {
  121. displacement+=MAX((double)0,levels_gap-(y[ordering[levels[i]]]+displacement-y[ordering[levels[i]-1]]));
  122. stop = i<num_levels-1 ? levels[i+1] : n;
  123. for (j=levels[i]; j<stop; j++) {
  124. y[ordering[j]] += displacement;
  125. }
  126. }
  127. }
  128. if (dim==2) {
  129. IMDS_given_dim(graph, n, y, x, Epsilon);
  130. }
  131. }
  132. else {
  133. initLayout(graph, n, dim, d_coords, nodes);
  134. compute_hierarchy(graph, n, abs_tol, relative_tol, NULL, &ordering, &levels, &num_levels);
  135. }
  136. if (n == 1) return 0;
  137. hierarchy_boundaries = N_GNEW(num_levels, float);
  138. /****************************************************
  139. ** Compute the all-pairs-shortest-distances matrix **
  140. ****************************************************/
  141. if (maxi==0)
  142. return iterations;
  143. if (Verbose)
  144. start_timer();
  145. if (model == MODEL_SUBSET) {
  146. /* weight graph to separate high-degree nodes */
  147. /* and perform slower Dijkstra-based computation */
  148. if (Verbose)
  149. fprintf(stderr, "Calculating subset model");
  150. Dij = compute_apsp_artifical_weights_packed(graph, n);
  151. } else if (model == MODEL_CIRCUIT) {
  152. Dij = circuitModel(graph, n);
  153. if (!Dij) {
  154. agerr(AGWARN,
  155. "graph is disconnected. Hence, the circuit model\n");
  156. agerr(AGPREV,
  157. "is undefined. Reverting to the shortest path model.\n");
  158. }
  159. } else if (model == MODEL_MDS) {
  160. if (Verbose)
  161. fprintf(stderr, "Calculating MDS model");
  162. Dij = mdsModel(graph, n);
  163. }
  164. if (!Dij) {
  165. if (Verbose)
  166. fprintf(stderr, "Calculating shortest paths");
  167. Dij = compute_apsp_packed(graph, n);
  168. }
  169. if (Verbose) {
  170. fprintf(stderr, ": %.2f sec\n", elapsed_sec());
  171. fprintf(stderr, "Setting initial positions");
  172. start_timer();
  173. }
  174. diameter=-1;
  175. length = n+n*(n-1)/2;
  176. for (i=0; i<length; i++) {
  177. if (Dij[i]>diameter) {
  178. diameter = (int)Dij[i];
  179. }
  180. }
  181. if (!smart_ini) {
  182. /* for numerical stability, scale down layout */
  183. /* No Jiggling, might conflict with constraints */
  184. double max=1;
  185. for (i=0; i<dim; i++) {
  186. for (j=0; j<n; j++) {
  187. if (fabs(d_coords[i][j])>max) {
  188. max=fabs(d_coords[i][j]);
  189. }
  190. }
  191. }
  192. for (i=0; i<dim; i++) {
  193. for (j=0; j<n; j++) {
  194. d_coords[i][j]*=10/max;
  195. }
  196. }
  197. }
  198. if (levels_gap>0) {
  199. int length = n+n*(n-1)/2;
  200. double sum1, sum2, scale_ratio;
  201. int count;
  202. sum1=(float)(n*(n-1)/2);
  203. sum2=0;
  204. for (count=0, i=0; i<n-1; i++) {
  205. count++; // skip self distance
  206. for (j=i+1; j<n; j++,count++) {
  207. sum2+=distance_kD(d_coords, dim, i, j)/Dij[count];
  208. }
  209. }
  210. scale_ratio=sum2/sum1;
  211. /* double scale_ratio=10; */
  212. for (i=0; i<length; i++) {
  213. Dij[i]*=(float)scale_ratio;
  214. }
  215. }
  216. /**************************
  217. ** Layout initialization **
  218. **************************/
  219. for (i=0; i<dim; i++) {
  220. orthog1(n, d_coords[i]);
  221. }
  222. /* for the y-coords, don't center them, but translate them so y[0]=0 */
  223. y_0 = d_coords[1][0];
  224. for (i=0; i<n; i++) {
  225. d_coords[1][i] -= y_0;
  226. }
  227. coords = N_GNEW(dim, float*);
  228. f_storage = N_GNEW(dim*n, float);
  229. for (i=0; i<dim; i++) {
  230. coords[i] = f_storage+i*n;
  231. for (j=0; j<n; j++) {
  232. coords[i][j] = (float)(d_coords[i][j]);
  233. }
  234. }
  235. /* compute constant term in stress sum
  236. * which is \sum_{i<j} w_{ij}d_{ij}^2
  237. */
  238. constant_term=(float)(n*(n-1)/2);
  239. if (Verbose) fprintf(stderr, ": %.2f sec", elapsed_sec());
  240. /**************************
  241. ** Laplacian computation **
  242. **************************/
  243. lap2 = Dij;
  244. lap_length = n+n*(n-1)/2;
  245. square_vec(lap_length, lap2);
  246. /* compute off-diagonal entries */
  247. invert_vec(lap_length, lap2);
  248. /* compute diagonal entries */
  249. count=0;
  250. degrees = N_GNEW(n, double);
  251. set_vector_val(n, 0, degrees);
  252. for (i=0; i<n-1; i++) {
  253. degree=0;
  254. count++; // skip main diag entry
  255. for (j=1; j<n-i; j++,count++) {
  256. val = lap2[count];
  257. degree+=val; degrees[i+j]-=val;
  258. }
  259. degrees[i]-=degree;
  260. }
  261. for (step=n,count=0,i=0; i<n; i++,count+=step,step--) {
  262. lap2[count]=(float)degrees[i];
  263. }
  264. #ifdef NONCORE
  265. fpos_t pos;
  266. if (n>max_nodes_in_mem) {
  267. #define FILENAME "tmp_Dij$$$.bin"
  268. fp = fopen(FILENAME, "wb");
  269. fwrite(lap2, sizeof(float), lap_length, fp);
  270. fclose(fp);
  271. fp = NULL;
  272. }
  273. #endif
  274. /*************************
  275. ** Layout optimization **
  276. *************************/
  277. b = N_GNEW (dim, float*);
  278. b[0] = N_GNEW (dim*n, float);
  279. for (k=1; k<dim; k++) {
  280. b[k] = b[0]+k*n;
  281. }
  282. tmp_coords = N_GNEW(n, float);
  283. dist_accumulator = N_GNEW(n, float);
  284. #ifdef NONCORE
  285. if (n<=max_nodes_in_mem) {
  286. #endif
  287. lap1 = N_GNEW(lap_length, float);
  288. #ifdef NONCORE
  289. }
  290. else {
  291. lap1=lap2;
  292. fp = fopen(FILENAME, "rb");
  293. fgetpos(fp, &pos);
  294. }
  295. #endif
  296. old_stress=DBL_MAX; /* at least one iteration */
  297. unpackedLap = unpackMatrix(lap2, n);
  298. cMajEnv=initConstrainedMajorization(lap2, n, ordering, levels, num_levels);
  299. for (converged=FALSE,iterations=0; iterations<maxi && !converged; iterations++) {
  300. /* First, construct Laplacian of 1/(d_ij*|p_i-p_j|) */
  301. set_vector_val(n, 0, degrees);
  302. #ifdef NONCORE
  303. if (n<=max_nodes_in_mem) {
  304. #endif
  305. sqrt_vecf(lap_length, lap2, lap1);
  306. #ifdef NONCORE
  307. }
  308. else {
  309. sqrt_vec(lap_length, lap1);
  310. }
  311. #endif
  312. for (count=0,i=0; i<n-1; i++) {
  313. len = n-i-1;
  314. /* init 'dist_accumulator' with zeros */
  315. set_vector_valf(n, 0, dist_accumulator);
  316. /* put into 'dist_accumulator' all squared distances
  317. * between 'i' and 'i'+1,...,'n'-1
  318. */
  319. for (k=0; k<dim; k++) {
  320. set_vector_valf(len, coords[k][i], tmp_coords);
  321. vectors_mult_additionf(len, tmp_coords, -1, coords[k]+i+1);
  322. square_vec(len, tmp_coords);
  323. vectors_additionf(len, tmp_coords, dist_accumulator, dist_accumulator);
  324. }
  325. /* convert to 1/d_{ij} */
  326. invert_sqrt_vec(len, dist_accumulator);
  327. /* detect overflows */
  328. for (j=0; j<len; j++) {
  329. if (dist_accumulator[j]>=FLT_MAX || dist_accumulator[j]<0) {
  330. dist_accumulator[j]=0;
  331. }
  332. }
  333. count++; /* save place for the main diagonal entry */
  334. degree=0;
  335. for (j=0; j<len; j++,count++) {
  336. val=lap1[count]*=dist_accumulator[j];
  337. degree+=val; degrees[i+j+1]-=val;
  338. }
  339. degrees[i]-=degree;
  340. }
  341. for (step=n,count=0,i=0; i<n; i++,count+=step,step--) {
  342. lap1[count]=(float)degrees[i];
  343. }
  344. /* Now compute b[] (L^(X(t))*X(t)) */
  345. for (k=0; k<dim; k++) {
  346. /* b[k] := lap1*coords[k] */
  347. right_mult_with_vector_ff(lap1, n, coords[k], b[k]);
  348. }
  349. /* compute new stress
  350. * remember that the Laplacians are negated, so we subtract
  351. * instead of add and vice versa
  352. */
  353. new_stress=0;
  354. for (k=0; k<dim; k++) {
  355. new_stress+=vectors_inner_productf(n, coords[k], b[k]);
  356. }
  357. new_stress*=2;
  358. new_stress+=constant_term; // only after mult by 2
  359. #ifdef NONCORE
  360. if (n>max_nodes_in_mem) {
  361. /* restore lap2 from disk */
  362. fsetpos(fp, &pos);
  363. fread(lap2, sizeof(float), lap_length, fp);
  364. }
  365. #endif
  366. for (k=0; k<dim; k++) {
  367. right_mult_with_vector_ff(lap2, n, coords[k], tmp_coords);
  368. new_stress-=vectors_inner_productf(n, coords[k], tmp_coords);
  369. }
  370. #ifdef ALTERNATIVE_STRESS_CALC
  371. {
  372. double mat_stress=new_stress;
  373. double compute_stress(float ** coords, float * lap, int dim, int n);
  374. new_stress = compute_stress(coords, lap2, dim, n);
  375. if (fabs(mat_stress-new_stress)/min(mat_stress,new_stress)>0.001) {
  376. fprintf(stderr,"Diff stress vals: %lf %lf (iteration #%d)\n", mat_stress, new_stress,iterations);
  377. }
  378. }
  379. #endif
  380. /* check for convergence */
  381. converged = fabs(new_stress-old_stress)/fabs(old_stress+1e-10) < Epsilon;
  382. converged = converged || (iterations>1 && new_stress>old_stress);
  383. /* in first iteration we allowed stress increase, which
  384. * might result ny imposing constraints
  385. */
  386. old_stress = new_stress;
  387. for (k=0; k<dim; k++) {
  388. /* now we find the optimizer of trace(X'LX)+X'B by solving 'dim'
  389. * system of equations, thereby obtaining the new coordinates.
  390. * If we use the constraints (given by the var's: 'ordering',
  391. * 'levels' and 'num_levels'), we cannot optimize
  392. * trace(X'LX)+X'B by simply solving equations, but we have
  393. * to use a quadratic programming solver
  394. * note: 'lap2' is a packed symmetric matrix, that is its
  395. * upper-triangular part is arranged in a vector row-wise
  396. * also note: 'lap2' is really the negated laplacian (the
  397. * laplacian is -'lap2')
  398. */
  399. if (k==1) {
  400. /* use quad solver in the y-dimension */
  401. constrained_majorization_new_with_gaps(cMajEnv, b[k], coords, dim, k, localConstrMajorIterations, hierarchy_boundaries, levels_gap);
  402. }
  403. else {
  404. /* use conjugate gradient for all dimensions except y */
  405. conjugate_gradient_mkernel(lap2, coords[k], b[k], n, conj_tol, n);
  406. }
  407. }
  408. }
  409. free (hierarchy_boundaries);
  410. deleteCMajEnv(cMajEnv);
  411. if (coords!=NULL) {
  412. for (i=0; i<dim; i++) {
  413. for (j=0; j<n; j++) {
  414. d_coords[i][j] = coords[i][j];
  415. }
  416. }
  417. free (coords[0]); free (coords);
  418. }
  419. if (b) {
  420. free (b[0]); free (b);
  421. }
  422. free (tmp_coords);
  423. free (dist_accumulator);
  424. free (degrees);
  425. free (lap2);
  426. #ifdef NONCORE
  427. if (n<=max_nodes_in_mem) {
  428. #endif
  429. free (lap1);
  430. #ifdef NONCORE
  431. }
  432. if (fp)
  433. fclose (fp);
  434. #endif
  435. free (ordering);
  436. free (levels);
  437. if (unpackedLap) {
  438. free (unpackedLap[0]); free (unpackedLap);
  439. }
  440. return iterations;
  441. }
  442. #endif /* DIGCOLA */