Graphviz 16.1.1~dev.20260926.2046
Loading...
Searching...
No Matches
smart_ini_x.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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11#include "config.h"
12
13#include <float.h>
14#include <math.h>
15#include <neatogen/digcola.h>
16#include <util/alloc.h>
17#include <util/gv_math.h>
18#ifdef DIGCOLA
19#include <neatogen/kkutils.h>
20#include <neatogen/matrix_ops.h>
21#include <neatogen/conjgrad.h>
22#include <stdbool.h>
23
24static void
25standardize(double* orthog, int nvtxs)
26{
27 double len, avg = 0;
28 int i;
29 for (i=0; i<nvtxs; i++)
30 avg+=orthog[i];
31 avg/=nvtxs;
32
33 /* centralize: */
34 for (i=0; i<nvtxs; i++)
35 orthog[i]-=avg;
36
37 /* normalize: */
38 len = norm(orthog, nvtxs-1);
39
40 // if we have a degenerate length, do not attempt to scale by it
41 if (fabs(len) < DBL_EPSILON) {
42 return;
43 }
44
45 vectors_scalar_mult(nvtxs, orthog, 1.0 / len, orthog);
46}
47
48static void
49mat_mult_vec_orthog(float** mat, int dim1, int dim2, double* vec,
50 double* result, double* orthog)
51{
52 /* computes mat*vec, where mat is a dim1*dim2 matrix */
53 int i,j;
54 double sum;
55
56 for (i=0; i<dim1; i++) {
57 sum=0;
58 for (j=0; j<dim2; j++) {
59 sum += mat[i][j]*vec[j];
60 }
61 result[i]=sum;
62 }
63 assert(orthog != NULL);
64 double alpha = -vectors_inner_product(dim1, result, orthog);
65 scadd(result, dim1 - 1, alpha, orthog);
66}
67
68static void
69power_iteration_orthog(float** square_mat, int n, int neigs,
70 double** eigs, double* evals, double* orthog, double p_iteration_threshold)
71{
72 // Power-Iteration with
73 // (I - orthog × orthogᵀ) × square_mat × (I - orthog × orthogᵀ)
74
75 int i,j;
76 double *tmp_vec = gv_calloc(n, sizeof(double));
77 double *last_vec = gv_calloc(n, sizeof(double));
78 double *curr_vector;
79 double len;
80 double angle;
81 double alpha;
82 int largest_index;
83 double largest_eval;
84
86
87 if (neigs>=n) {
88 neigs=n;
89 }
90
91 for (i=0; i<neigs; i++) {
92 curr_vector = eigs[i];
93 /* guess the i-th eigen vector */
94choose:
95 for (j=0; j<n; j++) {
96 curr_vector[j] = rand()%100;
97 }
98
99 assert(orthog != NULL);
100 alpha = -vectors_inner_product(n, orthog, curr_vector);
101 scadd(curr_vector, n - 1, alpha, orthog);
102 // orthogonalize against higher eigenvectors
103 for (j=0; j<i; j++) {
104 alpha = -vectors_inner_product(n, eigs[j], curr_vector);
105 scadd(curr_vector, n-1, alpha, eigs[j]);
106 }
107 len = norm(curr_vector, n-1);
108 if (len<1e-10) {
109 /* We have chosen a vector colinear with prvious ones */
110 goto choose;
111 }
112 vectors_scalar_mult(n, curr_vector, 1.0 / len, curr_vector);
113 do {
114 copy_vector(n, curr_vector, last_vec);
115
116 mat_mult_vec_orthog(square_mat,n,n,curr_vector,tmp_vec,orthog);
117 copy_vector(n, tmp_vec, curr_vector);
118
119 /* orthogonalize against higher eigenvectors */
120 for (j=0; j<i; j++) {
121 alpha = -vectors_inner_product(n, eigs[j], curr_vector);
122 scadd(curr_vector, n-1, alpha, eigs[j]);
123 }
124 len = norm(curr_vector, n-1);
125 if (len<1e-10) {
126 /* We have reached the null space (e.vec. associated
127 * with e.val. 0)
128 */
129 goto exit;
130 }
131
132 vectors_scalar_mult(n, curr_vector, 1.0 / len, curr_vector);
133 angle = vectors_inner_product(n, curr_vector, last_vec);
134 } while (fabs(angle)<tol);
135 /* the Rayleigh quotient (up to errors due to orthogonalization):
136 * u*(A*u)/||A*u||)*||A*u||, where u=last_vec, and ||u||=1
137 */
138 evals[i]=angle*len;
139 }
140exit:
141 for (; i<neigs; i++) {
142 /* compute the smallest eigenvector, which are
143 * probably associated with eigenvalue 0 and for
144 * which power-iteration is dangerous
145 */
146 curr_vector = eigs[i];
147 /* guess the i-th eigen vector */
148 for (j=0; j<n; j++)
149 curr_vector[j] = rand()%100;
150 /* orthogonalize against higher eigenvectors */
151 for (j=0; j<i; j++) {
152 alpha = -vectors_inner_product(n, eigs[j], curr_vector);
153 scadd(curr_vector, n-1, alpha, eigs[j]);
154 }
155 len = norm(curr_vector, n-1);
156 vectors_scalar_mult(n, curr_vector, 1.0 / len, curr_vector);
157 evals[i]=0;
158
159 }
160
161 /* sort vectors by their evals, for overcoming possible mis-convergence: */
162 for (i=0; i<neigs-1; i++) {
163 largest_index=i;
164 largest_eval=evals[largest_index];
165 for (j=i+1; j<neigs; j++) {
166 if (largest_eval<evals[j]) {
167 largest_index=j;
168 largest_eval=evals[largest_index];
169 }
170 }
171 if (largest_index!=i) { // exchange eigenvectors:
172 copy_vector(n, eigs[i], tmp_vec);
173 copy_vector(n, eigs[largest_index], eigs[i]);
174 copy_vector(n, tmp_vec, eigs[largest_index]);
175
176 evals[largest_index]=evals[i];
177 evals[i]=largest_eval;
178 }
179 }
180
181 free (tmp_vec); free (last_vec);
182
183}
184
185static float*
186compute_avgs(DistType** Dij, int n, float* all_avg)
187{
188 float* row_avg = gv_calloc(n, sizeof(float));
189 int i,j;
190 double sum=0, sum_row;
191
192 for (i=0; i<n; i++) {
193 sum_row=0;
194 for (j=0; j<n; j++) {
195 sum+=(double)Dij[i][j]*(double)Dij[i][j];
196 sum_row+=(double)Dij[i][j]*(double)Dij[i][j];
197 }
198 row_avg[i]=(float)sum_row/n;
199 }
200 *all_avg=(float)sum/(n*n);
201 return row_avg;
202}
203
204static float**
205compute_Bij(DistType** Dij, int n)
206{
207 int i,j;
208 float *storage = gv_calloc(n * n, sizeof(float));
209 float **Bij = gv_calloc(n, sizeof(float *));
210 float* row_avg;
211 float all_avg;
212
213 for (i=0; i<n; i++)
214 Bij[i] = storage+i*n;
215
216 row_avg = compute_avgs(Dij, n, &all_avg);
217 for (i=0; i<n; i++) {
218 for (j=0; j<=i; j++) {
219 Bij[i][j]=-(float)Dij[i][j]*Dij[i][j]+row_avg[i]+row_avg[j]-all_avg;
220 Bij[j][i]=Bij[i][j];
221 }
222 }
223 free (row_avg);
224 return Bij;
225}
226
227static void
228CMDS_orthog(int n, int dim, double** eigs, double tol,
229 double* orthog, DistType** Dij)
230{
231 int i,j;
232 float** Bij = compute_Bij(Dij, n);
233 double *evals = gv_calloc(dim, sizeof(double));
234
235 assert(orthog != NULL);
236 double *orthog_aux = gv_calloc(n, sizeof(double));
237 for (i=0; i<n; i++) {
238 orthog_aux[i]=orthog[i];
239 }
240 standardize(orthog_aux,n);
241 power_iteration_orthog(Bij, n, dim, eigs, evals, orthog_aux, tol);
242
243 for (i=0; i<dim; i++) {
244 for (j=0; j<n; j++) {
245 eigs[i][j]*=sqrt(fabs(evals[i]));
246 }
247 }
248 free (Bij[0]); free (Bij);
249 free (evals); free (orthog_aux);
250}
251
252#define SCALE_FACTOR 256
253
254int IMDS_given_dim(vtx_data* graph, int n, double* given_coords,
255 double* new_coords, double conj_tol)
256{
257 int iterations2;
258 int rv = 0;
259 double* x = given_coords;
260 double uniLength;
261 double* y = new_coords;
262 double **const lap = gv_calloc(n, sizeof(double *));
263 double *balance = gv_calloc(n, sizeof(double));
264 bool converged;
265
266 DistType **const Dij = compute_apsp(graph, n);
267
268 /* scaling up the distances to enable an 'sqrt' operation later
269 * (in case distances are integers)
270 */
271 for (int i = 0; i < n; i++)
272 for (int j = 0; j < n; j++)
273 Dij[i][j]*=SCALE_FACTOR;
274
275 assert(x!=NULL);
276 {
277 double sum1 = 0;
278 double sum2 = 0;
279
280 for (int i = 1; i < n; i++) {
281 for (int j = 0; j < i; j++) {
282 sum1+=1.0/(Dij[i][j])*fabs(x[i]-x[j]);
283 sum2+=1.0/(Dij[i][j]*Dij[i][j])*fabs(x[i]-x[j])*fabs(x[i]-x[j]);
284 }
285 }
286 uniLength = isinf(sum2) ? 0 : sum1 / sum2;
287 for (int i = 0; i < n; i++)
288 x[i]*=uniLength;
289 }
290
291 /* smart ini: */
292 CMDS_orthog(n, 1, &y, conj_tol, x, Dij);
293
294 /* Compute Laplacian: */
295 double *f_storage = gv_calloc(n * n, sizeof(double));
296
297 for (int i = 0; i < n; i++) {
298 lap[i]=f_storage+i*n;
299 double degree = 0;
300 for (int j = 0; j < n; j++) {
301 if (j==i)
302 continue;
303 degree -= lap[i][j] = -1.0 / ((double)Dij[i][j] * Dij[i][j]); // w_{ij}
304
305 }
306 lap[i][i]=degree;
307 }
308
309
310 /* compute residual distances */
311 /* if (x!=NULL) */
312 {
313 double diff;
314 for (int i = 1; i < n; i++) {
315 const double pos_i = x[i];
316 for (int j = 0; j < i; j++) {
317 diff=(double)Dij[i][j]*(double)Dij[i][j]-(pos_i-x[j])*(pos_i-x[j]);
318 Dij[i][j]=Dij[j][i]=diff>0 ? (DistType)sqrt(diff) : 0;
319 }
320 }
321 }
322
323 /* Compute the balance vector: */
324 for (int i = 0; i < n; i++) {
325 const double pos_i = y[i];
326 balance[i]=0;
327 for (int j = 0; j < n; j++) {
328 if (j==i)
329 continue;
330 if (pos_i>=y[j]) {
331 balance[i]+=Dij[i][j]*(-lap[i][j]); // w_{ij}*delta_{ij}
332 }
333 else {
334 balance[i]-=Dij[i][j]*(-lap[i][j]); // w_{ij}*delta_{ij}
335 }
336 }
337 }
338
339 for (converged=false,iterations2=0; iterations2<200 && !converged; iterations2++) {
340 if (conjugate_gradient_d(lap, y, balance, n, conj_tol, n, true) < 0) {
341 rv = 1;
342 goto cleanup;
343 }
344 converged = true;
345 for (int i = 0; i < n; i++) {
346 const double pos_i = y[i];
347 double b = 0;
348 for (int j = 0; j < n; j++) {
349 if (j==i)
350 continue;
351 if (pos_i>=y[j]) {
352 b+=Dij[i][j]*(-lap[i][j]);
353
354 }
355 else {
356 b-=Dij[i][j]*(-lap[i][j]);
357
358 }
359 }
360 if (!is_exactly_zero(balance[i]) && !is_exactly_equal(balance[i], -0.0) &&
361 fabs(1 - b / balance[i]) > 1e-5) {
362 converged = false;
363 balance[i]=b;
364 }
365 }
366 }
367
368 for (int i = 0; !(fabs(uniLength) < DBL_EPSILON) && i < n; i++) {
369 x[i] /= uniLength;
370 y[i] /= uniLength;
371 }
372
373cleanup:
374
375 free (Dij[0]); free (Dij);
376 free (lap[0]); free (lap);
377 free (balance);
378 return rv;
379}
380
381#endif /* DIGCOLA */
382
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
int conjugate_gradient_d(double **A, double *x, double *b, int n, double tol, int max_iterations, bool ortho1)
Definition conjgrad.c:92
static double norm(int n, const double *x)
static double len(glCompPoint p)
Definition glutils.c:138
static void cleanup(void)
Definition gmlparse.c:130
void free(void *)
node NULL
Definition grammar.y:181
Agraph_t * graph(char *name)
Definition gv.cpp:34
Arithmetic helper functions.
static bool is_exactly_zero(double v)
is a value precisely 0.0?
Definition gv_math.h:70
static bool is_exactly_equal(double a, double b)
are two values precisely the same?
Definition gv_math.h:51
DistType ** compute_apsp(vtx_data *graph, int n)
Definition kkutils.c:85
double vectors_inner_product(int n, const double *vector1, const double *vector2)
Definition matrix_ops.c:270
void copy_vector(int n, const double *restrict source, double *restrict dest)
Definition matrix_ops.c:264
void scadd(double *vec1, int end, double fac, double *vec2)
Definition matrix_ops.c:188
static const double p_iteration_threshold
Definition matrix_ops.c:20
void vectors_scalar_mult(int n, const double *vector, double alpha, double *result)
Definition matrix_ops.c:256
static const int dim
#define alpha
Definition shapes.c:4034
int DistType
Definition sparsegraph.h:39
static const double tol