/* * Set up environment and global constraints (dir-edge constraints, containment constraints * etc). * * diredges: 0=no dir edge constraints * 1=one separation constraint for each edge (in acyclic subgraph) * 2=DiG-CoLa level constraints */ CMajEnvVPSC *initCMajVPSC(int n, float *packedMat, vtx_data * graph, ipsep_options * opt, int diredges) { int i, j; /* nv is the number of real nodes */ int nConCs; /* fprintf(stderr,"Entered initCMajVPSC\n"); */ CMajEnvVPSC *e = GNEW(CMajEnvVPSC); e->A = NULL; e->packedMat = packedMat; /* if we have clusters then we'll need two constraints for each var in * a cluster */ e->nldv = 2 * opt->clusters->nclusters; e->nv = n - e->nldv; e->ndv = 0; e->gcs = NULL; e->vs = N_GNEW(n, Variable *); for (i = 0; i < n; i++) { e->vs[i] = newVariable(i, 1.0, 1.0); } e->gm = 0; if (diredges == 1) { if (Verbose) fprintf(stderr, " generate edge constraints...\n"); for (i = 0; i < e->nv; i++) { for (j = 1; j < graph[i].nedges; j++) { /* fprintf(stderr,"edist=%f\n",graph[i].edists[j]); */ if (graph[i].edists[j] > 0.01) { e->gm++; } } } e->gcs = newConstraints(e->gm); e->gm = 0; for (i = 0; i < e->nv; i++) { for (j = 1; j < graph[i].nedges; j++) { int u = i, v = graph[i].edges[j]; if (graph[i].edists[j] > 0) { e->gcs[e->gm++] = newConstraint(e->vs[u], e->vs[v], opt->edge_gap); } } } } else if (diredges == 2) { int *ordering = NULL, *ls = NULL, cvar; double halfgap; DigColaLevel *levels; Variable **vs = e->vs; /* e->ndv is the number of dummy variables required, one for each boundary */ if (compute_hierarchy(graph, e->nv, 1e-2, 1e-1, NULL, &ordering, &ls, &e->ndv)) return NULL; levels = assign_digcola_levels(ordering, e->nv, ls, e->ndv); if (Verbose) fprintf(stderr, "Found %d DiG-CoLa boundaries\n", e->ndv); e->gm = get_num_digcola_constraints(levels, e->ndv + 1) + e->ndv - 1; e->gcs = newConstraints(e->gm); e->gm = 0; e->vs = N_GNEW(n + e->ndv, Variable *); for (i = 0; i < n; i++) { e->vs[i] = vs[i]; } free(vs); /* create dummy vars */ for (i = 0; i < e->ndv; i++) { /* dummy vars should have 0 weight */ cvar = n + i; e->vs[cvar] = newVariable(cvar, 1.0, 0.000001); } halfgap = opt->edge_gap; for (i = 0; i < e->ndv; i++) { cvar = n + i; /* outgoing constraints for each var in level below boundary */ for (j = 0; j < levels[i].num_nodes; j++) { e->gcs[e->gm++] = newConstraint(e->vs[levels[i].nodes[j]], e->vs[cvar], halfgap); } /* incoming constraints for each var in level above boundary */ for (j = 0; j < levels[i + 1].num_nodes; j++) { e->gcs[e->gm++] = newConstraint(e->vs[cvar], e->vs[levels[i + 1].nodes[j]], halfgap); } } /* constraints between adjacent boundary dummy vars */ for (i = 0; i < e->ndv - 1; i++) { e->gcs[e->gm++] = newConstraint(e->vs[n + i], e->vs[n + i + 1], 0); } }
int stress_majorization_with_hierarchy( vtx_data* graph, /* Input graph in sparse representation */ int n, /* Number of nodes */ int nedges_graph, /* Number of edges */ double** d_coords, /* Coordinates of nodes (output layout) */ int dim, /* Dimemsionality of layout */ int smart_ini, /* smart initialization */ int model, /* difference model */ int maxi, /* max iterations */ double levels_gap ) { int iterations = 0; /* Output: number of iteration of the process */ /************************************************* ** Computation of full, dense, unrestricted k-D ** ** stress minimization by majorization ** ** This function imposes HIERARCHY CONSTRAINTS ** *************************************************/ int i,j,k; bool directionalityExist = FALSE; float * lap1 = NULL; float * dist_accumulator = NULL; float * tmp_coords = NULL; float ** b = NULL; #ifdef NONCORE FILE * fp=NULL; #endif double * degrees = NULL; float * lap2=NULL; int lap_length; float * f_storage=NULL; float ** coords=NULL; double conj_tol=tolerance_cg; /* tolerance of Conjugate Gradient */ float ** unpackedLap = NULL; CMajEnv *cMajEnv = NULL; clock_t start_time; double y_0; int length; DistType diameter; float * Dij=NULL; /* to compensate noises, we never consider gaps smaller than 'abs_tol' */ double abs_tol=1e-2; /* Additionally, we never consider gaps smaller than 'abs_tol'*<avg_gap> */ double relative_tol=levels_sep_tol; int *ordering=NULL, *levels=NULL; double hierarchy_spread; float constant_term; int count; double degree; int step; float val; double old_stress, new_stress; bool converged; int len; int num_levels; float *hierarchy_boundaries; if (graph[0].edists!=NULL) { for (i=0; i<n; i++) { for (j=1; j<graph[i].nedges; j++) { directionalityExist = directionalityExist || (graph[i].edists[j]!=0); } } } if (!directionalityExist) { return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, dim, smart_ini, model, maxi); } /****************************************************************** ** First, partition nodes into layers: These are our constraints ** ******************************************************************/ if (smart_ini) { double* x; double* y; if (dim>2) { /* the dim==2 case is handled below */ stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords+1, dim-1, smart_ini, model, 15); /* now copy the y-axis into the (dim-1)-axis */ for (i=0; i<n; i++) { d_coords[dim-1][i] = d_coords[1][i]; } } x = d_coords[0]; y = d_coords[1]; compute_y_coords(graph, n, y, n); hierarchy_spread = compute_hierarchy(graph, n, abs_tol, relative_tol, y, &ordering, &levels, &num_levels); if (num_levels<=1) { /* no hierarchy found, use faster algorithm */ return stress_majorization_kD_mkernel(graph, n, nedges_graph, d_coords, dim, smart_ini, model, maxi); } if (levels_gap>0) { /* ensure that levels are separated in the initial layout */ double displacement = 0; int stop; for (i=0; i<num_levels; i++) { displacement+=MAX((double)0,levels_gap-(y[ordering[levels[i]]]+displacement-y[ordering[levels[i]-1]])); stop = i<num_levels-1 ? levels[i+1] : n; for (j=levels[i]; j<stop; j++) { y[ordering[j]] += displacement; } } } if (dim==2) { IMDS_given_dim(graph, n, y, x, Epsilon); } } else { initLayout(graph, n, dim, d_coords); hierarchy_spread = compute_hierarchy(graph, n, abs_tol, relative_tol, NULL, &ordering, &levels, &num_levels); } if (n == 1) return 0; hierarchy_boundaries = N_GNEW(num_levels, float); /**************************************************** ** Compute the all-pairs-shortest-distances matrix ** ****************************************************/ if (maxi==0) return iterations; if (model == MODEL_SUBSET) { /* weight graph to separate high-degree nodes */ /* and perform slower Dijkstra-based computation */ if (Verbose) fprintf(stderr, "Calculating subset model"); Dij = compute_apsp_artifical_weights_packed(graph, n); } else if (model == MODEL_CIRCUIT) { Dij = circuitModel(graph, n); if (!Dij) { agerr(AGWARN, "graph is disconnected. Hence, the circuit model\n"); agerr(AGPREV, "is undefined. Reverting to the shortest path model.\n"); } } if (!Dij) { if (Verbose) fprintf(stderr, "Calculating shortest paths"); Dij = compute_apsp_packed(graph, n); } diameter=-1; length = n+n*(n-1)/2; for (i=0; i<length; i++) { if (Dij[i]>diameter) { diameter = (int)Dij[i]; } } if (!smart_ini) { /* for numerical stability, scale down layout */ /* No Jiggling, might conflict with constraints */ double max=1; for (i=0; i<dim; i++) { for (j=0; j<n; j++) { if (fabs(d_coords[i][j])>max) { max=fabs(d_coords[i][j]); } } } for (i=0; i<dim; i++) { for (j=0; j<n; j++) { d_coords[i][j]*=10/max; } } } if (levels_gap>0) { int length = n+n*(n-1)/2; double sum1, sum2, scale_ratio; int count; sum1=(float)(n*(n-1)/2); sum2=0; for (count=0, i=0; i<n-1; i++) { count++; // skip self distance for (j=i+1; j<n; j++,count++) { sum2+=distance_kD(d_coords, dim, i, j)/Dij[count]; } } scale_ratio=sum2/sum1; /* double scale_ratio=10; */ for (i=0; i<length; i++) { Dij[i]*=(float)scale_ratio; } } /************************** ** Layout initialization ** **************************/ for (i=0; i<dim; i++) { orthog1(n, d_coords[i]); } /* for the y-coords, don't center them, but translate them so y[0]=0 */ y_0 = d_coords[1][0]; for (i=0; i<n; i++) { d_coords[1][i] -= y_0; } coords = N_GNEW(dim, float*); f_storage = N_GNEW(dim*n, float); for (i=0; i<dim; i++) { coords[i] = f_storage+i*n; for (j=0; j<n; j++) { coords[i][j] = (float)(d_coords[i][j]); } } /* compute constant term in stress sum * which is \sum_{i<j} w_{ij}d_{ij}^2 */ constant_term=(float)(n*(n-1)/2); /************************** ** Laplacian computation ** **************************/ lap2 = Dij; lap_length = n+n*(n-1)/2; square_vec(lap_length, lap2); /* compute off-diagonal entries */ invert_vec(lap_length, lap2); /* compute diagonal entries */ count=0; degrees = N_GNEW(n, double); set_vector_val(n, 0, degrees); for (i=0; i<n-1; i++) { degree=0; count++; // skip main diag entry for (j=1; j<n-i; j++,count++) { val = lap2[count]; degree+=val; degrees[i+j]-=val; } degrees[i]-=degree; } for (step=n,count=0,i=0; i<n; i++,count+=step,step--) { lap2[count]=(float)degrees[i]; } #ifdef NONCORE fpos_t pos; if (n>max_nodes_in_mem) { #define FILENAME "tmp_Dij$$$.bin" fp = fopen(FILENAME, "wb"); fwrite(lap2, sizeof(float), lap_length, fp); fclose(fp); } #endif /************************* ** Layout optimization ** *************************/ b = N_GNEW (dim, float*); b[0] = N_GNEW (dim*n, float); for (k=1; k<dim; k++) { b[k] = b[0]+k*n; } tmp_coords = N_GNEW(n, float); dist_accumulator = N_GNEW(n, float); #ifdef NONCORE if (n<=max_nodes_in_mem) { #endif lap1 = N_GNEW(lap_length, float); #ifdef NONCORE }