/* sparse_stress_subspace_majorization_kD: * Optimization of the stress function using sparse distance matrix, within a vector-space * Fastest and least accurate method * * NOTE: We use integral shortest path values here, assuming * this is only to get an initial layout. In general, if edge lengths * are involved, we may end up with 0 length edges. */ static int sparse_stress_subspace_majorization_kD(vtx_data * graph, /* Input graph in sparse representation */ int n, /* Number of nodes */ int nedges_graph, /* Number of edges */ double **coords, /* coordinates of nodes (output layout) */ int dim, /* dimemsionality of layout */ int smart_ini, /* smart initialization */ int exp, /* scale exponent */ int reweight_graph, /* difference model */ int n_iterations, /* max #iterations */ int dist_bound, /* neighborhood size in sparse distance matrix */ int num_centers /* #pivots in sparse distance matrix */ ) { int iterations; /* output: number of iteration of the process */ double conj_tol = tolerance_cg; /* tolerance of Conjugate Gradient */ /************************************************* ** Computation of pivot-based, sparse, subspace-restricted ** ** k-D stress minimization by majorization ** *************************************************/ int i, j, k, node; /************************************************* ** First compute the subspace in which we optimize ** ** The subspace is the high-dimensional embedding ** *************************************************/ int subspace_dim = MIN(stress_pca_dim, n); /* overall dimensionality of subspace */ double **subspace = N_GNEW(subspace_dim, double *); double *d_storage = N_GNEW(subspace_dim * n, double); int num_centers_local; DistType **full_coords; /* if i is a pivot than CenterIndex[i] is its index, otherwise CenterIndex[i]= -1 */ int *CenterIndex; int *invCenterIndex; /* list the pivot nodes */ Queue Q; float *old_weights; /* this matrix stores the distance between each node and each "center" */ DistType **Dij; /* this vector stores the distances of each node to the selected "centers" */ DistType *dist; DistType max_dist; DistType *storage; int *visited_nodes; dist_data *distances; int available_space; int *storage1 = NULL; DistType *storage2 = NULL; int num_visited_nodes; int num_neighbors; int index; int nedges; DistType *dist_list; vtx_data *lap; int *edges; float *ewgts; double degree; double **directions; float **tmp_mat; float **matrix; double dist_ij; double *b; double *b_restricted; double L_ij; double old_stress, new_stress; boolean converged; for (i = 0; i < subspace_dim; i++) { subspace[i] = d_storage + i * n; } /* compute PHDE: */ num_centers_local = MIN(n, MAX(2 * subspace_dim, 50)); full_coords = NULL; /* High dimensional embedding */ embed_graph(graph, n, num_centers_local, &full_coords, reweight_graph); /* Centering coordinates */ center_coordinate(full_coords, n, num_centers_local); /* PCA */ PCA_alloc(full_coords, num_centers_local, n, subspace, subspace_dim); free(full_coords[0]); free(full_coords); /************************************************* ** Compute the sparse-shortest-distances matrix 'distances' ** *************************************************/ CenterIndex = N_GNEW(n, int); for (i = 0; i < n; i++) { CenterIndex[i] = -1; } invCenterIndex = NULL; mkQueue(&Q, n); old_weights = graph[0].ewgts; if (reweight_graph) { /* weight graph to separate high-degree nodes */ /* in the future, perform slower Dijkstra-based computation */ compute_new_weights(graph, n); } /* compute sparse distance matrix */ /* first select 'num_centers' pivots from which we compute distance */ /* to all other nodes */ Dij = NULL; dist = N_GNEW(n, DistType); if (num_centers == 0) { /* no pivots, skip pivots-to-nodes distance calculation */ goto after_pivots_selection; } invCenterIndex = N_GNEW(num_centers, int); storage = N_GNEW(n * num_centers, DistType); Dij = N_GNEW(num_centers, DistType *); for (i = 0; i < num_centers; i++) Dij[i] = storage + i * n; /* select 'num_centers' pivots that are uniformaly spreaded over the graph */ /* the first pivots is selected randomly */ node = rand() % n; CenterIndex[node] = 0; invCenterIndex[0] = node; if (reweight_graph) { dijkstra(node, graph, n, Dij[0]); } else { bfs(node, graph, n, Dij[0], &Q); } /* find the most distant node from first pivot */ max_dist = 0; for (i = 0; i < n; i++) { dist[i] = Dij[0][i]; if (dist[i] > max_dist) { node = i; max_dist = dist[i]; } } /* select other dim-1 nodes as pivots */ for (i = 1; i < num_centers; i++) { CenterIndex[node] = i; invCenterIndex[i] = node; if (reweight_graph) { dijkstra(node, graph, n, Dij[i]); } else { bfs(node, graph, n, Dij[i], &Q); } max_dist = 0; for (j = 0; j < n; j++) { dist[j] = MIN(dist[j], Dij[i][j]); if (dist[j] > max_dist || (dist[j] == max_dist && rand() % (j + 1) == 0)) { node = j; max_dist = dist[j]; } } } after_pivots_selection: /* Construct a sparse distance matrix 'distances' */ /* initialize dist to -1, important for 'bfs_bounded(..)' */ for (i = 0; i < n; i++) { dist[i] = -1; } visited_nodes = N_GNEW(n, int); distances = N_GNEW(n, dist_data); available_space = 0; nedges = 0; for (i = 0; i < n; i++) { if (CenterIndex[i] >= 0) { /* a pivot node */ distances[i].edges = N_GNEW(n - 1, int); distances[i].edist = N_GNEW(n - 1, DistType); distances[i].nedges = n - 1; nedges += n - 1; distances[i].free_mem = TRUE; index = CenterIndex[i]; for (j = 0; j < i; j++) { distances[i].edges[j] = j; distances[i].edist[j] = Dij[index][j]; } for (j = i + 1; j < n; j++) { distances[i].edges[j - 1] = j; distances[i].edist[j - 1] = Dij[index][j]; } continue; } /* a non pivot node */ if (dist_bound > 0) { if (reweight_graph) { num_visited_nodes = dijkstra_bounded(i, graph, n, dist, dist_bound, visited_nodes); } else { num_visited_nodes = bfs_bounded(i, graph, n, dist, &Q, dist_bound, visited_nodes); } /* filter the pivots out of the visited nodes list, and the self loop: */ for (j = 0; j < num_visited_nodes;) { if (CenterIndex[visited_nodes[j]] < 0 && visited_nodes[j] != i) { /* not a pivot or self loop */ j++; } else { dist[visited_nodes[j]] = -1; visited_nodes[j] = visited_nodes[--num_visited_nodes]; } } } else { num_visited_nodes = 0; } num_neighbors = num_visited_nodes + num_centers; if (num_neighbors > available_space) { available_space = (dist_bound + 1) * n; storage1 = N_GNEW(available_space, int); storage2 = N_GNEW(available_space, DistType); distances[i].free_mem = TRUE; } else {
static void local_beautify_kD(int *nodes, int num_nodes, vtx_data * graph, int n, int dist_bound, int reweight_graph, double **coords, int dim) { /* Optimize locally the k-D position of each of the nodes in 'nodes' */ /* sing majorization. */ /* Here, in each iteration only a single node is mobile */ int i, j, k; int *visited_nodes; DistType *dist; double *weights; Queue Q; int num_visited_nodes; double dist_ij; int v, neighbor; double dist_1d; double total_wgts; double *newpos; double max_diff; if (dist_bound <= 0) { return; } visited_nodes = N_GNEW(n, int); dist = N_GNEW(n, DistType); weights = N_GNEW(n, double); newpos = N_GNEW(dim, double); mkQueue(&Q, n); /* initialize dist to -1, important for bfs_bounded */ for (i = 0; i < n; i++) { dist[i] = -1; } for (i = 0; i < num_nodes; i++) { v = nodes[i]; if (reweight_graph) { num_visited_nodes = dijkstra_bounded(v, graph, n, dist, dist_bound, visited_nodes); } else { num_visited_nodes = bfs_bounded(v, graph, n, dist, &Q, dist_bound, visited_nodes); } total_wgts = 0; for (j = 0; j < num_visited_nodes; j++) { neighbor = visited_nodes[j]; if (neighbor != v) { #ifdef Dij2 total_wgts += weights[j] = 1.0 / ((double) dist[neighbor] * (double) dist[neighbor]); #else total_wgts += weights[j] = 1.0 / (double) dist[neighbor]; #endif } } if (total_wgts == 0) { /* no neighbors to current node */ continue; } do { for (k = 0; k < dim; newpos[k++] = 0); for (j = 0; j < num_visited_nodes; j++) { neighbor = visited_nodes[j]; if (neighbor == v) { continue; } for (k = 0; k < dim; k++) { dist_1d = coords[k][v] - coords[k][neighbor]; dist_ij = distance_kD(coords, dim, v, neighbor); newpos[k] += weights[j] * (coords[k][neighbor] + dist[neighbor] * dist_1d / dist_ij); } } max_diff = 0; for (k = 0; k < dim; k++) { newpos[k] /= total_wgts; max_diff = MAX(max_diff, fabs(newpos[k] - coords[k][v]) / fabs(newpos[k] + 1e-20)); coords[k][v] = newpos[k]; } } while (max_diff > Epsilon); /* initialize 'dist' for next run of 'bfs_bounded' */ for (j = 0; j < num_visited_nodes; j++) { dist[visited_nodes[j]] = -1; } } free(visited_nodes); free(dist); free(weights); free(newpos); freeQueue(&Q); }