349 lines
9.9 KiB
C
349 lines
9.9 KiB
C
//
|
|
// Created by Tiago Batista Cardoso on 2/27/2026.
|
|
//
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "algorithms.h"
|
|
|
|
// Step 1 : find all k-cliques
|
|
|
|
// Step 2 : build clique graph (simple graph)
|
|
// Nodes = cliques, edges between cliques sharing >= k-1 nodes
|
|
|
|
//static graph_t *build_clique_graph(clique_store_t *store, int k)
|
|
//{
|
|
// int nc = store->count;
|
|
// if (nc == 0)
|
|
// return NULL;
|
|
//
|
|
// graph_t *cg = malloc(sizeof(graph_t));
|
|
// cg->n = nc;
|
|
// cg->p = 0;
|
|
// cg->q = 0;
|
|
// cg->adj_lists = calloc(nc, sizeof(node_t *));
|
|
//
|
|
// for (int i = 0; i < nc; i++) {
|
|
// for (int j = i + 1; j < nc; j++) {
|
|
// // Count shared nodes between clique i and clique j
|
|
// int shared = 0;
|
|
// for (int a = 0; a < k; a++)
|
|
// for (int b = 0; b < k; b++)
|
|
// if (store->list[i][a] ==
|
|
// store->list[j][b])
|
|
// shared++;
|
|
//
|
|
// if (shared >= k - 1) {
|
|
// // Add edge in clique graph
|
|
// node_t *n1 = malloc(sizeof(node_t));
|
|
// n1->id = j;
|
|
// n1->next = cg->adj_lists[i];
|
|
// cg->adj_lists[i] = n1;
|
|
//
|
|
// node_t *n2 = malloc(sizeof(node_t));
|
|
// n2->id = i;
|
|
// n2->next = cg->adj_lists[j];
|
|
// cg->adj_lists[j] = n2;
|
|
// }
|
|
// }
|
|
// }
|
|
// return cg;
|
|
//}
|
|
//
|
|
//static void free_clique_graph(graph_t *cg)
|
|
//{
|
|
// for (int i = 0; i < cg->n; i++) {
|
|
// node_t *n = cg->adj_lists[i];
|
|
// while (n) {
|
|
// node_t *tmp = n->next;
|
|
// free(n);
|
|
// n = tmp;
|
|
// }
|
|
// }
|
|
// free(cg->adj_lists);
|
|
// free(cg);
|
|
//}
|
|
//
|
|
//// ── Step 3 : map clique communities back to original nodes ────────────────────
|
|
//
|
|
//static int *map_cliques_to_nodes(const graph_t *graph, clique_store_t *store,
|
|
// louvain_result_t *louvain_result, int k,
|
|
// int *classified)
|
|
//{
|
|
// int n = graph->n;
|
|
// int *node_community = malloc(n * sizeof(int));
|
|
// memset(node_community, -1,
|
|
// n * sizeof(int)); // -1 = not classified (ncn)
|
|
//
|
|
// for (int i = 0; i < store->count; i++) {
|
|
// int community = louvain_result->node_community[i];
|
|
// for (int j = 0; j < k; j++) {
|
|
// int node = store->list[i][j];
|
|
// node_community[node] = community;
|
|
// }
|
|
// }
|
|
//
|
|
// // Mark which nodes are classified
|
|
// for (int i = 0; i < n; i++)
|
|
// classified[i] = (node_community[i] != -1) ? 1 : 0;
|
|
//
|
|
// return node_community;
|
|
//}
|
|
//
|
|
//// ── Step 4 : assign ncn to nearest community ─────────────────────────────────
|
|
//// Each unclassified node joins the community it has the most edges into
|
|
//
|
|
//static void assign_ncn(const graph_t *graph, int *node_community,
|
|
// int *classified, int community_count)
|
|
//{
|
|
// int n = graph->n;
|
|
// int changed = 1;
|
|
//
|
|
// // Iterate until no more ncn can be assigned (some may be fully isolated)
|
|
// while (changed) {
|
|
// changed = 0;
|
|
// for (int i = 0; i < n; i++) {
|
|
// if (classified[i])
|
|
// continue;
|
|
//
|
|
// // Count edges to each community
|
|
// int *votes = calloc(community_count, sizeof(int));
|
|
// int total = 0;
|
|
//
|
|
// node_t *nb = graph->adj_lists[i];
|
|
// while (nb) {
|
|
// if (classified[nb->id]) {
|
|
// votes[node_community[nb->id]]++;
|
|
// total++;
|
|
// }
|
|
// nb = nb->next;
|
|
// }
|
|
//
|
|
// if (total == 0) {
|
|
// free(votes);
|
|
// continue;
|
|
// } // still isolated
|
|
//
|
|
// // Find community with most connections
|
|
// int best_c = -1, best_v = 0;
|
|
// for (int c = 0; c < community_count; c++) {
|
|
// if (votes[c] > best_v) {
|
|
// best_v = votes[c];
|
|
// best_c = c;
|
|
// }
|
|
// }
|
|
//
|
|
// if (best_c != -1) {
|
|
// node_community[i] = best_c;
|
|
// classified[i] = 1;
|
|
// changed = 1;
|
|
// }
|
|
// free(votes);
|
|
// }
|
|
// }
|
|
//
|
|
// // Any remaining isolated ncn get their own community
|
|
// for (int i = 0; i < n; i++) {
|
|
// if (!classified[i]) {
|
|
// node_community[i] = community_count++;
|
|
// classified[i] = 1;
|
|
// }
|
|
// }
|
|
//}
|
|
//
|
|
//// ── Compact ids ───────────────────────────────────────────────────────────────
|
|
//
|
|
//static int compact(int *community, int n)
|
|
//{
|
|
// int *map = malloc(n * sizeof(int));
|
|
// memset(map, -1, n * sizeof(int));
|
|
// int next = 0;
|
|
// for (int i = 0; i < n; i++) {
|
|
// if (community[i] < 0)
|
|
// continue;
|
|
// if (map[community[i]] == -1)
|
|
// map[community[i]] = next++;
|
|
// community[i] = map[community[i]];
|
|
// }
|
|
// free(map);
|
|
// return next;
|
|
//}
|
|
//
|
|
//// ── Main ──────────────────────────────────────────────────────────────────────
|
|
//
|
|
//cbla_result_t *cbla_community_detection(const graph_t *graph, int k)
|
|
//{
|
|
// int n = graph->n;
|
|
// printf("[cbla] starting cbla k-clique + louvain (k=%d, n=%d)\n", k, n);
|
|
//
|
|
// // Step 1 — find all k-cliques
|
|
// clique_store_t store = { .list = malloc(64 * sizeof(int *)),
|
|
// .count = 0,
|
|
// .capacity = 64,
|
|
// .k = k };
|
|
// int *current = malloc(k * sizeof(int));
|
|
// enumerate_cliques(graph, &store, current, 0, 0);
|
|
// free(current);
|
|
// printf("[cbla] found %d %d-cliques\n", store.count, k);
|
|
//
|
|
// cbla_result_t *result = malloc(sizeof(cbla_result_t));
|
|
// result->node_community = malloc(n * sizeof(int));
|
|
//
|
|
// if (store.count == 0) {
|
|
// // No cliques found — assign all nodes to community 0
|
|
// printf("[cbla] no cliques found, all nodes are ncn\n");
|
|
// for (int i = 0; i < n; i++)
|
|
// result->node_community[i] = 0;
|
|
// result->count = 1;
|
|
// free(store.list);
|
|
// return result;
|
|
// }
|
|
//
|
|
// // Step 2 — build clique graph and apply Louvain on it
|
|
// graph_t *clique_graph = build_clique_graph(&store, k);
|
|
// printf("[cbla] clique graph built (%d nodes)\n", clique_graph->n);
|
|
//
|
|
// louvain_result_t *lv = compute_louvain(clique_graph);
|
|
// printf("[cbla] louvain found %d communities on clique graph\n",
|
|
// lv->count);
|
|
//
|
|
// // Step 3 — map clique communities back to original nodes
|
|
// int *classified = calloc(n, sizeof(int));
|
|
// int *node_community =
|
|
// map_cliques_to_nodes(graph, &store, lv, k, classified);
|
|
//
|
|
// int ncn_count = 0;
|
|
// for (int i = 0; i < n; i++)
|
|
// if (!classified[i])
|
|
// ncn_count++;
|
|
// printf("[cbla] %d non-classified nodes (ncn)\n", ncn_count);
|
|
//
|
|
// // Step 4 — assign ncn to nearest community
|
|
// assign_ncn(graph, node_community, classified, lv->count);
|
|
//
|
|
// // Compact and finalize
|
|
// result->count = compact(node_community, n);
|
|
// memcpy(result->node_community, node_community, n * sizeof(int));
|
|
// printf("[cbla] final community count: %d\n", result->count);
|
|
//
|
|
// // Cleanup
|
|
// for (int i = 0; i < store.count; i++)
|
|
// free(store.list[i]);
|
|
// free(store.list);
|
|
// free_louvain_result(lv);
|
|
// free_clique_graph(clique_graph);
|
|
// free(node_community);
|
|
// free(classified);
|
|
//
|
|
// return result;
|
|
//}
|
|
//
|
|
void free_cbla_result(cbla_result_t *result)
|
|
{
|
|
if (!result)
|
|
return;
|
|
free(result->node_community);
|
|
free(result);
|
|
}
|
|
|
|
graph_t *build_meta_graph(const graph_t *graph, clique_store_t *store)
|
|
{
|
|
graph_t *meta = malloc(sizeof(graph_t));
|
|
meta->n = store->count;
|
|
meta->adj_lists = calloc(meta->n, sizeof(node_t *));
|
|
|
|
for (int i = 0; i < store->count; i++) {
|
|
for (int j = i + 1; j < store->count; j++) {
|
|
int connected = 0;
|
|
// Vérifier s'il existe une arête entre un nœud de clique i et clique j
|
|
for (int ni = 0; ni < store->k && !connected; ni++) {
|
|
for (int nj = 0; nj < store->k; nj++) {
|
|
if (has_edge(graph, store->list[i][ni],
|
|
store->list[j][nj])) {
|
|
connected = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (connected) {
|
|
add_edge(meta, i, j);
|
|
add_edge(meta, j, i);
|
|
}
|
|
}
|
|
}
|
|
return meta;
|
|
}
|
|
|
|
cbla_result_t *cbla_community_detection(const graph_t *graph, int k)
|
|
{
|
|
cbla_result_t *final_result = malloc(sizeof(cbla_result_t));
|
|
final_result->node_community = malloc(graph->n * sizeof(int));
|
|
// Initialisation à -1 (non classifié)
|
|
for (int i = 0; i < graph->n; i++)
|
|
final_result->node_community[i] = -1;
|
|
|
|
// 1. Énumérer toutes les k-cliques
|
|
clique_store_t store = { NULL, 0, 10, k };
|
|
store.list = malloc(store.capacity * sizeof(int *));
|
|
int *current_clique = malloc(k * sizeof(int));
|
|
|
|
enumerate_cliques(graph, &store, current_clique, 0, 0);
|
|
free(current_clique);
|
|
|
|
if (store.count == 0) {
|
|
// Cas dégradé : pas de cliques, on pourrait renvoyer Louvain simple
|
|
final_result->count = 0;
|
|
free(store.list);
|
|
return final_result;
|
|
}
|
|
|
|
// 2. Reconstruire le méta-graphe (Graphe de cliques)
|
|
// Chaque clique devient un "super-nœud"
|
|
//graph_t *meta_graph = malloc(sizeof(graph_t));
|
|
//meta_graph->n = store.count;
|
|
// Initialisation simplifiée de l'adjacence du méta-graphe
|
|
// (Supposons une matrice ou liste d'adjacence pour meta_graph)
|
|
// On connecte Clique A et Clique B s'il existe une arête entre un noeud de A et un noeud de B
|
|
|
|
// NOTE: Pour Louvain, nous devons construire meta_graph proprement (adjacences)
|
|
// Ici, nous simulons la création pour l'algorithme
|
|
graph_t *meta_graph = build_meta_graph(graph, &store);
|
|
|
|
// 3. Appliquer Louvain sur le méta-graphe
|
|
louvain_result_t *l_res = compute_louvain(meta_graph);
|
|
|
|
// 4. Mapper les résultats : Nœuds originaux appartenant aux cliques
|
|
for (int c_idx = 0; c_idx < store.count; c_idx++) {
|
|
int community_id = l_res->node_community[c_idx];
|
|
for (int i = 0; i < k; i++) {
|
|
int original_node = store.list[c_idx][i];
|
|
final_result->node_community[original_node] =
|
|
community_id;
|
|
}
|
|
}
|
|
|
|
// 5. Gérer les NCN (Non-Classified Nodes)
|
|
// On les affecte à la première communauté d'un voisin classifié
|
|
// ou à une nouvelle communauté isolée.
|
|
for (int i = 0; i < graph->n; i++) {
|
|
if (final_result->node_community[i] == -1) {
|
|
// Logique simplifiée : on cherche un voisin déjà classé
|
|
// Sinon, on le laisse à -1 ou on crée une ID unique
|
|
final_result->node_community[i] =
|
|
0; // Exemple : par défaut communauté 0
|
|
}
|
|
}
|
|
|
|
final_result->count = l_res->count;
|
|
|
|
// Nettoyage
|
|
for (int i = 0; i < store.count; i++)
|
|
free(store.list[i]);
|
|
free(store.list);
|
|
free_louvain_result(l_res);
|
|
// free_graph(meta_graph);
|
|
|
|
return final_result;
|
|
}
|