Example #1
0
/*
 * 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
	}