static void uniform_stress_augment_rhs(int m, int dim, real *x, real *y, real alpha, real M){
  int i, j, k;
  real dist, distij;
  for (i = 0; i < m; i++){
    for (j = i+1; j < m; j++){
      dist = distance_cropped(x, dim, i, j);
      for (k = 0; k < dim; k++){
	distij = (x[i*dim+k] - x[j*dim+k])/dist;
	y[i*dim+k] += alpha*M*distij;
	y[j*dim+k] += alpha*M*(-distij);
      }
    }
  }
}
Exemple #2
0
static void QuadTree_repulsive_force_interact(QuadTree qt1, QuadTree qt2, real *x, real *force, real bh, real p, real KP, real *counts){
  /* calculate the all to all reopulsive force and accumulate on each node of the quadtree if an interaction is possible.
     force[i*dim+j], j=1,...,dim is teh force on node i 
   */
  SingleLinkedList l1, l2;
  real *x1, *x2, dist, wgt1, wgt2, f, *f1, *f2, w1, w2;
  int dim, i, j, i1, i2, k;
  QuadTree qt11, qt12; 

  if (!qt1 || !qt2) return;
  assert(qt1->n > 0 && qt2->n > 0);
  dim = qt1->dim;

  l1 = qt1->l;
  l2 = qt2->l;

  /* far enough, calculate repulsive force */
  dist = point_distance(qt1->average, qt2->average, dim); 
  if (qt1->width + qt2->width < bh*dist){
    counts[0]++;
    x1 = qt1->average;
    w1 = qt1->total_weight;
    f1 = get_or_alloc_force_qt(qt1, dim);
    x2 = qt2->average;
    w2 = qt2->total_weight;
    f2 = get_or_alloc_force_qt(qt2, dim);
    assert(dist > 0);
    for (k = 0; k < dim; k++){
      if (p == -1){
	f = w1*w2*KP*(x1[k] - x2[k])/(dist*dist);
      } else {
	f = w1*w2*KP*(x1[k] - x2[k])/pow(dist, 1.- p);
      }
      f1[k] += f;
      f2[k] -= f;
    }
    return;
  }


  /* both at leaves, calculate repulsive force */
  if (l1 && l2){
    while (l1){
      x1 = node_data_get_coord(SingleLinkedList_get_data(l1));
      wgt1 = node_data_get_weight(SingleLinkedList_get_data(l1));
      i1 = node_data_get_id(SingleLinkedList_get_data(l1));
      f1 = get_or_assign_node_force(force, i1, l1, dim);
      l2 = qt2->l;
      while (l2){
	x2 = node_data_get_coord(SingleLinkedList_get_data(l2));
	wgt2 = node_data_get_weight(SingleLinkedList_get_data(l2));
	i2 = node_data_get_id(SingleLinkedList_get_data(l2));
	f2 = get_or_assign_node_force(force, i2, l2, dim);
	if ((qt1 == qt2 && i2 < i1) || i1 == i2) {
	  l2 = SingleLinkedList_get_next(l2);
	  continue;
	}
	counts[1]++;
	dist = distance_cropped(x, dim, i1, i2);
	for (k = 0; k < dim; k++){
	  if (p == -1){
	    f = wgt1*wgt2*KP*(x1[k] - x2[k])/(dist*dist);
	  } else {
	    f = wgt1*wgt2*KP*(x1[k] - x2[k])/pow(dist, 1.- p);
	  }
	  f1[k] += f;
	  f2[k] -= f;
	}
	l2 = SingleLinkedList_get_next(l2);
      }
      l1 = SingleLinkedList_get_next(l1);
    }
    return;
  }


  /* identical, split one */
  if (qt1 == qt2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	for (j = i; j < 1<<dim; j++){
	  qt12 = qt1->qts[j];
	  QuadTree_repulsive_force_interact(qt11, qt12, x, force, bh, p, KP, counts);
	}
      }
  } else {
    /* split the one with bigger box, or one not at the last level */
    if (qt1->width > qt2->width && !l1){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt2, x, force, bh, p, KP, counts);
      }
    } else if (qt2->width > qt1->width && !l2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt2->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt1, x, force, bh, p, KP, counts);
      }
    } else if (!l1){/* pick one that is not at the last level */
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt1->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt2, x, force, bh, p, KP, counts);
      }
    } else if (!l2){
      for (i = 0; i < 1<<dim; i++){
	qt11 = qt2->qts[i];
	QuadTree_repulsive_force_interact(qt11, qt1, x, force, bh, p, KP, counts);
      }
    } else {
      assert(0); /* can be both at the leaf level since that should be catched at the beginning of this func. */
    }
  }
}
static void get_edge_label_matrix(relative_position_constraints data, int m, int dim, real *x, SparseMatrix *LL, real **rhs){
  int edge_labeling_scheme = data->edge_labeling_scheme;
  int n_constr_nodes = data->n_constr_nodes;
  int *constr_nodes = data->constr_nodes;
  SparseMatrix A_constr = data->A_constr;
  int *ia = A_constr->ia, *ja = A_constr->ja, ii, jj, nz, l, ll, i, j;
  int *irn = data->irn, *jcn = data->jcn;
  real *val = data->val, dist, kk, k;
  real *x00 = NULL;
  SparseMatrix Lc = NULL;
  real constr_penalty = data->constr_penalty;

  if (edge_labeling_scheme == ELSCHEME_PENALTY || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY){
    /* for an node with two neighbors j--i--k, and assume i needs to be between j and k, then the contribution to P is
       .   i    j     k
       i   1   -0.5  -0.5
       j  -0.5 0.25  0.25
       k  -0.5 0.25  0.25
       in general, if i has m neighbors j, k, ..., then
       p_ii = 1
       p_jj = 1/m^2
       p_ij = -1/m
       p jk = 1/m^2
       .    i     j    k
       i    1    -1/m  -1/m ...
       j   -1/m  1/m^2 1/m^2 ...
       k   -1/m  1/m^2 1/m^2 ...
    */
    if (!irn){
      assert((!jcn) && (!val));
      nz = 0;
      for (i = 0; i < n_constr_nodes; i++){
	ii = constr_nodes[i];
	k = ia[ii+1] - ia[ii];/*usually k = 2 */
	nz += (k+1)*(k+1);
	
      }
      irn = data->irn = MALLOC(sizeof(int)*nz);
      jcn = data->jcn = MALLOC(sizeof(int)*nz);
      val = data->val = MALLOC(sizeof(double)*nz);
    }
    nz = 0;
    for (i = 0; i < n_constr_nodes; i++){
      ii = constr_nodes[i];
      jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
      if (jj == ll) continue; /* do not do loops */
      dist = distance_cropped(x, dim, jj, ll);
      dist *= dist;

      k = ia[ii+1] - ia[ii];/* usually k = 2 */
      kk = k*k;
      irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
      k = constr_penalty/(k*dist); kk = constr_penalty/(kk*dist);
      for (j = ia[ii]; j < ia[ii+1]; j++){
	irn[nz] = ii; jcn[nz] = ja[j]; val[nz++] = -k;
      }
      for (j = ia[ii]; j < ia[ii+1]; j++){
	jj = ja[j];
	irn[nz] = jj; jcn[nz] = ii; val[nz++] = -k;
	for (l = ia[ii]; l < ia[ii+1]; l++){
	  ll = ja[l];
	  irn[nz] = jj; jcn[nz] = ll; val[nz++] = kk;
	}
      }
    }
    Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(real));
  } else if (edge_labeling_scheme == ELSCHEME_PENALTY2 || edge_labeling_scheme == ELSCHEME_STRAIGHTLINE_PENALTY2){
    /* for an node with two neighbors j--i--k, and assume i needs to be between the old position of j and k, then the contribution to P is
       1/d_jk, and to the right hand side: {0,...,average_position_of_i's neighbor if i is an edge node,...}
    */
    if (!irn){
      assert((!jcn) && (!val));
      nz = n_constr_nodes;
      irn = data->irn = MALLOC(sizeof(int)*nz);
      jcn = data->jcn = MALLOC(sizeof(int)*nz);
      val = data->val = MALLOC(sizeof(double)*nz);
    }
    x00 = MALLOC(sizeof(real)*m*dim);
    for (i = 0; i < m*dim; i++) x00[i] = 0;
    nz = 0;
    for (i = 0; i < n_constr_nodes; i++){
      ii = constr_nodes[i];
      jj = ja[ia[ii]]; ll = ja[ia[ii] + 1];
      dist = distance_cropped(x, dim, jj, ll);
      irn[nz] = ii; jcn[nz] = ii; val[nz++] = constr_penalty/(dist);
      for (j = ia[ii]; j < ia[ii+1]; j++){
	jj = ja[j];
	for (l = 0; l < dim; l++){
	  x00[ii*dim+l] += x[jj*dim+l];
	}
      }
      for (l = 0; l < dim; l++) {
	x00[ii*dim+l] *= constr_penalty/(dist)/(ia[ii+1] - ia[ii]);
      }
    }
    Lc = SparseMatrix_from_coordinate_arrays(nz, m, m, irn, jcn, val, MATRIX_TYPE_REAL, sizeof(real));
    
  }
  *LL = Lc;
  *rhs = x00;
}
StressMajorizationSmoother StressMajorizationSmoother2_new(SparseMatrix A, int dim, real lambda0, real *x, 
							  int ideal_dist_scheme){
  /* use up to dist 2 neighbor */
  /* use up to dist 2 neighbor. This is used in overcoming pherical effect with ideal distance of
     2-neighbors equal graph distance etc.
   */
  StressMajorizationSmoother sm;
  int i, j, k, l, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, *id, *jd;
  int *mask, nz;
  real *d, *w, *lambda;
  real *avg_dist, diag_d, diag_w, dist, s = 0, stop = 0, sbot = 0;
  SparseMatrix ID;

  assert(SparseMatrix_is_symmetric(A, FALSE));

  ID = ideal_distance_matrix(A, dim, x);

  sm = GNEW(struct StressMajorizationSmoother_struct);
  sm->scaling = 1.;
  sm->data = NULL;
  sm->scheme = SM_SCHEME_NORMAL;
  sm->tol_cg = 0.01;
  sm->maxit_cg = sqrt((double) A->m);

  lambda = sm->lambda = N_GNEW(m,real);
  for (i = 0; i < m; i++) sm->lambda[i] = lambda0;
  mask = N_GNEW(m,int);

  avg_dist = N_GNEW(m,real);

  for (i = 0; i < m ;i++){
    avg_dist[i] = 0;
    nz = 0;
    for (j = ia[i]; j < ia[i+1]; j++){
      if (i == ja[j]) continue;
      avg_dist[i] += distance(x, dim, i, ja[j]);
      nz++;
    }
    assert(nz > 0);
    avg_dist[i] /= nz;
  }


  for (i = 0; i < m; i++) mask[i] = -1;

  nz = 0;
  for (i = 0; i < m; i++){
    mask[i] = i;
    for (j = ia[i]; j < ia[i+1]; j++){
      k = ja[j];
      if (mask[k] != i){
	mask[k] = i;
	nz++;
      }
    }
    for (j = ia[i]; j < ia[i+1]; j++){
      k = ja[j];
      for (l = ia[k]; l < ia[k+1]; l++){
	if (mask[ja[l]] != i){
	  mask[ja[l]] = i;
	  nz++;
	}
      }
    }
  }

  sm->Lw = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
  sm->Lwd = SparseMatrix_new(m, m, nz + m, MATRIX_TYPE_REAL, FORMAT_CSR);
  if (!(sm->Lw) || !(sm->Lwd)) {
    StressMajorizationSmoother_delete(sm);
    return NULL;
  }

  iw = sm->Lw->ia; jw = sm->Lw->ja;

  w = (real*) sm->Lw->a; d = (real*) sm->Lwd->a;

  id = sm->Lwd->ia; jd = sm->Lwd->ja;
  iw[0] = id[0] = 0;

  nz = 0;
  for (i = 0; i < m; i++){
    mask[i] = i+m;
    diag_d = diag_w = 0;
    for (j = ia[i]; j < ia[i+1]; j++){
      k = ja[j];
      if (mask[k] != i+m){
	mask[k] = i+m;

	jw[nz] = k;
	if (ideal_dist_scheme == IDEAL_GRAPH_DIST){
	  dist = 1;
	} else if (ideal_dist_scheme == IDEAL_AVG_DIST){
	  dist = (avg_dist[i] + avg_dist[k])*0.5;
	} else if (ideal_dist_scheme == IDEAL_POWER_DIST){
	  dist = pow(distance_cropped(x,dim,i,k),.4);
	} else {
	  fprintf(stderr,"ideal_dist_scheme value wrong");
	  assert(0);
	  exit(1);
	}

	/*	
	  w[nz] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]);
	  w[nz] = -2./(avg_dist[i]+avg_dist[k]);*/
	/* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
	w[nz] = -1/(dist*dist);

	diag_w += w[nz];

	jd[nz] = k;
	/*
	  d[nz] = w[nz]*distance(x,dim,i,k);
	  d[nz] = w[nz]*dd[j];
	  d[nz] = w[nz]*(avg_dist[i] + avg_dist[k])*0.5;
	*/
	d[nz] = w[nz]*dist;
	stop += d[nz]*distance(x,dim,i,k);
	sbot += d[nz]*dist;
	diag_d += d[nz];

	nz++;
      }
    }

    /* distance 2 neighbors */
    for (j = ia[i]; j < ia[i+1]; j++){
      k = ja[j];
      for (l = ia[k]; l < ia[k+1]; l++){
	if (mask[ja[l]] != i+m){
	  mask[ja[l]] = i+m;

	  if (ideal_dist_scheme == IDEAL_GRAPH_DIST){
	    dist = 2;
	  } else if (ideal_dist_scheme == IDEAL_AVG_DIST){
	    dist = (avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
	  } else if (ideal_dist_scheme == IDEAL_POWER_DIST){
	    dist = pow(distance_cropped(x,dim,i,ja[l]),.4);
	  } else {
	    fprintf(stderr,"ideal_dist_scheme value wrong");
	    assert(0);
	    exit(1);
	  }

	  jw[nz] = ja[l];
	  /*
	    w[nz] = -1/(ia[i+1]-ia[i]+ia[ja[l]+1]-ia[ja[l]]);
	    w[nz] = -2/(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]]);*/
	  /* w[nz] = -1.;*//* use unit weight for now, later can try 1/(deg(i)+deg(k)) */

	  w[nz] = -1/(dist*dist);

	  diag_w += w[nz];

	  jd[nz] = ja[l];
	  /*
	    d[nz] = w[nz]*(distance(x,dim,i,k)+distance(x,dim,k,ja[l]));
	    d[nz] = w[nz]*(dd[j]+dd[l]);
	    d[nz] = w[nz]*(avg_dist[i] + 2*avg_dist[k] + avg_dist[ja[l]])*0.5;
	  */
	  d[nz] = w[nz]*dist;
	  stop += d[nz]*distance(x,dim,ja[l],k);
	  sbot += d[nz]*dist;
	  diag_d += d[nz];

	  nz++;
	}
      }
    }
    jw[nz] = i;
    lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */

    w[nz] = -diag_w + lambda[i];
    jd[nz] = i;
    d[nz] = -diag_d;
    nz++;

    iw[i+1] = nz;
    id[i+1] = nz;
  }
  s = stop/sbot;
  for (i = 0; i < nz; i++) d[i] *= s;

  sm->scaling = s;
  sm->Lw->nz = nz;
  sm->Lwd->nz = nz;

  FREE(mask);
  FREE(avg_dist);
  SparseMatrix_delete(ID);
  return sm;
}
TriangleSmoother TriangleSmoother_new(SparseMatrix A, int dim, real lambda0, real *x, int use_triangularization){
  TriangleSmoother sm;
  int i, j, k, m = A->m, *ia = A->ia, *ja = A->ja, *iw, *jw, jdiag, nz;
  SparseMatrix B;
  real *avg_dist, *lambda, *d, *w, diag_d, diag_w, dist;
  real s = 0, stop = 0, sbot = 0;

  assert(SparseMatrix_is_symmetric(A, FALSE));

  avg_dist = N_GNEW(m,real);

  for (i = 0; i < m ;i++){
    avg_dist[i] = 0;
    nz = 0;
    for (j = ia[i]; j < ia[i+1]; j++){
      if (i == ja[j]) continue;
      avg_dist[i] += distance(x, dim, i, ja[j]);
      nz++;
    }
    assert(nz > 0);
    avg_dist[i] /= nz;
  }

  sm = N_GNEW(1,struct TriangleSmoother_struct);
  sm->scaling = 1;
  sm->data = NULL;
  sm->scheme = SM_SCHEME_NORMAL;
  sm->tol_cg = 0.01;
  sm->maxit_cg = sqrt((double) A->m);

  lambda = sm->lambda = N_GNEW(m,real);
  for (i = 0; i < m; i++) sm->lambda[i] = lambda0;
  
  if (m > 2){
    if (use_triangularization){
      B= call_tri(m, dim, x);
    } else {
      B= call_tri2(m, dim, x);
    }
  } else {
    B = SparseMatrix_copy(A);
  }



  sm->Lw = SparseMatrix_add(A, B);

  SparseMatrix_delete(B);
  sm->Lwd = SparseMatrix_copy(sm->Lw);
  if (!(sm->Lw) || !(sm->Lwd)) {
    TriangleSmoother_delete(sm);
    return NULL;
  }

  iw = sm->Lw->ia; jw = sm->Lw->ja;

  w = (real*) sm->Lw->a; d = (real*) sm->Lwd->a;

  for (i = 0; i < m; i++){
    diag_d = diag_w = 0;
    jdiag = -1;
    for (j = iw[i]; j < iw[i+1]; j++){
      k = jw[j];
      if (k == i){
	jdiag = j;
	continue;
      }
      /*      w[j] = -1./(ia[i+1]-ia[i]+ia[ja[j]+1]-ia[ja[j]]);
	      w[j] = -2./(avg_dist[i]+avg_dist[k]);
	      w[j] = -1.*/;/* use unit weight for now, later can try 1/(deg(i)+deg(k)) */
      dist = pow(distance_cropped(x,dim,i,k),0.6);
      w[j] = 1/(dist*dist);
      diag_w += w[j];

      /*      d[j] = w[j]*distance(x,dim,i,k);
	      d[j] = w[j]*(avg_dist[i] + avg_dist[k])*0.5;*/
      d[j] = w[j]*dist;
      stop += d[j]*distance(x,dim,i,k);
      sbot += d[j]*dist;
      diag_d += d[j];

    }

    lambda[i] *= (-diag_w);/* alternatively don't do that then we have a constant penalty term scaled by lambda0 */

    assert(jdiag >= 0);
    w[jdiag] = -diag_w + lambda[i];
    d[jdiag] = -diag_d;
  }

  s = stop/sbot;
  for (i = 0; i < iw[m]; i++) d[i] *= s;
  sm->scaling = s;

  FREE(avg_dist);

  return sm;
}
Exemple #6
0
real StressMajorizationSmoother_smooth(StressMajorizationSmoother sm, int dim, real *x, int maxit_sm, real tol) {
  SparseMatrix Lw = sm->Lw, Lwd = sm->Lwd, Lwdd = NULL;
  int i, j, m, *id, *jd, *iw, *jw, idiag, flag = 0, iter = 0;
  real *w, *dd, *d, *y = NULL, *x0 = NULL, *x00 = NULL, diag, diff = 1, *lambda = sm->lambda, maxit, res, alpha = 0., M = 0.;
  SparseMatrix Lc = NULL;

  Lwdd = SparseMatrix_copy(Lwd);
  m = Lw->m;
  x0 = N_GNEW(dim*m,real);
  if (!x0) goto RETURN;

  x0 = MEMCPY(x0, x, sizeof(real)*dim*m);
  y = N_GNEW(dim*m,real);
  if (!y) goto RETURN;

  id = Lwd->ia; jd = Lwd->ja;
  d = (real*) Lwd->a;
  dd = (real*) Lwdd->a;
  w = (real*) Lw->a;
  iw = Lw->ia; jw = Lw->ja;

  /* for the additional matrix L due to the position constraints */
  if (sm->scheme == SM_SCHEME_NORMAL_ELABEL){
    get_edge_label_matrix(sm->data, m, dim, x, &Lc, &x00);
    if (Lc) Lw = SparseMatrix_add(Lw, Lc);
  } else if (sm->scheme == SM_SCHEME_UNIFORM_STRESS){
    alpha = ((real*) (sm->data))[0];
    M = ((real*) (sm->data))[1];
  }

  while (iter++ < maxit_sm && diff > tol){

    for (i = 0; i < m; i++){
      idiag = -1;
      diag = 0.;
      for (j = id[i]; j < id[i+1]; j++){
	if (i == jd[j]) {
	  idiag = j;
	  continue;
	}
	dd[j] = d[j]/distance_cropped(x, dim, i, jd[j]);
	diag += dd[j];
      }
      assert(idiag >= 0);
      dd[idiag] = -diag;
    }

    /* solve (Lw+lambda*I) x = Lwdd y + lambda x0 */

    SparseMatrix_multiply_dense(Lwdd, FALSE, x, FALSE, &y, FALSE, dim);
 
    if (lambda){/* is there a penalty term? */
      for (i = 0; i < m; i++){
	for (j = 0; j < dim; j++){
	  y[i*dim+j] += lambda[i]*x0[i*dim+j];
	}
      }
    }

    if (sm->scheme == SM_SCHEME_NORMAL_ELABEL){
      for (i = 0; i < m; i++){
	for (j = 0; j < dim; j++){
	  y[i*dim+j] += x00[i*dim+j];
	}
      }
    } else if (sm->scheme == SM_SCHEME_UNIFORM_STRESS){/* this part can be done more efficiently using octree approximation */
      uniform_stress_augment_rhs(m, dim, x, y, alpha, M);
    }

#ifdef DEBUG_PRINT
    if (Verbose) fprintf(stderr, "stress1 = %f\n",get_stress(m, dim, iw, jw, w, d, x, sm->scaling, sm->data, 0));
#endif

    maxit = sqrt((double) m);
    if (sm->scheme == SM_SCHEME_UNIFORM_STRESS){
      res = uniform_stress_solve(Lw, alpha, dim, x, y, 0.01, maxit, &flag);
    } else {
      res = SparseMatrix_solve(Lw, dim, x, y, 0.01, maxit, SOLVE_METHOD_CG, &flag);
    }
    if (flag) goto RETURN;
#ifdef DEBUG_PRINT
    if (Verbose) fprintf(stderr, "stress2 = %f\n",get_stress(m, dim, iw, jw, w, d, y, sm->scaling, sm->data, 0));
#endif

    diff = total_distance(m, dim, x, y)/sqrt(vector_product(m*dim, x, x));
#ifdef DEBUG_PRINT
    if (Verbose){
      fprintf(stderr, "Outer iter = %d, res = %g Stress Majorization diff = %g\n",iter, res, diff);
    }
#endif
    MEMCPY(x, y, sizeof(real)*m*dim);
  }

#ifdef DEBUG
  _statistics[1] += iter-1;
#endif

 RETURN:
  SparseMatrix_delete(Lwdd);
  if (Lc) {
    SparseMatrix_delete(Lc);
    SparseMatrix_delete(Lw);
  }

  if (x0) FREE(x0);
  if (y) FREE(y);
  if (x00) FREE(x00);
  return diff;
  
}
int main(int argc, char *argv[])
{


  char *infile;

  SparseMatrix B = NULL;
  int dim = 2, n = 0, i, *ia, *ja, j, k, nz = 0;
  real *x, *y, mean, dev, ratio, *z, r, theta, p, q, xx;

  FILE *fp;

  
  if (argc < 2){
    fprintf(stderr,"Usage: similarity graph layout1 layout2\n");
  }

  infile = argv[1];
  fp = fopen(infile,"r");
  while (fscanf(fp,"%lf %lf\n",&xx, &xx) == 2) n++;
  fclose(fp);

  infile = argv[1];
  fp = fopen(infile,"r");
  x = N_GNEW(n*2,real);
  for (i = 0; i < n; i++){
    fscanf(fp,"%lf %lf\n",&(x[2*i]), &(x[2*i+1]));
  }
  fclose(fp);


  infile = argv[2];
  fp = fopen(infile,"r");
  y = N_GNEW(n*2,real);
  nz = 0;
  for (i = 0; i < n; i++){
    if (fscanf(fp,"%lf %lf\n",&(y[2*i]), &(y[2*i+1])) != 2) goto ERROR;
  }
  fclose(fp);

  B = call_tri(n, 2, x);

  z = N_GNEW(B->nz,real);
  ia = B->ia; ja = B->ja;
  nz = 0;
  for (i = 0; i < n; i++){
    for (j = ia[i]; j < ia[i+1]; j++){
      k = ja[j];
      if (k == i) continue;
      z[nz++] = distance(y,dim,i,k)/distance_cropped(x,dim,i,k);
    }
  }

  /* mean */
  mean = 0;
  for (i = 0; i < nz; i++) mean += z[i];
  mean /= nz;

  /* deviation*/
  dev = 0;
  for (i = 0; i < nz; i++) {
    dev += (z[i]-mean)*(z[i]-mean);
  }
  dev /= nz;
  dev = sqrt(dev);

  /* bounding box area */
  ratio = boundingbox_area(n, y);
  /*/MAX(boundingbox_area(n, x), 0.001);*/


  fprintf(stderr, "mean = %f std = %f disimilarity = %f area = %f badness = %f displacement = %f\n",
	  mean, dev, dev/mean, ratio, (dev/mean+1)*ratio, dispacement(n, x, y, &r, &theta, &p, &q));
  fprintf(stderr, "theta = %f scaling = %f, shift = {%f, %f}\n",theta, 1/r, p, q);


  printf("%.2f %.2f %.2f\n",dev/mean, dispacement(n, x, y, &r, &theta, &p, &q),ratio/1000000.);

  SparseMatrix_delete(B);
  FREE(x);
  FREE(y);
  FREE(z);


  return 0;

 ERROR:
  printf("- - -\n");
  return 0;
}