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); } } } }
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; }
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; }