Refinement phase: within each move-phase community, merge singleton nodes * into the best connected sub-community (positive modularity gain, edge must * exist). This re-derives communities bottom-up so each one is guaranteed * internally connected — the defect single-level Louvain suffers from, which * fragments the graph into hundreds of tiny noisy clusters. Writes * sub-community labels int
| 6911 | * fragments the graph into hundreds of tiny noisy clusters. Writes |
| 6912 | * sub-community labels into refined[] and returns their count. */ |
| 6913 | static int leiden_refine(const cbm_lg_t *g, const int *comm, double gamma, double twom, |
| 6914 | int *refined) { |
| 6915 | int n = g->n; |
| 6916 | double *stot = calloc((size_t)n, sizeof(double)); |
| 6917 | double *acc = calloc((size_t)n, sizeof(double)); |
| 6918 | int *rsize = malloc((size_t)n * sizeof(int)); |
| 6919 | int *dirty = malloc((size_t)n * sizeof(int)); |
| 6920 | if (!stot || !acc || !rsize || !dirty) { |
| 6921 | free(stot); |
| 6922 | free(acc); |
| 6923 | free(rsize); |
| 6924 | free(dirty); |
| 6925 | for (int i = 0; i < n; i++) { |
| 6926 | refined[i] = i; |
| 6927 | } |
| 6928 | return leiden_relabel(refined, n); |
| 6929 | } |
| 6930 | for (int i = 0; i < n; i++) { |
| 6931 | refined[i] = i; |
| 6932 | stot[i] = g->k[i]; |
| 6933 | rsize[i] = 1; |
| 6934 | } |
| 6935 | for (int v = 0; v < n; v++) { |
| 6936 | if (rsize[refined[v]] != 1) { |
| 6937 | continue; /* only singletons merge, per the refinement rule */ |
| 6938 | } |
| 6939 | int cv = comm[v]; |
| 6940 | int ndirty = 0; |
| 6941 | for (int e = g->off[v]; e < g->off[v + 1]; e++) { |
| 6942 | int u = g->nbr[e]; |
| 6943 | if (u == v || comm[u] != cv) { |
| 6944 | continue; /* stay within the move-phase community */ |
| 6945 | } |
| 6946 | int ru = refined[u]; |
| 6947 | if (acc[ru] == 0.0) { |
| 6948 | dirty[ndirty++] = ru; |
| 6949 | } |
| 6950 | acc[ru] += g->w[e]; |
| 6951 | } |
| 6952 | int rv = refined[v]; |
| 6953 | double kv = g->k[v]; |
| 6954 | stot[rv] -= kv; |
| 6955 | int best_r = rv; |
| 6956 | double best_gain = 0.0; |
| 6957 | for (int d = 0; d < ndirty; d++) { |
| 6958 | int r = dirty[d]; |
| 6959 | if (r == rv) { |
| 6960 | continue; |
| 6961 | } |
| 6962 | double gain = acc[r] - gamma * kv * stot[r] / twom; |
| 6963 | if (gain > best_gain) { |
| 6964 | best_gain = gain; |
| 6965 | best_r = r; |
| 6966 | } |
| 6967 | } |
| 6968 | if (best_r != rv) { |
| 6969 | refined[v] = best_r; |
| 6970 | stot[best_r] += kv; |
no test coverage detected