\n\n"
+ "Options:\n"
+ " -fig1 preset: n=20, p=0.9, q=0.25\n"
+ " -fig2 preset: n=50, p=0.7, q=0.15\n"
+ " -fig3 preset: n=100, p=0.5, q=0.05\n"
+ " -fig4 preset: n=200, p=0.2, q=0.02\n"
+ " -fig5 preset: n=500, p=0.01, q=0.00025\n"
+ " -b run benchmark\n"
+ " n number of nodes\n"
+ " p intra-group edge probability\n"
+ " q inter-group edge probability\n"
+ " algo clique | louvain | cbla\n",
+ prog, prog, prog);
+}
- SDL_Init(SDL_INIT_VIDEO);
+typedef struct {
+ int n;
+ double p;
+ double q;
+ int seed;
+ int k;
+ VISUALIZATION_TYPE algo;
+} run_config_t;
- SDL_Window *window = SDL_CreateWindow(
- "Graph Render", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
- WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
- SDL_Renderer *renderer =
- SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+static int parse_algo(const char *s, VISUALIZATION_TYPE *out)
+{
+ if (strcmp(s, "clique") == 0) {
+ *out = CLIQUE;
+ return 1;
+ } else if (strcmp(s, "louvain") == 0) {
+ *out = LOUVAIN;
+ return 1;
+ } else if (strcmp(s, "cbla") == 0) {
+ *out = CBLA;
+ return 1;
+ }
+ return 0;
+}
- // Figure 2
- render_graph(renderer, g, LOUVAIN);
+static int parse_fig(const char *arg, run_config_t *cfg)
+{
+ if (strcmp(arg, "-fig1") == 0) {
+ cfg->n = 20;
+ cfg->p = 1.0;
+ cfg->q = 0.04;
+ cfg->k = 3;
+ cfg->seed = 122;
+ cfg->algo = CLIQUE;
+ return 1;
+ } else if (strcmp(arg, "-fig2") == 0) {
+ cfg->n = 20;
+ cfg->q = 1.0;
+ cfg->p = 0.04;
+ cfg->k = 3;
+ cfg->seed = 122;
+ cfg->algo = CLIQUE;
+ return 1;
+ } else if (strcmp(arg, "-fig3") == 0) {
+ cfg->n = 20;
+ cfg->p = 0.04;
+ cfg->q = 0.04;
+ cfg->k = 3;
+ cfg->seed = 122;
+ cfg->algo = CLIQUE;
+ return 1;
+ } else if (strcmp(arg, "-fig4") == 0) {
+ cfg->n = 20;
+ cfg->p = 0.8;
+ cfg->q = 0.10;
+ cfg->k = 0;
+ cfg->seed = 122;
+ cfg->algo = LOUVAIN;
+ return 1;
+ } else if (strcmp(arg, "-fig5") == 0) {
+ cfg->n = 20;
+ cfg->p = 0.8;
+ cfg->q = 0.10;
+ cfg->k = 3;
+ cfg->seed = 122;
+ cfg->algo = CBLA;
+ return 1;
+ }
+ return 0;
+}
+static void run_sdl(run_config_t *cfg)
+{
+ printf("[main] generating graph n=%d p=%.4f q=%.6f\n", cfg->n, cfg->p,
+ cfg->q);
+
+ graph_t *graph = generate_graph(cfg->n, cfg->p, cfg->q, cfg->seed);
+ if (!graph) {
+ fprintf(stderr, "[main] generate_graph failed\n");
+ return;
+ }
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
+ free_graph(graph);
+ return;
+ }
+ SDL_Window *window =
+ SDL_CreateWindow("Graph Visualizer", SDL_WINDOWPOS_CENTERED,
+ SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH,
+ WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
+
+ SDL_Renderer *renderer = SDL_CreateRenderer(
+ window, -1,
+ SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
+
+ render_graph(renderer, graph, cfg->algo, cfg->k);
+
+ // Event loop
SDL_Event e;
int running = 1;
while (running) {
- while (SDL_PollEvent(&e))
+ while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT)
running = 0;
+ if (e.type == SDL_KEYDOWN &&
+ e.key.keysym.sym == SDLK_ESCAPE)
+ running = 0;
+ // Re-render on window resize
+ if (e.type == SDL_WINDOWEVENT &&
+ e.window.event == SDL_WINDOWEVENT_RESIZED)
+ render_graph(renderer, graph, cfg->algo,
+ cfg->k);
+ }
SDL_Delay(16);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
+ free_graph(graph);
+}
- free_graph(g);
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ if (strcmp(argv[1], "-b") == 0) {
+ run_benchmark();
+ return 0;
+ }
+
+ run_config_t cfg = {
+ .n = 0, .p = 0, .q = 0, .seed = 42, .algo = CLIQUE
+ };
+
+ if (parse_fig(argv[1], &cfg)) {
+ // optionally override algo: ./graphe -fig3 louvain
+ if (argc >= 3 && !parse_algo(argv[2], &cfg.algo)) {
+ fprintf(stderr, "[main] unknown algo '%s'\n", argv[2]);
+ print_usage(argv[0]);
+ return 1;
+ }
+ run_sdl(&cfg);
+ return 0;
+ }
+
+ if (argc < 5) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ cfg.n = atoi(argv[1]);
+ cfg.p = atof(argv[2]);
+ cfg.q = atof(argv[3]);
+
+ if (cfg.n <= 0 || cfg.p < 0 || cfg.p > 1 || cfg.q < 0 || cfg.q > 1) {
+ fprintf(stderr,
+ "[main] invalid arguments: n must be > 0, p and q in [0,1]\n");
+ return 1;
+ }
+ if (!parse_algo(argv[4], &cfg.algo)) {
+ fprintf(stderr, "[main] unknown algo '%s'\n", argv[4]);
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ run_sdl(&cfg);
+ return 0;
return 0;
}
diff --git a/render.c b/render.c
index ae6391b..13f7652 100644
--- a/render.c
+++ b/render.c
@@ -132,7 +132,7 @@ static SDL_Color community_colors[] = {
#define N_COLORS (sizeof(community_colors) / sizeof(community_colors[0]))
void render_graph(SDL_Renderer *renderer, const graph_t *graph,
- VISUALIZATION_TYPE type)
+ VISUALIZATION_TYPE type, int k)
{
if (!renderer || !graph || !graph->adj_lists)
return;
@@ -145,14 +145,18 @@ void render_graph(SDL_Renderer *renderer, const graph_t *graph,
community_result_t *communities = NULL;
louvain_result_t *louvain = NULL;
+ cbla_result_t *cbla = NULL;
switch (type) {
case CLIQUE:
- communities = find_k_clique_communities(graph, 3);
+ communities = find_k_clique_communities(graph, k);
break;
case LOUVAIN:
louvain = compute_louvain(graph);
break;
+ case CBLA:
+ cbla = cbla_community_detection(graph, k);
+ break;
}
SDL_SetRenderDrawColor(renderer, 48, 48, 48, 255);
@@ -193,6 +197,11 @@ void render_graph(SDL_Renderer *renderer, const graph_t *graph,
col = (c == -1) ? (SDL_Color){ 168, 153, 132, 255 } :
community_colors[c % N_COLORS];
break;
+ case CBLA:
+ c = cbla->node_community[i];
+ col = (c == -1) ? (SDL_Color){ 168, 153, 132, 255 } :
+ community_colors[c % N_COLORS];
+ break;
}
SDL_SetRenderDrawColor(renderer, col.r, col.g, col.b, col.a);
@@ -206,6 +215,9 @@ void render_graph(SDL_Renderer *renderer, const graph_t *graph,
if (louvain != NULL) {
free_louvain_result(louvain);
}
+ if (cbla != NULL) {
+ free_cbla_result(cbla);
+ }
free(layout);
SDL_RenderPresent(renderer);
diff --git a/render.h b/render.h
index a645d59..1ace07b 100644
--- a/render.h
+++ b/render.h
@@ -8,10 +8,10 @@
#include "structs.h"
#include
-enum VISUALIZATION_TYPE { CLIQUE, LOUVAIN };
+enum VISUALIZATION_TYPE { CLIQUE, LOUVAIN, CBLA };
typedef enum VISUALIZATION_TYPE VISUALIZATION_TYPE;
void render_graph(SDL_Renderer *renderer, const graph_t *graph,
- VISUALIZATION_TYPE);
+ VISUALIZATION_TYPE, int k);
#endif
diff --git a/structs.c b/structs.c
index 4981a3e..f00bb76 100644
--- a/structs.c
+++ b/structs.c
@@ -52,6 +52,17 @@ void add_edge(graph_t *graph, int src, int dest)
graph->adj_lists[dest] = new_node;
}
+int has_edge(const graph_t *graph, int u, int v)
+{
+ node_t *n = graph->adj_lists[u];
+ while (n) {
+ if (n->id == v)
+ return 1;
+ n = n->next;
+ }
+ return 0;
+}
+
graph_t *basic_graph()
{
clock_t start, end;
diff --git a/structs.h b/structs.h
index f0637b0..1d4bd4d 100644
--- a/structs.h
+++ b/structs.h
@@ -20,6 +20,7 @@ struct graph_t {
typedef struct graph_t graph_t;
// structure-related functions
+int has_edge(const graph_t *graph, int u, int v);
node_t *create_node(int id);
graph_t *create_graph(int n, double p, double q);
void add_edge(graph_t *graph, int src, int dest);