/** * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * . * * (c) Vincenzo Nicosia 2009-2017 -- * * This file is part of NetBunch, a package for complex network * analysis and modelling. For more information please visit: * * http://www.complex-networks.net/ * * If you use this software, please add a reference to * * V. Latora, V. Nicosia, G. Russo * "Complex Networks: Principles, Methods and Applications" * Cambridge University Press (2017) * ISBN: 9781107103184 * *********************************************************************** * * Compute all the simple cycles of an undirected graph provided as * input (optionally, only up to a certain length) using the Johnson * algorithm. * * References: * * [1] D. B. Johnson. "Finding All the Elementary Circuits of a * Directed Graph". SIAM J. Comput. 4 (1975), 77-84. * */ #include #include #include #include "utils.h" #include "stack.h" #define FALSE 0 #define TRUE 1 void usage(char *argv[]){ printf("********************************************************************\n" "** **\n" "** -*- johnson_cycles -*- **\n" "** **\n" "** Enumerates all the elementary cycles of a graph on input. **\n" "** **\n" "** If 'graph_in' is equal to '-' (dash), read the edge list **\n" "** from standard input (STDIN). **\n" "** **\n" "** If 'max_length' is provided, ignore all cycles whose length **\n" "** is larger than 'max_length' **\n" "** **\n" "** The program prints on output a row for each cycle length, **\n" "** in the format: **\n" "** **\n" "** 2 N_2 **\n" "** 3 N_3 **\n" "** 4 N_4 **\n" "** 5 N_5 **\n" "** .... **\n" "** **\n" "** where 2, 3, 4, 5, ... is the length of the cycle, and N_2, **\n" "** N_3, N_4, N_5, ... is the number of cycles of that length. **\n" "** Notice that in undirected graphs each cycle is reported **\n" "** (and counted) twice, once for each possible direction in **\n" "** which the cycle can be traversed. **\n" "** **\n" "** If the third parameter is equal to 'SHOW', the program will **\n" "** dump all the found cycles on STDERR, using the format: **\n" "** **\n" "** N_(l-1) N_(l-2)... N_0 **\n" "** **\n" "** where N_(l-i) is the i-th node in the cycle starting at node **\n" "** N_0. Notice that according to this notation we report only **\n" "** 'l' nodes belonging to the cycle, in reversed traversal **\n" "** order, avoiding to report the starting node (N_0) twice. **\n" "** **\n" "** The program shows also cycles of length '2', that in **\n" "** undirected graphs correspond to the degenerate cycle obtained **\n" "** by traversing the same edge in the two directions. **\n" "** **\n" "********************************************************************\n" " This is Free Software - You can use and distribute it under \n" " the terms of the GNU General Public License, version 3 or later\n\n" " Please visit http://www.complex-networks.net for more information\n\n" " (c) Vincenzo Nicosia 2009-2017 (v.nicosia@qmul.ac.uk)\n" "********************************************************************\n\n" ); printf("Usage: %s [ [SHOW]]\n", argv[0]); } typedef struct{ unsigned int num; double *c; } count_t; /** * * This structure contains all the information needed by the algorithm * to run: * * - N is the number of nodes * * - K is twice the number of edges * * - J_slap and r_slap are the usual SLAP representation of the * adjacency matrix * * - B_len is an array with the length of the set B(u) for each node u * * - B is a vector of character of the same size of J_slap, which * stores the values of the indicator vector B(u) for each node u * * - blocked is an indicator vector of size N * * - v is the stack of nodes * * - stats maintains the counts for all the cycle lengths * */ typedef struct{ unsigned int N; unsigned int K; unsigned int *J_slap; unsigned int *r_slap; my_stack_t *stack; int max_depth; count_t stats; unsigned int *B_len; unsigned int *B; char *blocked; char show; } algo_info_t; void unblock(algo_info_t *info, unsigned int u){ unsigned int w, idx; info->blocked[u] = FALSE; while(info->B_len[u] >0){ info->B_len[u] -= 1; idx = info->r_slap[u] + info->B_len[u]; w = info->B[idx]; if (info->blocked[w] == TRUE){ unblock(info, w); } } } /* add v to B(w) */ void set_B(algo_info_t *info, unsigned int v, unsigned int w){ unsigned int idx, i; idx = info->r_slap[w]; for(i=0; i B_len[w]; i ++){ if (info->B[idx + i] == v) return; } info->B[idx + info->B_len[w]] = v; info->B_len[w] += 1; } char circuit(algo_info_t *info, unsigned int s, unsigned int v){ unsigned int i, w; char f = FALSE; if (v < s) return FALSE; /* check if maximum depth has been reached */ if(stack_size(info->stack) == info->max_depth){ return TRUE; } stack_push(info->stack, v); info->blocked[v] = TRUE; for(i=info->r_slap[v]; i< info->r_slap[v+1]; i++){ w = info->J_slap[i]; if (w == s){ /* output a cycle here */ if (info->show) stack_dump(info->stack, stderr); info->stats.c[stack_size(info->stack)] += 1; f = TRUE; } if (w < s) continue; /* We only consider nodes with higher IDs */ else if (! (info->blocked[w])){ if (circuit(info, s, w) == TRUE) f = TRUE; } } if (f == TRUE) { unblock(info, v); } else{ for(i=info->r_slap[v]; i < info->r_slap[v+1]; i++){ w = info->J_slap[i]; if (w > s) set_B(info, v, w); /* add v to B(w) only if v is not already in B(w) */ } } stack_pop(info->stack, &w); return f; } void johnson_cycles(algo_info_t *info){ unsigned int i, N; N = info->N; for(i=0; iblocked, 0, N * sizeof(char)); /* clear the indicator vector B */ /* we reset the length of each set B*/ memset(info->B_len, 0, N * sizeof(unsigned int)); circuit(info, i, i); } } int main(int argc, char *argv[]){ algo_info_t info; FILE *filein; unsigned int i; if (argc < 2){ usage(argv); exit(1); } info.J_slap = NULL; info.r_slap = NULL; if(!strcmp(argv[1], "-")){ filein = stdin; } else{ filein = openfile_or_exit(argv[1], "r", 2); } read_slap(filein, &(info.K), &(info.N), &(info.J_slap), &(info.r_slap)); fclose(filein); info.B = malloc(info.r_slap[info.N] * sizeof(unsigned int)); /* This is the indicator vector B(u) */ info.B_len = malloc(info.N * sizeof(unsigned int)); /* lengths of the B(u) */ info.blocked = malloc(info.N * sizeof(char)); /* This is the indicator vector of blocked nodes */ info.stack = malloc(sizeof(my_stack_t)); stack_create(info.stack); info.show = FALSE; if (argc > 2){ info.max_depth = atoi(argv[2]); if (argc > 3){ if (!my_strcasecmp(argv[3], "show")){ info.show = TRUE; } } } else{ info.max_depth = info.N; } /* Here we initialise the counters */ info.stats.num = info.max_depth + 1; info.stats.c = malloc(info.stats.num * sizeof(double)); /* Here we set the counters to zero */ memset(info.stats.c, 0, info.stats.num * sizeof(double)); johnson_cycles(&info); for(i=2; iv); free(info.stack); free(info.stats.c); free(info.r_slap); free(info.J_slap); }