void compute_new_weights(vtx_data * graph, int n) { /* Reweight graph so that high degree nodes will be separated */ int i, j; int nedges = 0; float *weights; int *vtx_vec = N_GNEW(n, int); int deg_i, deg_j, neighbor; for (i = 0; i < n; i++) { nedges += graph[i].nedges; } weights = N_GNEW(nedges, float); for (i = 0; i < n; i++) { vtx_vec[i] = 0; } for (i = 0; i < n; i++) { graph[i].ewgts = weights; fill_neighbors_vec_unweighted(graph, i, vtx_vec); deg_i = graph[i].nedges - 1; for (j = 1; j <= deg_i; j++) { neighbor = graph[i].edges[j]; deg_j = graph[neighbor].nedges - 1; weights[j] = (float) (deg_i + deg_j - 2 * common_neighbors(graph, i, neighbor, vtx_vec)); } empty_neighbors_vec(graph, i, vtx_vec); weights += graph[i].nedges; } free(vtx_vec); }
static int maxmatch(v_data * graph, /* array of vtx data for graph */ ex_vtx_data * geom_graph, /* array of vtx data for graph */ int nvtxs, /* number of vertices in graph */ int *mflag, /* flag indicating vtx selected or not */ int dist2_limit ) /* Compute a matching of the nodes set. The matching is not based only on the edge list of 'graph', which might be too small, but on the wider edge list of 'geom_graph' (which includes 'graph''s edges) We match nodes that are close both in the graph-theoretical sense and in the geometry sense (in the layout) */ { int *order; /* random ordering of vertices */ int *iptr, *jptr; /* loops through integer arrays */ int vtx; /* vertex to process next */ int neighbor; /* neighbor of a vertex */ int nmerged = 0; /* number of edges in matching */ int i, j; /* loop counters */ float max_norm_edge_weight; double inv_size; double *matchability = N_NEW(nvtxs, double); double min_edge_len; double closest_val = -1, val; int closest_neighbor; float *vtx_vec = N_NEW(nvtxs, float); float *weighted_vtx_vec = N_NEW(nvtxs, float); float sum_weights; // gather statistics, to enable normalizing the values double avg_edge_len = 0, avg_deg_2 = 0; int nedges = 0; for (i = 0; i < nvtxs; i++) { avg_deg_2 += graph[i].nedges; for (j = 1; j < graph[i].nedges; j++) { avg_edge_len += ddist(geom_graph, i, graph[i].edges[j]); nedges++; } } avg_edge_len /= nedges; avg_deg_2 /= nvtxs; avg_deg_2 *= avg_deg_2; // the normalized edge weight of edge <v,u> is defined as: // weight(<v,u>)/sqrt(size(v)*size(u)) // Now we compute the maximal normalized weight if (graph[0].ewgts != NULL) { max_norm_edge_weight = -1; for (i = 0; i < nvtxs; i++) { inv_size = sqrt(1.0 / geom_graph[i].size); for (j = 1; j < graph[i].nedges; j++) { if (graph[i].ewgts[j] * inv_size / sqrt((float) geom_graph[graph[i].edges[j]].size) > max_norm_edge_weight) { max_norm_edge_weight = (float) (graph[i].ewgts[j] * inv_size / sqrt((double) geom_graph[graph[i].edges[j]].size)); } } } } else { max_norm_edge_weight = 1; } /* Now determine the order of the vertices. */ iptr = order = N_NEW(nvtxs, int); jptr = mflag; for (i = 0; i < nvtxs; i++) { *(iptr++) = i; *(jptr++) = -1; } // Option 1: random permutation #if 0 int temp; for (i=0; i<nvtxs-1; i++) { // use long_rand() (not rand()), as n may be greater than RAND_MAX j=i+long_rand()%(nvtxs-i); temp=order[i]; order[i]=order[j]; order[j]=temp; } #endif // Option 2: sort the nodes begining with the ones highly approriate for matching #ifdef DEBUG srand(0); #endif for (i = 0; i < nvtxs; i++) { vtx = order[i]; matchability[vtx] = graph[vtx].nedges; // we less want to match high degree nodes matchability[vtx] += geom_graph[vtx].size; // we less want to match large sized nodes min_edge_len = 1e99; for (j = 1; j < graph[vtx].nedges; j++) { min_edge_len = MIN(min_edge_len, ddist(geom_graph, vtx, graph[vtx].edges[j]) / avg_edge_len); } matchability[vtx] += min_edge_len; // we less want to match distant nodes matchability[vtx] += ((double) rand()) / RAND_MAX; // add some randomness } quicksort_place(matchability, order, 0, nvtxs - 1); free(matchability); // Start determining the matched pairs for (i = 0; i < nvtxs; i++) { vtx_vec[i] = 0; } for (i = 0; i < nvtxs; i++) { weighted_vtx_vec[i] = 0; } // relative weights of the different criteria for (i = 0; i < nvtxs; i++) { vtx = order[i]; if (mflag[vtx] >= 0) { /* already matched. */ continue; } inv_size = sqrt(1.0 / geom_graph[vtx].size); sum_weights = fill_neighbors_vec(graph, vtx, weighted_vtx_vec); fill_neighbors_vec_unweighted(graph, vtx, vtx_vec); closest_neighbor = -1; /* We match node i with the "closest" neighbor, based on 4 criteria: (1) (Weighted) fraction of common neighbors (measured on orig. graph) (2) AvgDeg*AvgDeg/(deg(vtx)*deg(neighbor)) (degrees measured on orig. graph) (3) AvgEdgeLen/dist(vtx,neighbor) (4) Weight of normalized direct connection between nodes (measured on orig. graph) */ for (j = 1; j < geom_graph[vtx].nedges; j++) { neighbor = geom_graph[vtx].edges[j]; if (mflag[neighbor] >= 0) { /* already matched. */ continue; } // (1): val = A * unweighted_common_fraction(graph, vtx, neighbor, vtx_vec); if (val == 0 && (dist2_limit || !dist3(graph, vtx, neighbor))) { // graph theoretical distance is larger than 3 (or 2 if '!dist3(graph, vtx, neighbor)' is commented) // nodes cannot be matched continue; } // (2) val += B * avg_deg_2 / (graph[vtx].nedges * graph[neighbor].nedges); // (3) val += C * avg_edge_len / ddist(geom_graph, vtx, neighbor); // (4) val += (weighted_vtx_vec[neighbor] * inv_size / sqrt((float) geom_graph[neighbor].size)) / max_norm_edge_weight; if (val > closest_val || closest_neighbor == -1) { closest_neighbor = neighbor; closest_val = val; } } if (closest_neighbor != -1) { mflag[vtx] = closest_neighbor; mflag[closest_neighbor] = vtx; nmerged++; } empty_neighbors_vec(graph, vtx, vtx_vec); empty_neighbors_vec(graph, vtx, weighted_vtx_vec); } free(order); free(vtx_vec); free(weighted_vtx_vec); return (nmerged); }