From 8c52ebcbf870debad6733a7513be6eb4390da0f6 Mon Sep 17 00:00:00 2001 From: Brent Ewing Date: Fri, 2 Oct 2020 11:54:57 -0700 Subject: [PATCH] leidenbase version 0.1.2 --- DESCRIPTION | 8 +- README.md | 24 +++ configure | 18 +- configure.ac | 2 +- src/Makevars.in | 4 +- src/Makevars.win | 4 +- src/cigraph/include/igraph_arpack.h | 52 +++--- src/cigraph/include/igraph_vector_pmt.h | 1 + src/cigraph/include/igraph_version.h | 2 +- src/cigraph/src/adjlist.c | 56 +++--- src/cigraph/src/bliss.cc | 83 +++++---- src/cigraph/src/bliss/bignum.hh | 4 +- src/cigraph/src/bliss/bliss_heap.cc | 6 +- src/cigraph/src/bliss/defs.cc | 12 +- src/cigraph/src/bliss/defs.hh | 7 - src/cigraph/src/bliss/graph.cc | 57 ++---- src/cigraph/src/bliss/graph.hh | 10 +- src/cigraph/src/bliss/orbit.cc | 4 +- src/cigraph/src/bliss/partition.cc | 6 +- src/cigraph/src/bliss/partition.hh | 6 +- src/cigraph/src/bliss/uintseqhash.hh | 2 +- src/cigraph/src/bliss/utils.cc | 2 + src/cigraph/src/bliss/utils.hh | 6 +- src/cigraph/src/centrality.c | 58 +++---- src/cigraph/src/clustertool.cpp | 191 +++++++++++---------- src/cigraph/src/coloring.c | 2 +- src/cigraph/src/community_leiden.c | 188 +++++++++----------- src/cigraph/src/degree_sequence.cpp | 58 ++++--- src/cigraph/src/drl_layout.cpp | 22 ++- src/cigraph/src/drl_layout_3d.cpp | 23 +-- src/cigraph/src/foreign.c | 8 +- src/cigraph/src/games.c | 11 +- src/cigraph/src/gengraph_mr-connected.cpp | 80 +++++---- src/cigraph/src/igraph_blas_internal.h | 4 +- src/cigraph/src/igraph_handle_exceptions.h | 14 ++ src/cigraph/src/igraph_isoclasses.h | 54 ++++++ src/cigraph/src/layout.c | 177 ++++++++++++++----- src/cigraph/src/layout_kk.c | 26 ++- src/cigraph/src/motifs.c | 26 +-- src/cigraph/src/operators.c | 14 ++ src/cigraph/src/random.c | 14 +- src/cigraph/src/scg.c | 6 +- src/cigraph/src/structural_properties.c | 22 ++- src/cigraph/src/structure_generators.c | 20 ++- src/cigraph/src/topology.c | 37 ++-- src/cigraph/src/vector.pmt | 40 ++++- src/igraph_rinterface.cpp | 11 ++ src/leidenFindPartitionR2C.cpp | 4 +- 48 files changed, 870 insertions(+), 616 deletions(-) create mode 100644 src/cigraph/src/igraph_handle_exceptions.h create mode 100644 src/cigraph/src/igraph_isoclasses.h diff --git a/DESCRIPTION b/DESCRIPTION index 27e35f7..8375130 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: leidenbase Type: Package Title: R and C wrappers to run the Leiden find_partition function -Version: 0.1.1 +Version: 0.1.2 Authors@R: person( 'Brent', 'Ewing', role = c( 'aut', 'cre' ), email = 'bge@uw.edu' ) Description: An R to C interface that runs the Leiden community detection algorithm to find a basic partition. It runs the @@ -20,13 +20,13 @@ Description: An R to C interface that runs the Leiden community Scientific Reports (2019), DOI: 10.1038/s41598-019-41695-z. Requires: R (>= 3.0.0) -Imports: igraph (>= 1.1.0) +Imports: igraph (>= 0.8.2) License: GNU PUBLIC LICENSE 2 and GNU PUBLIC LICENSE 3 Encoding: UTF-8 LazyData: true -RoxygenNote: 7.1.0 +RoxygenNote: 7.1.1 Suggests: testthat NeedsCompilation: yes -Packaged: 2020-07-10 20:45:17 UTC; brent +Packaged: 2020-10-02 18:40:44 UTC; brent Author: Brent Ewing [aut, cre] Maintainer: Brent Ewing diff --git a/README.md b/README.md index 718b9ee..4d3f7f6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,30 @@ Use the devtools::install_github() command devtools::install_github('cole-trapnell-lab/leidenbase') ``` +### Tested conditions + +#### Linux Debian 10 Buster + +R distribution: Debian packages +C/C++ compilers: Debian distribution packages +FORTRAN compiler: Debian distribution packages + +#### MacOS 10.15 Catalina + +R distribution: CRAN package from https://cran.r-project.org/bin/macosx/R-4.0.2.pkg +C/C++ compilers: Apple Xcode 12.0 +FORTRAN compiler: gfortran 10.2 from https://github.com/fxcoudert/gfortran-for-macOS/releases + +#### MicroSoft Windows 10 + +R distribution: CRAN package from https://cran.r-project.org/bin/windows/base/R-4.0.2-win.exe +C/C++ compilers: CRAN Rtools40 from https://cran.r-project.org/bin/windows/Rtools/rtools40-x86_64.exe +FORTRAN compiler: CRAN Rtools40 from https://cran.r-project.org/bin/windows/Rtools/rtools40-x86_64.exe + +#### Suggestions + +Try to use compatible C/C++ and FORTRAN compilers. For example, if you install R using a CRAN package, try to use the R compiler tools, and if you use *brew* on MacOS to install R, try to use brew-installed compilers. + ### Leidenbase functions The R wrapper for running the Leiden algorithm find partition function is diff --git a/configure b/configure index 285de77..ee11c31 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for leidenbase 0.1.0. +# Generated by GNU Autoconf 2.69 for leidenbase 0.1.2. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='leidenbase' PACKAGE_TARNAME='leidenbase' -PACKAGE_VERSION='0.1.0' -PACKAGE_STRING='leidenbase 0.1.0' +PACKAGE_VERSION='0.1.2' +PACKAGE_STRING='leidenbase 0.1.2' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1253,7 +1253,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures leidenbase 0.1.0 to adapt to many kinds of systems. +\`configure' configures leidenbase 0.1.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1315,7 +1315,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of leidenbase 0.1.0:";; + short | recursive ) echo "Configuration of leidenbase 0.1.2:";; esac cat <<\_ACEOF @@ -1405,7 +1405,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -leidenbase configure 0.1.0 +leidenbase configure 0.1.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2081,7 +2081,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by leidenbase $as_me 0.1.0, which was +It was created by leidenbase $as_me 0.1.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -4935,7 +4935,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by leidenbase $as_me 0.1.0, which was +This file was extended by leidenbase $as_me 0.1.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -4988,7 +4988,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -leidenbase config.status 0.1.0 +leidenbase config.status 0.1.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 98d4243..b0929fe 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ dnl We acknowledge Gabor Csardi's work on the R/C igraph dnl distributions and for making the source code publicly dnl and freely available. dnl -AC_INIT([leidenbase], 0.1.0) +AC_INIT([leidenbase], 0.1.2) AC_CONFIG_SRCDIR(src/leidenFindPartition.cpp) : ${R_HOME=`R RHOME`} diff --git a/src/Makevars.in b/src/Makevars.in index 1302fc0..fd24b47 100644 --- a/src/Makevars.in +++ b/src/Makevars.in @@ -17,7 +17,7 @@ PKG_CFLAGS=-I. \ -Icigraph/src/AMD/Include \ -Icigraph/src/COLAMD/Include \ -Icigraph/src/SuiteSparse_config \ - @CPPFLAGS@ @CFLAGS@ -DNDEBUG -DNPARTITION -DNTIMER -DNCAMD -DNPRINT\ + @CPPFLAGS@ @CFLAGS@ -DUSING_R -DNDEBUG -DNPARTITION -DNTIMER -DNCAMD -DNPRINT\ -DPACKAGE_VERSION=\"0.8.2\" -DINTERNAL_ARPACK \ -DIGRAPH_THREAD_LOCAL=/**/ PKG_CXXFLAGS= -DIGRAPH_THREAD_LOCAL=/**/ -DNDEBUG \ @@ -26,7 +26,7 @@ PKG_CXXFLAGS= -DIGRAPH_THREAD_LOCAL=/**/ -DNDEBUG \ -Icigraph/include \ -Icigraph/src/prpack \ -Ileidenalg/include \ - -DPRPACK_IGRAPH_SUPPORT + -DUSING_R -DPRPACK_IGRAPH_SUPPORT PKG_LIBS=@XML2_LIBS@ @GMP_LIBS@ @GLPK_LIBS@ $(FLIBS) $(LAPACK_LIBS) $(BLAS_LIBS) all: $(SHLIB) diff --git a/src/Makevars.win b/src/Makevars.win index 43207d8..f589190 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -17,7 +17,7 @@ PKG_CPPFLAGS=-I. \ -Icigraph/src/AMD/Include \ -Icigraph/src/COLAMD/Include \ -Icigraph/src/SuiteSparse_config \ - -DNDEBUG -DNPARTITION -DNTIMER -DNCAMD -DNPRINT\ + -DUSING_R -DNDEBUG -DNPARTITION -DNTIMER -DNCAMD -DNPRINT\ -DPACKAGE_VERSION=\"1.1.2.9000\" -DINTERNAL_ARPACK \ -DIGRAPH_THREAD_LOCAL= \ -DHAVE_GFORTRAN @@ -27,7 +27,7 @@ PKG_CXXFLAGS= -DIGRAPH_THREAD_LOCAL= -DNDEBUG \ -Icigraph/include \ -Icigraph/src/prpack \ -Ileidenalg/include \ - -DPRPACK_IGRAPH_SUPPORT + -DUSING_R -DPRPACK_IGRAPH_SUPPORT PKG_LIBS= -liconv -lz -lws2_32 -lgfortran -lquadmath $(BLAS_LIBS) $(LAPACK_LIBS) all: $(SHLIB) diff --git a/src/cigraph/include/igraph_arpack.h b/src/cigraph/include/igraph_arpack.h index 91d2a50..71fab85 100644 --- a/src/cigraph/include/igraph_arpack.h +++ b/src/cigraph/include/igraph_arpack.h @@ -222,34 +222,34 @@ __BEGIN_DECLS typedef struct igraph_arpack_options_t { /* INPUT */ - char bmat[1]; /* I-standard problem, G-generalized */ - int n; /* Dimension of the eigenproblem */ - char which[2]; /* LA, SA, LM, SM, BE */ - int nev; /* Number of eigenvalues to be computed */ - igraph_real_t tol; /* Stopping criterion */ - int ncv; /* Number of columns in V */ - int ldv; /* Leading dimension of V */ - int ishift; /* 0-reverse comm., 1-exact with tridiagonal */ - int mxiter; /* Maximum number of update iterations to take */ - int nb; /* Block size on the recurrence, only 1 works */ - int mode; /* The kind of problem to be solved (1-5) - 1: A*x=l*x, A symmetric - 2: A*x=l*M*x, A symm. M pos. def. - 3: K*x = l*M*x, K symm., M pos. semidef. - 4: K*x = l*KG*x, K s. pos. semidef. KG s. indef. - 5: A*x = l*M*x, A symm., M symm. pos. semidef. */ - int start; /* 0: random, 1: use the supplied vector */ - int lworkl; /* Size of temporary storage, default is fine */ - igraph_real_t sigma; /* The shift for modes 3,4,5 */ - igraph_real_t sigmai; /* The imaginary part of shift for rnsolve */ + char bmat[1]; /* I-standard problem, G-generalized */ + int n; /* Dimension of the eigenproblem */ + char which[2]; /* LA, SA, LM, SM, BE */ + int nev; /* Number of eigenvalues to be computed */ + igraph_real_t tol; /* Stopping criterion */ + int ncv; /* Number of columns in V */ + int ldv; /* Leading dimension of V */ + int ishift; /* 0-reverse comm., 1-exact with tridiagonal */ + int mxiter; /* Maximum number of update iterations to take */ + int nb; /* Block size on the recurrence, only 1 works */ + int mode; /* The kind of problem to be solved (1-5) + 1: A*x=l*x, A symmetric + 2: A*x=l*M*x, A symm. M pos. def. + 3: K*x = l*M*x, K symm., M pos. semidef. + 4: K*x = l*KG*x, K s. pos. semidef. KG s. indef. + 5: A*x = l*M*x, A symm., M symm. pos. semidef. */ + int start; /* 0: random, 1: use the supplied vector */ + int lworkl; /* Size of temporary storage, default is fine */ + igraph_real_t sigma; /* The shift for modes 3,4,5 */ + igraph_real_t sigmai; /* The imaginary part of shift for rnsolve */ /* OUTPUT */ - int info; /* What happened, see docs */ - int ierr; /* What happened in the dseupd call */ - int noiter; /* The number of iterations taken */ + int info; /* What happened, see docs */ + int ierr; /* What happened in the dseupd call */ + int noiter; /* The number of iterations taken */ int nconv; - int numop; /* Number of OP*x operations */ - int numopb; /* Number of B*x operations if BMAT='G' */ - int numreo; /* Number of steps of re-orthogonalizations */ + int numop; /* Number of OP*x operations */ + int numopb; /* Number of B*x operations if BMAT='G' */ + int numreo; /* Number of steps of re-orthogonalizations */ /* INTERNAL */ int iparam[11]; int ipntr[14]; diff --git a/src/cigraph/include/igraph_vector_pmt.h b/src/cigraph/include/igraph_vector_pmt.h index 6fa7a2b..3bf2a4d 100644 --- a/src/cigraph/include/igraph_vector_pmt.h +++ b/src/cigraph/include/igraph_vector_pmt.h @@ -205,6 +205,7 @@ DECLDIR void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, /*-----------*/ DECLDIR void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v); +DECLDIR void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v); DECLDIR long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, igraph_vector_t *inds, igraph_bool_t descending); diff --git a/src/cigraph/include/igraph_version.h b/src/cigraph/include/igraph_version.h index abc7c99..1e15b74 100644 --- a/src/cigraph/include/igraph_version.h +++ b/src/cigraph/include/igraph_version.h @@ -34,7 +34,7 @@ __BEGIN_DECLS #define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ #define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" -int igraph_version(const char **version_string, +DECLDIR int igraph_version(const char **version_string, int *major, int *minor, int *subminor); diff --git a/src/cigraph/src/adjlist.c b/src/cigraph/src/adjlist.c index 9fbd5e2..53fab88 100644 --- a/src/cigraph/src/adjlist.c +++ b/src/cigraph/src/adjlist.c @@ -360,21 +360,27 @@ int igraph_adjlist_simplify(igraph_adjlist_t *al) { int igraph_adjlist_remove_duplicate(const igraph_t *graph, igraph_adjlist_t *al) { - long int i; - long int n = al->length; + long int i, j, l, n, p; + igraph_vector_int_t *v; + IGRAPH_UNUSED(graph); + + n = al->length; for (i = 0; i < n; i++) { - igraph_vector_int_t *v = &al->adjs[i]; - long int j, p = 1, l = igraph_vector_int_size(v); - for (j = 1; j < l; j++) { - long int e = (long int) VECTOR(*v)[j]; - /* Non-loop edges, and one end of loop edges are fine. */ - /* We use here, that the vector is sorted and we also keep it sorted */ - if (e != i || VECTOR(*v)[j - 1] != e) { - VECTOR(*v)[p++] = e; + v = &al->adjs[i]; + l = igraph_vector_int_size(v); + if (l > 0) { + p = 1; + for (j = 1; j < l; j++) { + long int e = (long int) VECTOR(*v)[j]; + /* Non-loop edges, and one end of loop edges are fine. */ + /* We assume that the vector is sorted and we also keep it sorted */ + if (e != i || VECTOR(*v)[j - 1] != e) { + VECTOR(*v)[p++] = e; + } } + igraph_vector_int_resize(v, p); } - igraph_vector_int_resize(v, p); } return 0; @@ -501,21 +507,25 @@ void igraph_adjedgelist_destroy(igraph_inclist_t *il) { int igraph_inclist_remove_duplicate(const igraph_t *graph, igraph_inclist_t *al) { - long int i; - long int n = al->length; + long int i, j, l, n, p; + igraph_vector_int_t* v; + + n = al->length; for (i = 0; i < n; i++) { - igraph_vector_int_t *v = &al->incs[i]; - long int j, p = 1, l = igraph_vector_int_size(v); - for (j = 1; j < l; j++) { - long int e = (long int) VECTOR(*v)[j]; - /* Non-loop edges and one end of loop edges are fine. */ - /* We use here, that the vector is sorted and we also keep it sorted */ - if (IGRAPH_FROM(graph, e) != IGRAPH_TO(graph, e) || - VECTOR(*v)[j - 1] != e) { - VECTOR(*v)[p++] = e; + v = &al->incs[i]; + l = igraph_vector_int_size(v); + if (l > 0) { + p = 1; + for (j = 1; j < l; j++) { + long int e = (long int) VECTOR(*v)[j]; + /* Non-loop edges and one end of loop edges are fine. */ + /* We assume that the vector is sorted and we also keep it sorted */ + if (VECTOR(*v)[j - 1] != e) { + VECTOR(*v)[p++] = e; + } } + igraph_vector_int_resize(v, p); } - igraph_vector_int_resize(v, p); } return 0; diff --git a/src/cigraph/src/bliss.cc b/src/cigraph/src/bliss.cc index 72c1128..564809b 100644 --- a/src/cigraph/src/bliss.cc +++ b/src/cigraph/src/bliss.cc @@ -25,6 +25,7 @@ #include "igraph_datatype.h" #include "igraph_interface.h" +#include "igraph_handle_exceptions.h" using namespace bliss; using namespace std; @@ -53,7 +54,7 @@ inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) { } -void bliss_free_graph(AbstractGraph *g) { +static void bliss_free_graph(AbstractGraph *g) { delete g; } @@ -118,7 +119,7 @@ inline void bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) // this is the callback function used with AbstractGraph::find_automorphisms() // it collects the group generators into a pointer vector -void collect_generators(void *generators, unsigned int n, const unsigned int *aut) { +static void collect_generators(void *generators, unsigned int n, const unsigned int *aut) { igraph_vector_ptr_t *gen = static_cast(generators); igraph_vector_t *newvector = igraph_Calloc(1, igraph_vector_t); igraph_vector_init(newvector, n); @@ -153,24 +154,27 @@ void collect_generators(void *generators, unsigned int n, const unsigned int *au */ int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { - AbstractGraph *g = bliss_from_igraph(graph); - IGRAPH_FINALLY(bliss_free_graph, g); - const unsigned int N = g->get_nof_vertices(); - - IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); - IGRAPH_CHECK(bliss_set_colors(g, colors)); - - Stats stats; - const unsigned int *cl = g->canonical_form(stats, NULL, NULL); - IGRAPH_CHECK(igraph_vector_resize(labeling, N)); - for (unsigned int i = 0; i < N; i++) { - VECTOR(*labeling)[i] = cl[i]; - } + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); + const unsigned int N = g->get_nof_vertices(); + + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); + + Stats stats; + const unsigned int *cl = g->canonical_form(stats, NULL, NULL); + IGRAPH_CHECK(igraph_vector_resize(labeling, N)); + for (unsigned int i = 0; i < N; i++) { + VECTOR(*labeling)[i] = cl[i]; + } - bliss_info_to_igraph(info, stats); + bliss_info_to_igraph(info, stats); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); - delete g; - IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -199,19 +203,21 @@ int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_ */ int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { - AbstractGraph *g = bliss_from_igraph(graph); - IGRAPH_FINALLY(bliss_free_graph, g); + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); - IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); - IGRAPH_CHECK(bliss_set_colors(g, colors)); + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); - Stats stats; - g->find_automorphisms(stats, NULL, NULL); + Stats stats; + g->find_automorphisms(stats, NULL, NULL); - bliss_info_to_igraph(info, stats); + bliss_info_to_igraph(info, stats); - delete g; - IGRAPH_FINALLY_CLEAN(1); + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); return IGRAPH_SUCCESS; } @@ -241,20 +247,23 @@ int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *color int igraph_automorphism_group( const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_ptr_t *generators, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { - AbstractGraph *g = bliss_from_igraph(graph); - IGRAPH_FINALLY(bliss_free_graph, g); + IGRAPH_HANDLE_EXCEPTIONS( + AbstractGraph *g = bliss_from_igraph(graph); + IGRAPH_FINALLY(bliss_free_graph, g); - IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); - IGRAPH_CHECK(bliss_set_colors(g, colors)); + IGRAPH_CHECK(bliss_set_sh(g, sh, igraph_is_directed(graph))); + IGRAPH_CHECK(bliss_set_colors(g, colors)); - Stats stats; - igraph_vector_ptr_resize(generators, 0); - g->find_automorphisms(stats, collect_generators, generators); + Stats stats; + igraph_vector_ptr_resize(generators, 0); + g->find_automorphisms(stats, collect_generators, generators); - bliss_info_to_igraph(info, stats); + bliss_info_to_igraph(info, stats); + + delete g; + IGRAPH_FINALLY_CLEAN(1); + ); - delete g; - IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } diff --git a/src/cigraph/src/bliss/bignum.hh b/src/cigraph/src/bliss/bignum.hh index ff8250c..943d130 100644 --- a/src/cigraph/src/bliss/bignum.hh +++ b/src/cigraph/src/bliss/bignum.hh @@ -21,7 +21,7 @@ */ #include -#include +// #include #include #include #include @@ -111,7 +111,7 @@ public: /** * Print the number in the file stream \a fp. */ - size_t print(FILE* const fp) const {return fprintf(fp, "%Lg", v); } + // size_t print(FILE* const fp) const {return fprintf(fp, "%Lg", v); } int tostring(char **str) const { int size=static_cast( (std::log(std::abs(v))/std::log(10.0))+4 ); diff --git a/src/cigraph/src/bliss/bliss_heap.cc b/src/cigraph/src/bliss/bliss_heap.cc index e5ce6d4..cbd3001 100644 --- a/src/cigraph/src/bliss/bliss_heap.cc +++ b/src/cigraph/src/bliss/bliss_heap.cc @@ -1,6 +1,6 @@ -#include -#include -#include +#include +//#include +#include #include "defs.hh" #include "heap.hh" diff --git a/src/cigraph/src/bliss/defs.cc b/src/cigraph/src/bliss/defs.cc index e154944..2ebc93d 100644 --- a/src/cigraph/src/bliss/defs.cc +++ b/src/cigraph/src/bliss/defs.cc @@ -1,5 +1,6 @@ #include #include +#include #include "defs.hh" /* @@ -23,20 +24,17 @@ namespace bliss { -#ifndef USING_R - void fatal_error(const char* fmt, ...) { + char buffer[1024]; va_list ap; va_start(ap, fmt); - fprintf(stderr,"Bliss fatal error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\nAborting!\n"); + sprintf(buffer, "Bliss fatal error: "); + vsprintf(buffer, fmt, ap); + throw std::runtime_error(buffer); va_end(ap); exit(1); } -#endif - } diff --git a/src/cigraph/src/bliss/defs.hh b/src/cigraph/src/bliss/defs.hh index 37f1404..493c7f7 100644 --- a/src/cigraph/src/bliss/defs.hh +++ b/src/cigraph/src/bliss/defs.hh @@ -29,11 +29,6 @@ # define BLISS_USE_GMP #endif -#ifdef USING_R -#include -#define fatal_error(...) (error(__VA_ARGS__)) -#endif - namespace bliss { /** @@ -47,9 +42,7 @@ static const char * const version = "0.73"; * There should not be a return from this function but exit or * a jump to code that deallocates the AbstractGraph instance that called this. */ -#ifndef USING_R void fatal_error(const char* fmt, ...); -#endif #if defined(BLISS_DEBUG) diff --git a/src/cigraph/src/bliss/graph.cc b/src/cigraph/src/bliss/graph.cc index 691645a..818e6af 100644 --- a/src/cigraph/src/bliss/graph.cc +++ b/src/cigraph/src/bliss/graph.cc @@ -1,4 +1,5 @@ -#include + +// #include #include #include #include @@ -15,11 +16,6 @@ #include #endif -#ifdef USING_R -#undef stdout -#define stdout NULL -#endif - /* Copyright (c) 2003-2015 Tommi Junttila Released under the GNU Lesser General Public License version 3. @@ -1036,15 +1032,6 @@ AbstractGraph::search(const bool canonical, Stats& stats) */ if(index == cell->length and all_same_level == current_level+1) all_same_level = current_level; - if(verbstr and verbose_level >= 2) { - fprintf(verbstr, - "Level %u: orbits=%u, index=%u/%u, all_same_level=%u\n", - current_level, - first_path_orbits.nof_orbits(), - index, cell->length, - all_same_level); - fflush(verbstr); - } } continue; } @@ -2082,6 +2069,7 @@ Digraph::permute(const unsigned int* const perm) const *-------------------------------------------------------------------------*/ +#if 0 void Digraph::write_dot(const char* const filename) { @@ -2118,6 +2106,7 @@ Digraph::write_dot(FILE* const fp) fprintf(fp, "}\n"); } +#endif void @@ -2187,6 +2176,7 @@ Digraph::get_hash() * *-------------------------------------------------------------------------*/ +#if 0 Digraph* Digraph::read_dimacs(FILE* const fp, FILE* const errstr) { @@ -2343,11 +2333,11 @@ Digraph::read_dimacs(FILE* const fp, FILE* const errstr) delete g; return 0; } +#endif - - +#if 0 void Digraph::write_dimacs(FILE* const fp) { @@ -2389,7 +2379,7 @@ Digraph::write_dimacs(FILE* const fp) } } } - +#endif @@ -3690,12 +3680,6 @@ Digraph::nucr_find_first_component(const unsigned int level) cr_component_elements += cell->length; } - if(verbstr and verbose_level > 2) { - fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", - (long unsigned)cr_component.size(), cr_component_elements); - fflush(verbstr); - } - return true; } @@ -3891,12 +3875,6 @@ Digraph::nucr_find_first_component(const unsigned int level, component_elements += cell->length; } - if(verbstr and verbose_level > 2) { - fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", - (long unsigned)component.size(), component_elements); - fflush(verbstr); - } - return true; } @@ -4036,6 +4014,7 @@ Graph::change_color(const unsigned int vertex, const unsigned int color) * *-------------------------------------------------------------------------*/ +#if 0 Graph* Graph::read_dimacs(FILE* const fp, FILE* const errstr) { @@ -4251,7 +4230,7 @@ Graph::write_dimacs(FILE* const fp) } } } - +#endif void @@ -4372,6 +4351,7 @@ Graph::permute(const unsigned int* perm) const *-------------------------------------------------------------------------*/ +#if 0 void Graph::write_dot(const char* const filename) { @@ -4409,8 +4389,7 @@ Graph::write_dot(FILE* const fp) fprintf(fp, "}\n"); } - - +#endif @@ -5432,12 +5411,6 @@ Graph::nucr_find_first_component(const unsigned int level) cr_component_elements += cell->length; } - if(verbstr and verbose_level > 2) { - fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", - (long unsigned)cr_component.size(), cr_component_elements); - fflush(verbstr); - } - return true; } @@ -5596,12 +5569,6 @@ Graph::nucr_find_first_component(const unsigned int level, component_elements += cell->length; } - if(verbstr and verbose_level > 2) { - fprintf(verbstr, "NU-component with %lu cells and %u vertices\n", - (long unsigned)component.size(), component_elements); - fflush(verbstr); - } - return true; } diff --git a/src/cigraph/src/bliss/graph.hh b/src/cigraph/src/bliss/graph.hh index 8288379..9355d59 100644 --- a/src/cigraph/src/bliss/graph.hh +++ b/src/cigraph/src/bliss/graph.hh @@ -29,7 +29,7 @@ namespace bliss { class AbstractGraph; } -#include +// #include #include #include "kstack.hh" #include "kqueue.hh" @@ -81,6 +81,7 @@ private: public: Stats() { reset(); } /** Print the statistics. */ + /* size_t print(FILE* const fp) const { size_t r = 0; @@ -94,6 +95,7 @@ public: fflush(fp); return r; } + */ /** An approximation (due to possible overflows/rounding errors) of * the size of the automorphism group. */ long double get_group_size_approx() const {return group_size_approx;} @@ -241,6 +243,7 @@ public: const unsigned int* aut), void* hook_user_param); +#if 0 /** * Write the graph to a file in a variant of the DIMACS format. * See the bliss website @@ -264,6 +267,7 @@ public: * \param file_name the name of the file to which the graph is written */ virtual void write_dot(const char * const file_name) = 0; +#endif /** * Get a hash value for the graph. @@ -658,6 +662,7 @@ public: */ ~Graph(); +#if 0 /** * Read the graph from the file \a fp in a variant of the DIMACS format. * See the bliss website @@ -690,6 +695,7 @@ public: * \copydoc AbstractGraph::write_dot(const char * const file_name) */ void write_dot(const char* const file_name); +#endif /** * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const @@ -898,6 +904,7 @@ public: */ ~Digraph(); +#if 0 /** * Read the graph from the file \a fp in a variant of the DIMACS format. * See the bliss website @@ -928,6 +935,7 @@ public: * \copydoc AbstractGraph::write_dot(const char * const file_name) */ void write_dot(const char * const file_name); +#endif /** * \copydoc AbstractGraph::is_automorphism(const std::vector& perm) const diff --git a/src/cigraph/src/bliss/orbit.cc b/src/cigraph/src/bliss/orbit.cc index 9d6d706..1e2fbcf 100644 --- a/src/cigraph/src/bliss/orbit.cc +++ b/src/cigraph/src/bliss/orbit.cc @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include "defs.hh" #include "orbit.hh" diff --git a/src/cigraph/src/bliss/partition.cc b/src/cigraph/src/bliss/partition.cc index 5dcbe16..1d35dd6 100644 --- a/src/cigraph/src/bliss/partition.cc +++ b/src/cigraph/src/bliss/partition.cc @@ -1,4 +1,4 @@ -#include +#include #include #include #include "graph.hh" @@ -344,7 +344,7 @@ Partition::aux_split_in_two(Partition::Cell* const cell, } - +#if 0 size_t Partition::print(FILE* const fp, const bool add_newline) const { @@ -388,7 +388,7 @@ Partition::print_signature(FILE* const fp, const bool add_newline) const if(add_newline) r += fprintf(fp, "\n"); return r; } - +#endif void diff --git a/src/cigraph/src/bliss/partition.hh b/src/cigraph/src/bliss/partition.hh index 6316f0c..3ac0f4d 100644 --- a/src/cigraph/src/bliss/partition.hh +++ b/src/cigraph/src/bliss/partition.hh @@ -25,7 +25,7 @@ namespace bliss { } #include -#include +//#include #include #include "kstack.hh" #include "kqueue.hh" @@ -186,12 +186,12 @@ public: /** * Print the partition into the file stream \a fp. */ - size_t print(FILE* const fp, const bool add_newline = true) const; + // size_t print(FILE* const fp, const bool add_newline = true) const; /** * Print the partition cell sizes into the file stream \a fp. */ - size_t print_signature(FILE* const fp, const bool add_newline = true) const; + // size_t print_signature(FILE* const fp, const bool add_newline = true) const; /* * Splits the Cell \a cell into [cell_1,...,cell_n] diff --git a/src/cigraph/src/bliss/uintseqhash.hh b/src/cigraph/src/bliss/uintseqhash.hh index 6b9bfca..4cabcff 100644 --- a/src/cigraph/src/bliss/uintseqhash.hh +++ b/src/cigraph/src/bliss/uintseqhash.hh @@ -1,7 +1,7 @@ #ifndef BLISS_UINTSEQHASH_HH #define BLISS_UINTSEQHASH_HH -#include +// #include /* Copyright (c) 2003-2015 Tommi Junttila diff --git a/src/cigraph/src/bliss/utils.cc b/src/cigraph/src/bliss/utils.cc index 548f500..778a14a 100644 --- a/src/cigraph/src/bliss/utils.cc +++ b/src/cigraph/src/bliss/utils.cc @@ -23,6 +23,7 @@ namespace bliss { +#if 0 void print_permutation(FILE* const fp, const unsigned int N, @@ -88,6 +89,7 @@ print_permutation(FILE* const fp, fprintf(fp, ")"); } } +#endif bool is_permutation(const unsigned int N, const unsigned int* perm) diff --git a/src/cigraph/src/bliss/utils.hh b/src/cigraph/src/bliss/utils.hh index 99f2fde..e9f297b 100644 --- a/src/cigraph/src/bliss/utils.hh +++ b/src/cigraph/src/bliss/utils.hh @@ -26,11 +26,12 @@ * */ -#include -using namespace std; +//#include +#include namespace bliss { +#if 0 /** * Print the permutation \a perm of {0,...,N-1} in the cycle format * in the file stream \a fp. @@ -51,6 +52,7 @@ void print_permutation(FILE* fp, void print_permutation(FILE* fp, const std::vector& perm, const unsigned int offset = 0); +#endif /** * Check whether \a perm is a valid permutation on {0,...,N-1}. diff --git a/src/cigraph/src/centrality.c b/src/cigraph/src/centrality.c index 28245a5..f7272c4 100644 --- a/src/cigraph/src/centrality.c +++ b/src/cigraph/src/centrality.c @@ -140,25 +140,6 @@ static int igraph_i_eigenvector_centrality2(igraph_real_t *to, const igraph_real return 0; } -static int igraph_i_eigenvector_centrality_loop(igraph_adjlist_t *adjlist) { - - long int i, j, k, nlen, n = igraph_adjlist_size(adjlist); - igraph_vector_int_t *neis; - - for (i = 0; i < n; i++) { - neis = igraph_adjlist_get(adjlist, i); - nlen = igraph_vector_int_size(neis); - for (j = 0; j < nlen && VECTOR(*neis)[j] < i; j++) ; - for (k = j; k < nlen && VECTOR(*neis)[k] == i; k++) ; - if (k != j) { - /* First loop edge is 'j', first non-loop edge is 'k' */ - igraph_vector_int_remove_section(neis, j + (k - j) / 2, k); - } - } - - return 0; -} - int igraph_eigenvector_centrality_undirected(const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, @@ -236,8 +217,6 @@ int igraph_eigenvector_centrality_undirected(const igraph_t *graph, igraph_vecto IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - IGRAPH_CHECK(igraph_i_eigenvector_centrality_loop(&adjlist)); - IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality, &adjlist, options, 0, &values, &vectors)); @@ -252,8 +231,6 @@ int igraph_eigenvector_centrality_undirected(const igraph_t *graph, igraph_vecto IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - IGRAPH_CHECK(igraph_inclist_remove_duplicate(graph, &inclist)); - IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_eigenvector_centrality2, &data, options, 0, &values, &vectors)); @@ -504,19 +481,32 @@ int igraph_eigenvector_centrality_directed(const igraph_t *graph, igraph_vector_ * * Eigenvector centrality is a measure of the importance of a node in a * network. It assigns relative scores to all nodes in the network based - * on the principle that connections to high-scoring nodes contribute - * more to the score of the node in question than equal connections to - * low-scoring nodes. In practice, this is determined by calculating the + * on the principle that connections from high-scoring nodes contribute + * more to the score of the node in question than equal connections from + * low-scoring nodes. Specifically, the eigenvector centrality of each + * vertex is proportional to the sum of eigenvector centralities of its + * neighbors. In practice, the centralities are determined by calculating the * eigenvector corresponding to the largest positive eigenvalue of the - * adjacency matrix. The centrality scores returned by igraph are always - * normalized such that the largest eigenvector centrality score is one - * (with one exception, see below). + * adjacency matrix. In the undirected case, this function considers + * the diagonal entries of the adjacency matrix to be \em twice the number of + * self-loops on the corresponding vertex. + * + * + * The centrality scores returned by igraph can be normalized + * (using the \p scale parameter) such that the largest eigenvector centrality + * score is 1 (with one exception, see below). + * + * + * In the directed case, the left eigenvector of the adjacency matrix is + * calculated. In other words, the centrality of a vertex is proportional + * to the sum of centralities of vertices pointing to it. * * - * Since the eigenvector centrality scores of nodes in different components - * do not affect each other, it may be beneficial for large graphs to - * decompose it first into weakly connected components and calculate the - * centrality scores individually for each component. + * Eigenvector centrality is meaningful only for connected graphs. + * Graphs that are not connected should be decomposed into connected + * components, and the eigenvector centrality calculated for each separately. + * This function does not verify that the graph is connected. If it is not, + * in the undirected case the scores of all but one component will be zeros. * * * Also note that the adjacency matrix of a directed acyclic graph or the @@ -529,7 +519,7 @@ int igraph_eigenvector_centrality_directed(const igraph_t *graph, igraph_vector_ * parameter, see below) and checking whether the eigenvalue is very close * to zero. * - * \param graph The input graph. It might be directed. + * \param graph The input graph. It may be directed. * \param vector Pointer to an initialized vector, it will be resized * as needed. The result of the computation is stored here. It can * be a null pointer, then it is ignored. diff --git a/src/cigraph/src/clustertool.cpp b/src/cigraph/src/clustertool.cpp index 708972d..b1b19e2 100644 --- a/src/cigraph/src/clustertool.cpp +++ b/src/cigraph/src/clustertool.cpp @@ -56,6 +56,7 @@ #include "igraph_interface.h" #include "igraph_components.h" #include "igraph_interrupt_internal.h" +#include "igraph_handle_exceptions.h" static int igraph_i_community_spinglass_orig( const igraph_t *graph, @@ -202,29 +203,30 @@ int igraph_community_spinglass(const igraph_t *graph, /* igraph_real_t *polarization, */ igraph_real_t gamma_minus) { - switch (implementation) { - case IGRAPH_SPINCOMM_IMP_ORIG: - return igraph_i_community_spinglass_orig(graph, weights, modularity, - temperature, membership, csize, - spins, parupdate, starttemp, - stoptemp, coolfact, update_rule, - gamma); - break; - case IGRAPH_SPINCOMM_IMP_NEG: - return igraph_i_community_spinglass_negative(graph, weights, modularity, - temperature, membership, csize, - spins, parupdate, starttemp, - stoptemp, coolfact, - update_rule, gamma, - /* adhesion, normalised_adhesion, */ - /* polarization, */ - gamma_minus); - break; - default: - IGRAPH_ERROR("Unknown `implementation' in spinglass community finding", - IGRAPH_EINVAL); - } - + IGRAPH_HANDLE_EXCEPTIONS( + switch (implementation) { + case IGRAPH_SPINCOMM_IMP_ORIG: + return igraph_i_community_spinglass_orig(graph, weights, modularity, + temperature, membership, csize, + spins, parupdate, starttemp, + stoptemp, coolfact, update_rule, + gamma); + break; + case IGRAPH_SPINCOMM_IMP_NEG: + return igraph_i_community_spinglass_negative(graph, weights, modularity, + temperature, membership, csize, + spins, parupdate, starttemp, + stoptemp, coolfact, + update_rule, gamma, + /* adhesion, normalised_adhesion, */ + /* polarization, */ + gamma_minus); + break; + default: + IGRAPH_ERROR("Unknown `implementation' in spinglass community finding", + IGRAPH_EINVAL); + } + ); return 0; } @@ -446,89 +448,90 @@ int igraph_community_spinglass_single(const igraph_t *graph, igraph_integer_t spins, igraph_spincomm_update_t update_rule, igraph_real_t gamma) { - - igraph_bool_t use_weights = 0; - double prob; - ClusterList *cl_cur; - network *net; - PottsModel *pm; - char startnode[255]; - - /* Check arguments */ - - if (spins < 2 || spins > 500) { - IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL); - } - if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && - update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { - IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); - } - if (weights) { - if (igraph_vector_size(weights) != igraph_ecount(graph)) { - IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t use_weights = 0; + double prob; + ClusterList *cl_cur; + network *net; + PottsModel *pm; + char startnode[255]; + + /* Check arguments */ + + if (spins < 2 || spins > 500) { + IGRAPH_ERROR("Invalid number of spins", IGRAPH_EINVAL); + } + if (update_rule != IGRAPH_SPINCOMM_UPDATE_SIMPLE && + update_rule != IGRAPH_SPINCOMM_UPDATE_CONFIG) { + IGRAPH_ERROR("Invalid update rule", IGRAPH_EINVAL); + } + if (weights) { + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + } + use_weights = 1; + } + if (gamma < 0.0) { + IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); + } + if (vertex < 0 || vertex > igraph_vcount(graph)) { + IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); } - use_weights = 1; - } - if (gamma < 0.0) { - IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); - } - if (vertex < 0 || vertex > igraph_vcount(graph)) { - IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); - } - /* Check whether we have a single component */ - igraph_bool_t conn; - IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); - if (!conn) { - IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); - } + /* Check whether we have a single component */ + igraph_bool_t conn; + IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); + if (!conn) { + IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); + } - net = new network; - net->node_list = new DL_Indexed_List(); - net->link_list = new DL_Indexed_List(); - net->cluster_list = new DL_Indexed_List*>(); + net = new network; + net->node_list = new DL_Indexed_List(); + net->link_list = new DL_Indexed_List(); + net->cluster_list = new DL_Indexed_List*>(); - /* Transform the igraph_t */ - IGRAPH_CHECK(igraph_i_read_network(graph, weights, - net, use_weights, 0)); + /* Transform the igraph_t */ + IGRAPH_CHECK(igraph_i_read_network(graph, weights, + net, use_weights, 0)); - prob = 2.0 * net->sum_weights / double(net->node_list->Size()) - / double(net->node_list->Size() - 1); + prob = 2.0 * net->sum_weights / double(net->node_list->Size()) + / double(net->node_list->Size() - 1); - pm = new PottsModel(net, (unsigned int)spins, update_rule); + pm = new PottsModel(net, (unsigned int)spins, update_rule); - /* initialize the random number generator */ - RNG_BEGIN(); + /* initialize the random number generator */ + RNG_BEGIN(); - /* to be exected, if we want to find the community around a particular node*/ - /* the initial conf is needed, because otherwise, - the degree of the nodes is not in the weight property, stupid!!! */ - pm->assign_initial_conf(-1); - snprintf(startnode, 255, "%li", (long int)vertex + 1); - pm->FindCommunityFromStart(gamma, prob, startnode, community, - cohesion, adhesion, inner_links, outer_links); + /* to be exected, if we want to find the community around a particular node*/ + /* the initial conf is needed, because otherwise, + the degree of the nodes is not in the weight property, stupid!!! */ + pm->assign_initial_conf(-1); + snprintf(startnode, 255, "%li", (long int)vertex + 1); + pm->FindCommunityFromStart(gamma, prob, startnode, community, + cohesion, adhesion, inner_links, outer_links); - while (net->link_list->Size()) { - delete net->link_list->Pop(); - } - while (net->node_list->Size()) { - delete net->node_list->Pop(); - } - while (net->cluster_list->Size()) { - cl_cur = net->cluster_list->Pop(); - while (cl_cur->Size()) { - cl_cur->Pop(); + while (net->link_list->Size()) { + delete net->link_list->Pop(); } - delete cl_cur; - } - delete net->link_list; - delete net->node_list; - delete net->cluster_list; + while (net->node_list->Size()) { + delete net->node_list->Pop(); + } + while (net->cluster_list->Size()) { + cl_cur = net->cluster_list->Pop(); + while (cl_cur->Size()) { + cl_cur->Pop(); + } + delete cl_cur; + } + delete net->link_list; + delete net->node_list; + delete net->cluster_list; - RNG_END(); + RNG_END(); - delete net; - delete pm; + delete net; + delete pm; + ); return 0; } diff --git a/src/cigraph/src/coloring.c b/src/cigraph/src/coloring.c index ccd6612..80fefe3 100644 --- a/src/cigraph/src/coloring.c +++ b/src/cigraph/src/coloring.c @@ -134,7 +134,7 @@ static int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vect * \brief Computes a vertex coloring using a greedy algorithm. * * - * This function assigns a "color"---represented as a non-negative integer---to + * This function assigns a "color"—represented as a non-negative integer—to * each vertex of the graph in such a way that neighboring vertices never have * the same color. The obtained coloring is not necessarily minimal. * diff --git a/src/cigraph/src/community_leiden.c b/src/cigraph/src/community_leiden.c index 45180d2..ee20034 100644 --- a/src/cigraph/src/community_leiden.c +++ b/src/cigraph/src/community_leiden.c @@ -137,12 +137,14 @@ static int igraph_i_community_leiden_fastmovenodes( for (i = 0; i < degree; i++) { long int e = VECTOR(*edges)[i]; long int u = (long int)IGRAPH_OTHER(graph, e, v); - c = VECTOR(*membership)[u]; - if (!VECTOR(neighbor_cluster_added)[c]) { - VECTOR(neighbor_cluster_added)[c] = 1; - VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; + if (u != v) { + c = VECTOR(*membership)[u]; + if (!VECTOR(neighbor_cluster_added)[c]) { + VECTOR(neighbor_cluster_added)[c] = 1; + VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; + } + VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; } - VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; } /* Calculate maximum diff */ @@ -329,7 +331,7 @@ static int igraph_i_community_leiden_mergenodes( for (j = 0; j < degree; j++) { long int e = VECTOR(*edges)[j]; long int u = (long int)IGRAPH_OTHER(graph, e, v); - if (VECTOR(*membership)[u] == cluster_subset) { + if (u != v && VECTOR(*membership)[u] == cluster_subset) { VECTOR(external_edge_weight_per_cluster_in_subset)[i] += VECTOR(*edge_weights)[e]; } } @@ -383,7 +385,7 @@ static int igraph_i_community_leiden_mergenodes( for (j = 0; j < degree; j++) { long int e = (long int)VECTOR(*edges)[j]; long int u = (long int)IGRAPH_OTHER(graph, e, v); - if (VECTOR(*membership)[u] == cluster_subset) { + if (u != v && VECTOR(*membership)[u] == cluster_subset) { long int c = VECTOR(*refined_membership)[u]; if (!VECTOR(neighbor_cluster_added)[c]) { VECTOR(neighbor_cluster_added)[c] = 1; @@ -610,16 +612,20 @@ static int igraph_i_community_leiden_aggregate( } - IGRAPH_CHECK(igraph_create(aggregated_graph, &aggregated_edges, nb_refined_clusters, - IGRAPH_UNDIRECTED)); - igraph_vector_destroy(&neighbor_clusters); igraph_vector_bool_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weight_to_cluster); - igraph_vector_destroy(&aggregated_edges); igraph_vector_ptr_destroy_all(&refined_clusters); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FINALLY_CLEAN(4); + + igraph_destroy(aggregated_graph); + IGRAPH_CHECK(igraph_create(aggregated_graph, &aggregated_edges, nb_refined_clusters, + IGRAPH_UNDIRECTED)); + + igraph_vector_destroy(&aggregated_edges); + + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -709,8 +715,9 @@ int igraph_i_community_leiden(const igraph_t *graph, igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { igraph_integer_t nb_refined_clusters; long int i, c, n = igraph_vcount(graph); - igraph_t *aggregated_graph, *tmp_graph; - igraph_vector_t *aggregated_edge_weights, *aggregated_node_weights, *aggregated_membership; + igraph_t aggregated_graph, *i_graph; + igraph_vector_t aggregated_edge_weights, aggregated_node_weights, aggregated_membership; + igraph_vector_t *i_edge_weights, *i_node_weights, *i_membership; igraph_vector_t tmp_edge_weights, tmp_node_weights, tmp_membership; igraph_vector_t refined_membership; igraph_vector_int_t aggregate_node; @@ -731,6 +738,7 @@ int igraph_i_community_leiden(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_ptr_init(&clusters, n)); IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&clusters, igraph_vector_destroy); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &clusters); + /* Initialize aggregate nodes, which initially is identical to simply the * nodes in the graph. */ IGRAPH_CHECK(igraph_vector_int_init(&aggregate_node, n)); @@ -739,17 +747,35 @@ int igraph_i_community_leiden(const igraph_t *graph, VECTOR(aggregate_node)[i] = i; } + /* Initialize refined membership */ IGRAPH_CHECK(igraph_vector_init(&refined_membership, 0)); IGRAPH_FINALLY(igraph_vector_destroy, &refined_membership); - /* Initialize aggregated graph, weights and membership. */ - aggregated_graph = (igraph_t*)graph; - aggregated_edge_weights = edge_weights; - aggregated_node_weights = node_weights; - aggregated_membership = membership; + /* Initialize aggregated graph */ + IGRAPH_CHECK(igraph_empty(&aggregated_graph, 0, IGRAPH_UNDIRECTED)); + IGRAPH_FINALLY(igraph_destroy, &aggregated_graph); + + /* Initialize aggregated edge weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_edge_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edge_weights); + + /* Initialize aggregated node weights */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_node_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_node_weights); + + /* Initialize aggregated membership */ + IGRAPH_CHECK(igraph_vector_init(&aggregated_membership, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_membership); + + /* Set actual graph, weights and membership to be used. */ + i_graph = (igraph_t*)graph; + i_edge_weights = edge_weights; + i_node_weights = node_weights; + i_membership = membership; /* Clean membership and count number of *clusters */ - IGRAPH_CHECK(igraph_reindex_membership(aggregated_membership, NULL, nb_clusters)); + + IGRAPH_CHECK(igraph_reindex_membership(i_membership, NULL, nb_clusters)); if (*nb_clusters > n) { IGRAPH_ERROR("Too many communities in membership vector", IGRAPH_EINVAL); @@ -758,45 +784,45 @@ int igraph_i_community_leiden(const igraph_t *graph, do { /* Get incidence list for fast iteration */ - IGRAPH_CHECK(igraph_inclist_init(aggregated_graph, &edges_per_node, IGRAPH_ALL)); + IGRAPH_CHECK(igraph_inclist_init( i_graph, &edges_per_node, IGRAPH_ALL)); IGRAPH_FINALLY(igraph_inclist_destroy, &edges_per_node); /* Move around the nodes in order to increase the quality */ - IGRAPH_CHECK(igraph_i_community_leiden_fastmovenodes(aggregated_graph, + IGRAPH_CHECK(igraph_i_community_leiden_fastmovenodes(i_graph, &edges_per_node, - aggregated_edge_weights, aggregated_node_weights, + i_edge_weights, i_node_weights, resolution_parameter, nb_clusters, - aggregated_membership)); + i_membership)); /* We only continue clustering if not all clusters are represented by a * single node yet */ - continue_clustering = (*nb_clusters < igraph_vcount(aggregated_graph)); + continue_clustering = (*nb_clusters < igraph_vcount(i_graph)); if (continue_clustering) { /* Set original membership */ if (level > 0) { for (i = 0; i < n; i++) { long int v_aggregate = VECTOR(aggregate_node)[i]; - VECTOR(*membership)[i] = VECTOR(*aggregated_membership)[v_aggregate]; + VECTOR(*membership)[i] = VECTOR(*i_membership)[v_aggregate]; } } /* Get node sets for each cluster. */ - IGRAPH_CHECK(igraph_i_community_get_clusters(aggregated_membership, &clusters)); + IGRAPH_CHECK(igraph_i_community_get_clusters(i_membership, &clusters)); /* Ensure refined membership is correct size */ - IGRAPH_CHECK(igraph_vector_resize(&refined_membership, igraph_vcount(aggregated_graph))); + IGRAPH_CHECK(igraph_vector_resize(&refined_membership, igraph_vcount(i_graph))); /* Refine each cluster */ nb_refined_clusters = 0; for (c = 0; c < *nb_clusters; c++) { igraph_vector_t* cluster = (igraph_vector_t*)VECTOR(clusters)[c]; - IGRAPH_CHECK(igraph_i_community_leiden_mergenodes(aggregated_graph, + IGRAPH_CHECK(igraph_i_community_leiden_mergenodes(i_graph, &edges_per_node, - aggregated_edge_weights, aggregated_node_weights, - cluster, aggregated_membership, c, + i_edge_weights, i_node_weights, + cluster, i_membership, c, resolution_parameter, beta, &nb_refined_clusters, &refined_membership)); /* Empty cluster */ @@ -805,8 +831,8 @@ int igraph_i_community_leiden(const igraph_t *graph, /* If refinement didn't aggregate anything, we aggregate on the basis of * the actual clustering */ - if (nb_refined_clusters >= igraph_vcount(aggregated_graph)) { - igraph_vector_update(&refined_membership, aggregated_membership); + if (nb_refined_clusters >= igraph_vcount(i_graph)) { + igraph_vector_update(&refined_membership, i_membership); nb_refined_clusters = *nb_clusters; } @@ -818,70 +844,27 @@ int igraph_i_community_leiden(const igraph_t *graph, VECTOR(aggregate_node)[i] = (igraph_integer_t)VECTOR(refined_membership)[v_aggregate]; } - /* Allocate temporary graph */ - tmp_graph = igraph_Calloc(1, igraph_t); - if (tmp_graph == 0) { - IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate graph", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, tmp_graph); - IGRAPH_CHECK(igraph_i_community_leiden_aggregate( - aggregated_graph, &edges_per_node, aggregated_edge_weights, aggregated_node_weights, - aggregated_membership, &refined_membership, nb_refined_clusters, - tmp_graph, &tmp_edge_weights, &tmp_node_weights, &tmp_membership)); - - /* Graph has been created by aggregation, ensure it is properly destroyed if - * an error occurs. */ - IGRAPH_FINALLY(igraph_destroy, tmp_graph); - - if (level >= 1) { - /* Destroy previously allocated graph (note that aggregated_graph points to - * the previously allocated tmp_graph). */ - igraph_destroy(aggregated_graph); - igraph_Free(aggregated_graph); - IGRAPH_FINALLY_CLEAN(2); - } + i_graph, &edges_per_node, i_edge_weights, i_node_weights, + i_membership, &refined_membership, nb_refined_clusters, + &aggregated_graph, &tmp_edge_weights, &tmp_node_weights, &tmp_membership)); /* On the lowest level, the actual graph and node and edge weights and - * membership are used. On higher levels, we will have to use a new graph - * and node and edge weights to represent them. We perform the allocation - * of memory here. We only allocate the memory once, and simply update - * them in any subsequent rounds. + * membership are used. On higher levels, we will use the aggregated graph + * and associated vectors. */ if (level == 0) { - aggregated_edge_weights = igraph_Calloc(1, igraph_vector_t); - if (aggregated_edge_weights == 0) { - IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate edge weights", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, aggregated_edge_weights); - IGRAPH_CHECK(igraph_vector_init(aggregated_edge_weights, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, aggregated_edge_weights); - - aggregated_node_weights = igraph_Calloc(1, igraph_vector_t); - if (aggregated_node_weights == 0) { - IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate node weights", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, aggregated_node_weights); - IGRAPH_CHECK(igraph_vector_init(aggregated_node_weights, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, aggregated_node_weights); - - aggregated_membership = igraph_Calloc(1, igraph_vector_t); - if (aggregated_membership == 0) { - IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for aggregate membership", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, aggregated_membership); - IGRAPH_CHECK(igraph_vector_init(aggregated_membership, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, aggregated_membership); + /* Set actual graph, weights and membership to be used. */ + i_graph = &aggregated_graph; + i_edge_weights = &aggregated_edge_weights; + i_node_weights = &aggregated_node_weights; + i_membership = &aggregated_membership; } - /* Set the aggregated graph correctly */ - aggregated_graph = tmp_graph; - - /* Update the aggregated administration. This does not allocate memory, - * it will always fit in existing memory allocated previously. */ - igraph_vector_update(aggregated_edge_weights, &tmp_edge_weights); - igraph_vector_update(aggregated_node_weights, &tmp_node_weights); - igraph_vector_update(aggregated_membership, &tmp_membership); + /* Update the aggregated administration. */ + IGRAPH_CHECK(igraph_vector_update(i_edge_weights, &tmp_edge_weights)); + IGRAPH_CHECK(igraph_vector_update(i_node_weights, &tmp_node_weights)); + IGRAPH_CHECK(igraph_vector_update(i_membership, &tmp_membership)); level += 1; } @@ -891,21 +874,12 @@ int igraph_i_community_leiden(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } while (continue_clustering); - /* If memory was allocated to represent the aggregated administration we need - * to make sure it is properly freed. This is only done if we have at least - * passed on to the next level of aggregation. - */ - if (level > 0) { - igraph_destroy(aggregated_graph); - igraph_Free(aggregated_graph); - igraph_vector_destroy(aggregated_membership); - igraph_Free(aggregated_membership); - igraph_vector_destroy(aggregated_node_weights); - igraph_Free(aggregated_node_weights); - igraph_vector_destroy(aggregated_edge_weights); - igraph_Free(aggregated_edge_weights); - IGRAPH_FINALLY_CLEAN(8); - } + /* Free aggregated graph and associated vectors */ + igraph_vector_destroy(&aggregated_membership); + igraph_vector_destroy(&aggregated_node_weights); + igraph_vector_destroy(&aggregated_edge_weights); + igraph_destroy(&aggregated_graph); + IGRAPH_FINALLY_CLEAN(4); /* Free remaining memory */ igraph_vector_destroy(&refined_membership); @@ -1043,8 +1017,8 @@ int igraph_community_leiden(const igraph_t *graph, if (i_edge_weights == 0) { IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for edge weights", IGRAPH_ENOMEM); } - IGRAPH_CHECK(igraph_vector_init(i_edge_weights, igraph_ecount(graph))); IGRAPH_FINALLY(igraph_free, i_edge_weights); + IGRAPH_CHECK(igraph_vector_init(i_edge_weights, igraph_ecount(graph))); IGRAPH_FINALLY(igraph_vector_destroy, i_edge_weights); igraph_vector_fill(i_edge_weights, 1); } else { @@ -1057,8 +1031,8 @@ int igraph_community_leiden(const igraph_t *graph, if (i_node_weights == 0) { IGRAPH_ERROR("Leiden algorithm failed, could not allocate memory for node weights", IGRAPH_ENOMEM); } - IGRAPH_CHECK(igraph_vector_init(i_node_weights, n)); IGRAPH_FINALLY(igraph_free, i_node_weights); + IGRAPH_CHECK(igraph_vector_init(i_node_weights, n)); IGRAPH_FINALLY(igraph_vector_destroy, i_node_weights); igraph_vector_fill(i_node_weights, 1); } else { diff --git a/src/cigraph/src/degree_sequence.cpp b/src/cigraph/src/degree_sequence.cpp index 31c078e..0d038dc 100644 --- a/src/cigraph/src/degree_sequence.cpp +++ b/src/cigraph/src/degree_sequence.cpp @@ -1,6 +1,6 @@ /* Constructing realizations of degree sequences and bi-degree sequences. - Copyright (C) 2018 Szabolcs Horvat + Copyright (C) 2018-2020 Szabolcs Horvat 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 @@ -82,10 +82,6 @@ static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *ed vd_pair vd = vertices.back(); vertices.pop_back(); - if (vd.degree < 0) { - IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL); - } - if (vd.degree == 0) { continue; } @@ -148,10 +144,6 @@ static int igraph_i_havel_hakimi_index(const igraph_vector_t *deg, igraph_vector vd_pair vd = **pt; vertices.erase(*pt); - if (vd.degree < 0) { - IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL); - } - if (vd.degree == 0) { continue; } @@ -226,12 +218,8 @@ static int igraph_i_kleitman_wang(const igraph_vector_t *outdeg, const igraph_ve } - if (vdp->degree.first < 0 || vdp->degree.second < 0) { - IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL); - } - // are there a sufficient number of other vertices to connect to? - if (vertices.size() < vdp->degree.second - 1) { + if (vertices.size() - 1 < vdp->degree.second) { goto fail; } @@ -294,10 +282,6 @@ static int igraph_i_kleitman_wang_index(const igraph_vector_t *outdeg, const igr continue; } - if (vd.degree.first < 0 || vd.degree.second < 0) { - IGRAPH_ERROR("Vertex degrees must be positive", IGRAPH_EINVAL); - } - int k = 0; vlist::iterator it; for (it = vertices.begin(); @@ -342,6 +326,10 @@ static int igraph_i_realize_undirected_degree_sequence( IGRAPH_ERROR("The sum of degrees must be even for an undirected graph", IGRAPH_EINVAL); } + if (igraph_vector_min(deg) < 0) { + IGRAPH_ERROR("Vertex degrees must be non-negative", IGRAPH_EINVAL); + } + igraph_vector_t edges; IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum)); IGRAPH_FINALLY(igraph_vector_destroy, &edges); @@ -384,6 +372,10 @@ static int igraph_i_realize_directed_degree_sequence( IGRAPH_ERROR("In- and out-degree sequences do not sum to the same value", IGRAPH_EINVAL); } + if (igraph_vector_min(outdeg) < 0 || igraph_vector_min(indeg) < 0) { + IGRAPH_ERROR("Vertex degrees must be non-negative", IGRAPH_EINVAL); + } + igraph_vector_t edges; IGRAPH_CHECK(igraph_vector_init(&edges, 2 * edge_count)); IGRAPH_FINALLY(igraph_vector_destroy, &edges); @@ -426,6 +418,32 @@ static int igraph_i_realize_directed_degree_sequence( * * The \c method parameter controls the order in which the vertices to be connected are chosen. * + * + * References: + * + * + * V. Havel, + * Poznámka o existenci konečných grafů (A remark on the existence of finite graphs), + * Časopis pro pěstování matematiky 80, 477-480 (1955). + * http://eudml.org/doc/19050 + * + * + * S. L. Hakimi, + * On Realizability of a Set of Integers as Degrees of the Vertices of a Linear Graph, + * Journal of the SIAM 10, 3 (1962). + * https://www.jstor.org/stable/2098746 + * + * + * D. J. Kleitman and D. L. Wang, + * Algorithms for Constructing Graphs and Digraphs with Given Valences and Factors, + * Discrete Mathematics 6, 1 (1973). + * https://doi.org/10.1016/0012-365X%2873%2990037-X + * + * + * Sz. Horvát and C. D. Modes, + * Connectivity matters: Construction and exact random sampling of connected graphs (2020). + * https://arxiv.org/abs/2009.03747 + * * \param graph Pointer to an uninitialized graph object. * \param outdeg The degree sequence for a simple undirected graph * (if \p indeg is NULL or of length zero), or the out-degree sequence of @@ -438,8 +456,8 @@ static int igraph_i_realize_directed_degree_sequence( * The vertex with smallest remaining degree is selected first. The result is usually * a graph with high negative degree assortativity. In the undirected case, this method * is guaranteed to generate a connected graph, provided that a connected realization exists. - * See http://szhorvat.net/pelican/hh-connected-graphs.html for a proof. - * In the directed case it tends to generate weakly connected graphs, but this is not + * See Horvát and Modes (2020) as well as http://szhorvat.net/pelican/hh-connected-graphs.html + * for a proof. In the directed case it tends to generate weakly connected graphs, but this is not * guaranteed. * \cli IGRAPH_REALIZE_DEGSEQ_LARGEST * The vertex with the largest remaining degree is selected first. The result diff --git a/src/cigraph/src/drl_layout.cpp b/src/cigraph/src/drl_layout.cpp index 2d0c822..3b8e62a 100644 --- a/src/cigraph/src/drl_layout.cpp +++ b/src/cigraph/src/drl_layout.cpp @@ -65,6 +65,8 @@ using namespace drl; #include "igraph_random.h" #include "igraph_interface.h" +#include "igraph_handle_exceptions.h" + namespace drl { // int main(int argc, char **argv) { @@ -455,17 +457,19 @@ int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed) { - RNG_BEGIN(); + IGRAPH_HANDLE_EXCEPTIONS( + RNG_BEGIN(); - drl::graph neighbors(graph, options, weights); - neighbors.init_parms(options); - if (use_seed) { - IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); - neighbors.read_real(res, fixed); - } - neighbors.draw_graph(res); + drl::graph neighbors(graph, options, weights); + neighbors.init_parms(options); + if (use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); + neighbors.read_real(res, fixed); + } + neighbors.draw_graph(res); - RNG_END(); + RNG_END(); + ); return 0; } diff --git a/src/cigraph/src/drl_layout_3d.cpp b/src/cigraph/src/drl_layout_3d.cpp index 796704b..486c825 100644 --- a/src/cigraph/src/drl_layout_3d.cpp +++ b/src/cigraph/src/drl_layout_3d.cpp @@ -65,6 +65,8 @@ using namespace drl3d; #include "igraph_random.h" #include "igraph_interface.h" +#include "igraph_handle_exceptions.h" + /** * \function igraph_layout_drl_3d * The DrL layout generator, 3d version. @@ -101,18 +103,19 @@ int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_layout_drl_options_t *options, const igraph_vector_t *weights, const igraph_vector_bool_t *fixed) { + IGRAPH_HANDLE_EXCEPTIONS( + RNG_BEGIN(); - RNG_BEGIN(); - - drl3d::graph neighbors(graph, options, weights); - neighbors.init_parms(options); - if (use_seed) { - IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); - neighbors.read_real(res, fixed); - } - neighbors.draw_graph(res); + drl3d::graph neighbors(graph, options, weights); + neighbors.init_parms(options); + if (use_seed) { + IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); + neighbors.read_real(res, fixed); + } + neighbors.draw_graph(res); - RNG_END(); + RNG_END(); + ); return 0; } diff --git a/src/cigraph/src/foreign.c b/src/cigraph/src/foreign.c index 6d4696a..71dda05 100644 --- a/src/cigraph/src/foreign.c +++ b/src/cigraph/src/foreign.c @@ -53,10 +53,10 @@ * \brief Reads an edge list from a file and creates a graph. * * - * This format is simply a series of even number integers separated by - * whitespace. The one edge (ie. two integers) per line format is thus - * not required (but recommended for readability). Edges of directed - * graphs are assumed to be in from, to order. + * This format is simply a series of an even number of non-negative integers separated by + * whitespace. The integers represent vertex IDs. Placing each edge (i.e. pair of integers) + * on a separate line is not required, but it is recommended for readability. + * Edges of directed graphs are assumed to be in "from, to" order. * \param graph Pointer to an uninitialized graph object. * \param instream Pointer to a stream, it should be readable. * \param n The number of vertices in the graph. If smaller than the diff --git a/src/cigraph/src/games.c b/src/cigraph/src/games.c index 3023510..287bcf8 100644 --- a/src/cigraph/src/games.c +++ b/src/cigraph/src/games.c @@ -4343,8 +4343,11 @@ int igraph_static_power_law_game(igraph_t *graph, * graphs, at least one of k and the number of vertices must be even. * * - * The game simply uses \ref igraph_degree_sequence_game with appropriately - * constructed degree sequences. + * Currently, this game simply uses \ref igraph_degree_sequence_game with + * the \c SIMPLE_NO_MULTIPLE method and appropriately constructed degree sequences. + * Thefore, it does not sample uniformly: while it can generate all k-regular graphs + * with the given number of vertices, it does not generate each one with the same + * probability. * * \param graph Pointer to an uninitialized graph object. * \param no_of_nodes The number of nodes in the generated graph. @@ -4769,7 +4772,7 @@ static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_in * \brief Generates a random tree with the given number of nodes * * This function samples uniformly from the set of labelled trees, - * i.e. it can generate each labelled tree with the same probability. + * i.e. it generates each labelled tree with the same probability. * * \param graph Pointer to an uninitialized graph object. * \param n The number of nodes in the tree. @@ -4777,7 +4780,7 @@ static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_in * \param method The algorithm to use to generate the tree. Possible values: * \clist * \cli IGRAPH_RANDOM_TREE_PRUFER - * This algorithm samples Prüfer sequences unformly, then converts them to trees. + * This algorithm samples Prüfer sequences uniformly, then converts them to trees. * Directed trees are not currently supported. * \cli IGRAPH_RANDOM_LERW * This algorithm effectively performs a loop-erased random walk on the complete graph diff --git a/src/cigraph/src/gengraph_mr-connected.cpp b/src/cigraph/src/gengraph_mr-connected.cpp index c8f32a9..7c65442 100644 --- a/src/cigraph/src/gengraph_mr-connected.cpp +++ b/src/cigraph/src/gengraph_mr-connected.cpp @@ -28,6 +28,8 @@ #include "igraph_types.h" #include "igraph_error.h" +#include "igraph_handle_exceptions.h" + namespace gengraph { // return negative number if program should exit @@ -137,48 +139,50 @@ extern "C" { int igraph_degree_sequence_game_vl(igraph_t *graph, const igraph_vector_t *out_seq, const igraph_vector_t *in_seq) { - long int sum = igraph_vector_sum(out_seq); - if (sum % 2 != 0) { - IGRAPH_ERROR("Sum of degrees should be even", IGRAPH_EINVAL); - } - - RNG_BEGIN(); - - if (in_seq && igraph_vector_size(in_seq) != 0) { - RNG_END(); - IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); - } - - degree_sequence *dd = new degree_sequence(out_seq); - - graph_molloy_opt *g = new graph_molloy_opt(*dd); - delete dd; - - if (!g->havelhakimi()) { + IGRAPH_HANDLE_EXCEPTIONS( + long int sum = igraph_vector_sum(out_seq); + if (sum % 2 != 0) { + IGRAPH_ERROR("Sum of degrees should be even", IGRAPH_EINVAL); + } + + RNG_BEGIN(); + + if (in_seq && igraph_vector_size(in_seq) != 0) { + RNG_END(); + IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); + } + + degree_sequence *dd = new degree_sequence(out_seq); + + graph_molloy_opt *g = new graph_molloy_opt(*dd); + delete dd; + + if (!g->havelhakimi()) { + delete g; + RNG_END(); + IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph", + IGRAPH_EINVAL); + } + + if (!g->make_connected()) { + delete g; + RNG_END(); + IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence", + IGRAPH_EINVAL); + } + + int *hc = g->hard_copy(); delete g; - RNG_END(); - IGRAPH_ERROR("Cannot realize the given degree sequence as an undirected, simple graph", - IGRAPH_EINVAL); - } - - if (!g->make_connected()) { - delete g; - RNG_END(); - IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence", - IGRAPH_EINVAL); - } - - int *hc = g->hard_copy(); - delete g; - graph_molloy_hash *gh = new graph_molloy_hash(hc); - delete [] hc; + graph_molloy_hash *gh = new graph_molloy_hash(hc); + delete [] hc; - gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); + gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); - IGRAPH_CHECK(gh->print(graph)); - delete gh; + IGRAPH_CHECK(gh->print(graph)); + delete gh; - RNG_END(); + RNG_END(); + ); return 0; } diff --git a/src/cigraph/src/igraph_blas_internal.h b/src/cigraph/src/igraph_blas_internal.h index f4ea577..bc74b95 100644 --- a/src/cigraph/src/igraph_blas_internal.h +++ b/src/cigraph/src/igraph_blas_internal.h @@ -34,13 +34,13 @@ #ifndef INTERNAL_BLAS #define igraphdaxpy_ daxpy_ - #define igraphdger_ dger_ + #define igraphdger_ dger_ #define igraphdcopy_ dcopy_ #define igraphdscal_ dscal_ #define igraphdswap_ dswap_ #define igraphdgemm_ dgemm_ #define igraphdgemv_ dgemv_ - #define igraphddot_ ddot_ + #define igraphddot_ ddot_ #define igraphdnrm2_ dnrm2_ #define igraphlsame_ lsame_ #define igraphdrot_ drot_ diff --git a/src/cigraph/src/igraph_handle_exceptions.h b/src/cigraph/src/igraph_handle_exceptions.h new file mode 100644 index 0000000..8aab4aa --- /dev/null +++ b/src/cigraph/src/igraph_handle_exceptions.h @@ -0,0 +1,14 @@ +#ifndef IGRAPH_HANDLE_EXCEPTIONS_H +#define IGRAPH_HANDLE_EXCEPTIONS_H + +#include +#include + +#define IGRAPH_HANDLE_EXCEPTIONS(code) \ + try { code; } \ + catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); } \ + catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); } \ + catch (...) { IGRAPH_ERROR("Unknown exception caught", IGRAPH_FAILURE); } + + +#endif // IGRAPH_HANDLE_EXCEPTIONS_H diff --git a/src/cigraph/src/igraph_isoclasses.h b/src/cigraph/src/igraph_isoclasses.h new file mode 100644 index 0000000..78db8f6 --- /dev/null +++ b/src/cigraph/src/igraph_isoclasses.h @@ -0,0 +1,54 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2008-2020 The igraph development team + 334 Harvard street, Cambridge, MA 02139 USA + + 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 2 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + +*/ + +#ifndef IGRAPH_ISOCLASSES_H +#define IGRAPH_ISOCLASSES_H + +#undef __BEGIN_DECLS +#undef __END_DECLS +#ifdef __cplusplus + #define __BEGIN_DECLS extern "C" { + #define __END_DECLS } +#else + #define __BEGIN_DECLS /* empty */ + #define __END_DECLS /* empty */ +#endif + +__BEGIN_DECLS + +extern const unsigned int igraph_i_isoclass_3[]; +extern const unsigned int igraph_i_isoclass_4[]; +extern const unsigned int igraph_i_isoclass_3u[]; +extern const unsigned int igraph_i_isoclass_4u[]; +extern const unsigned int igraph_i_isoclass2_3[]; +extern const unsigned int igraph_i_isoclass2_4[]; +extern const unsigned int igraph_i_isoclass2_3u[]; +extern const unsigned int igraph_i_isoclass2_4u[]; +extern const unsigned int igraph_i_isoclass_3_idx[]; +extern const unsigned int igraph_i_isoclass_4_idx[]; +extern const unsigned int igraph_i_isoclass_3u_idx[]; +extern const unsigned int igraph_i_isoclass_4u_idx[]; + +__END_DECLS + +#endif diff --git a/src/cigraph/src/layout.c b/src/cigraph/src/layout.c index e9d2ee8..7b543e5 100644 --- a/src/cigraph/src/layout.c +++ b/src/cigraph/src/layout.c @@ -773,8 +773,12 @@ struct igraph_i_reingold_tilford_vertex { of the subtree rooted at this node */ long int right_contour; /* Next right node of the contour of the subtree rooted at this node */ - igraph_real_t offset_follow_lc; /* X offset when following the left contour */ - igraph_real_t offset_follow_rc; /* X offset when following the right contour */ + igraph_real_t offset_to_left_contour; /* X offset when following the left contour */ + igraph_real_t offset_to_right_contour; /* X offset when following the right contour */ + long int left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ + long int right_extreme; /* Rightmost node on the deepest layer of the subtree rooted at this node */ + igraph_real_t offset_to_left_extreme; /* X offset when jumping to the left extreme node */ + igraph_real_t offset_to_right_extreme; /* X offset when jumping to the right extreme node */ }; static int igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, @@ -783,6 +787,9 @@ static int igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold igraph_matrix_t *res, long int node, long int vcount, igraph_real_t xpos); +/* uncomment the next line for debugging the Reingold-Tilford layout */ +/* #define LAYOUT_RT_DEBUG 1 */ + static int igraph_i_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, @@ -812,8 +819,12 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, vdata[i].offset = 0.0; vdata[i].left_contour = -1; vdata[i].right_contour = -1; - vdata[i].offset_follow_lc = 0.0; - vdata[i].offset_follow_rc = 0.0; + vdata[i].offset_to_left_contour = 0.0; + vdata[i].offset_to_right_contour = 0.0; + vdata[i].left_extreme = i; + vdata[i].right_extreme = i; + vdata[i].offset_to_left_extreme = 0.0; + vdata[i].offset_to_right_extreme = 0.0; } vdata[root].parent = root; vdata[root].level = 0; @@ -855,6 +866,24 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, IGRAPH_PROGRESS("Reingold-Tilford tree layout", 100.0, NULL); +#ifdef LAYOUT_RT_DEBUG + for (i = 0; i < no_of_nodes; i++) { + printf( + "%3ld: offset = %.2f, contours = [%ld, %ld], contour offsets = [%.2f, %.2f]\n", + i, vdata[i].offset, + vdata[i].left_contour, vdata[i].right_contour, + vdata[i].offset_to_left_contour, vdata[i].offset_to_right_contour + ); + if (vdata[i].left_extreme != i || vdata[i].right_extreme != i) { + printf( + " extrema = [%ld, %ld], offsets to extrema = [%.2f, %.2f]\n", + vdata[i].left_extreme, vdata[i].right_extreme, + vdata[i].offset_to_left_extreme, vdata[i].offset_to_right_extreme + ); + } + } +#endif + return 0; } @@ -880,9 +909,12 @@ static int igraph_i_layout_reingold_tilford_postorder( struct igraph_i_reingold_tilford_vertex *vdata, long int node, long int vcount) { long int i, j, childcount, leftroot, leftrootidx; + const igraph_real_t minsep = 1; igraph_real_t avg; - /* printf("Starting visiting node %d\n", node); */ +#ifdef LAYOUT_RT_DEBUG + printf("Starting visiting node %ld\n", node); +#endif /* Check whether this node is a leaf node */ childcount = 0; @@ -911,93 +943,156 @@ static int igraph_i_layout_reingold_tilford_postorder( * will be checked against the left contour of the next subtree */ leftroot = leftrootidx = -1; avg = 0.0; - /*printf("Visited node %d and arranged its subtrees\n", node);*/ +#ifdef LAYOUT_RT_DEBUG + printf("Visited node %ld and arranged its subtrees\n", node); +#endif for (i = 0, j = 0; i < vcount; i++) { if (i == node) { continue; } if (vdata[i].parent == node) { - /*printf(" Placing child %d on level %d\n", i, vdata[i].level);*/ if (leftroot >= 0) { /* Now we will follow the right contour of leftroot and the * left contour of the subtree rooted at i */ - long lnode, rnode; - igraph_real_t loffset, roffset, minsep, rootsep; + long lnode, rnode, auxnode; + igraph_real_t loffset, roffset, rootsep, newoffset; + +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld, to the right of %ld\n", i, vdata[i].level, leftroot); +#endif lnode = leftroot; rnode = i; - minsep = 1; rootsep = vdata[leftroot].offset + minsep; - loffset = 0; roffset = minsep; - /*printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", - lnode, rnode, loffset, roffset, rootsep);*/ + loffset = vdata[leftroot].offset; roffset = loffset + minsep; + + /* Keep on updating the right contour now that we have attached + * a new node to the subtree being built */ + vdata[node].right_contour = i; + vdata[node].offset_to_right_contour = rootsep; + +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif while ((lnode >= 0) && (rnode >= 0)) { /* Step to the next level on the right contour of the left subtree */ if (vdata[lnode].right_contour >= 0) { - loffset += vdata[lnode].offset_follow_rc; + loffset += vdata[lnode].offset_to_right_contour; lnode = vdata[lnode].right_contour; } else { - /* Left subtree ended there. The right contour of the left subtree - * will continue to the next step on the right subtree. */ + /* Left subtree ended there. The left and right contour + * of the left subtree will continue to the next step + * on the right subtree. */ if (vdata[rnode].left_contour >= 0) { - /*printf(" Left subtree ended, continuing left subtree's left and right contour on right subtree (node %ld)\n", vdata[rnode].left_contour);*/ - vdata[lnode].left_contour = vdata[rnode].left_contour; - vdata[lnode].right_contour = vdata[rnode].left_contour; - vdata[lnode].offset_follow_lc = vdata[lnode].offset_follow_rc = - (roffset - loffset) + vdata[rnode].offset_follow_lc; - /*printf(" vdata[lnode].offset_follow_* = %.4f\n", vdata[lnode].offset_follow_lc);*/ + auxnode = vdata[node].left_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = (vdata[node].offset_to_right_extreme - vdata[node].offset_to_left_extreme) + minsep + vdata[rnode].offset_to_left_contour; + vdata[auxnode].left_contour = vdata[rnode].left_contour; + vdata[auxnode].right_contour = vdata[rnode].left_contour; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* since we attached a larger subtree to the + * already placed left subtree, we need to update + * the extrema of the subtree rooted at 'node' */ + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme + rootsep; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; +#ifdef LAYOUT_RT_DEBUG + printf(" Left subtree ended earlier, continuing left subtree's left and right contour on right subtree (node %ld gets connected to node %ld)\n", auxnode, vdata[rnode].left_contour); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif + } else { + /* Both subtrees are ending at the same time; the + * left extreme node of the subtree rooted at + * 'node' remains the same but the right extreme + * will change */ + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme + rootsep; } lnode = -1; } /* Step to the next level on the left contour of the right subtree */ if (vdata[rnode].left_contour >= 0) { - roffset += vdata[rnode].offset_follow_lc; + roffset += vdata[rnode].offset_to_left_contour; rnode = vdata[rnode].left_contour; } else { - /* Right subtree ended here. The left contour of the right + /* Right subtree ended here. The right contour of the right * subtree will continue to the next step on the left subtree. * Note that lnode has already been advanced here */ if (lnode >= 0) { - /*printf(" Right subtree ended, continuing right subtree's left and right contour on left subtree (node %ld)\n", lnode);*/ - vdata[rnode].left_contour = lnode; - vdata[rnode].right_contour = lnode; - vdata[rnode].offset_follow_lc = vdata[rnode].offset_follow_rc = - (loffset - roffset); /* loffset has also been increased earlier */ - /*printf(" vdata[rnode].offset_follow_* = %.4f\n", vdata[rnode].offset_follow_lc);*/ + auxnode = vdata[i].right_extreme; + + /* this is the "threading" step that the original + * paper is talking about */ + newoffset = loffset - rootsep - vdata[i].offset_to_right_extreme; + vdata[auxnode].left_contour = lnode; + vdata[auxnode].right_contour = lnode; + vdata[auxnode].offset_to_left_contour = vdata[auxnode].offset_to_right_contour = newoffset; + + /* no need to update the extrema of the subtree + * rooted at 'node' because the right subtree was + * smaller */ +#ifdef LAYOUT_RT_DEBUG + printf(" Right subtree ended earlier, continuing right subtree's left and right contour on left subtree (node %ld gets connected to node %ld)\n", auxnode, lnode); + printf(" New contour following offset for node %ld is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); +#endif } rnode = -1; } - /*printf(" Contour: [%d, %d], offsets: [%lf, %lf], rootsep: %lf\n", - lnode, rnode, loffset, roffset, rootsep);*/ +#ifdef LAYOUT_RT_DEBUG + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + lnode, rnode, loffset, roffset, rootsep); +#endif /* Push subtrees away if necessary */ if ((lnode >= 0) && (rnode >= 0) && (roffset - loffset < minsep)) { - /*printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset);*/ +#ifdef LAYOUT_RT_DEBUG + printf(" Pushing right subtree away by %lf\n", minsep-roffset+loffset); +#endif rootsep += minsep - roffset + loffset; roffset = loffset + minsep; + vdata[node].offset_to_right_contour = rootsep; } } - /*printf(" Offset of subtree with root node %d will be %lf\n", i, rootsep);*/ +#ifdef LAYOUT_RT_DEBUG + printf(" Offset of subtree with root node %ld will be %lf\n", i, rootsep); +#endif vdata[i].offset = rootsep; - vdata[node].right_contour = i; - vdata[node].offset_follow_rc = rootsep; + vdata[node].offset_to_right_contour = rootsep; avg = (avg * j) / (j + 1) + rootsep / (j + 1); leftrootidx = j; leftroot = i; } else { + /* This is the first child of the node being considered so we + * can simply place the subtree on our virtual canvas */ +#ifdef LAYOUT_RT_DEBUG + printf(" Placing child %ld on level %ld as first child\n", i, vdata[i].level); +#endif leftrootidx = j; leftroot = i; vdata[node].left_contour = i; vdata[node].right_contour = i; - vdata[node].offset_follow_lc = 0.0; - vdata[node].offset_follow_rc = 0.0; + vdata[node].offset_to_left_contour = 0.0; + vdata[node].offset_to_right_contour = 0.0; + vdata[node].left_extreme = vdata[i].left_extreme; + vdata[node].right_extreme = vdata[i].right_extreme; + vdata[node].offset_to_left_extreme = vdata[i].offset_to_left_extreme; + vdata[node].offset_to_right_extreme = vdata[i].offset_to_right_extreme; avg = vdata[i].offset; } j++; } } - /*printf("Shifting node to be centered above children. Shift amount: %lf\n", avg);*/ - vdata[node].offset_follow_lc -= avg; - vdata[node].offset_follow_rc -= avg; +#ifdef LAYOUT_RT_DEBUG + printf("Shifting node %ld to be centered above children. Shift amount: %lf\n", node, avg); +#endif + vdata[node].offset_to_left_contour -= avg; + vdata[node].offset_to_right_contour -= avg; + vdata[node].offset_to_left_extreme -= avg; + vdata[node].offset_to_right_extreme -= avg; for (i = 0, j = 0; i < vcount; i++) { if (i == node) { continue; diff --git a/src/cigraph/src/layout_kk.c b/src/cigraph/src/layout_kk.c index ccb4c38..b59af57 100644 --- a/src/cigraph/src/layout_kk.c +++ b/src/cigraph/src/layout_kk.c @@ -41,8 +41,10 @@ * contain the result (x-positions in column zero and * y-positions in column one) and will be resized if needed. * \param use_seed Boolean, whether to use the values supplied in the - * \p res argument as the initial configuration. If zero then a - * random initial configuration is used. + * \p res argument as the initial configuration. If zero and there + * are any limits on the X or Y coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed on a + * circle of radius 1 as the initial configuration. * \param maxiter The maximum number of iterations to perform. A reasonable * default value is at least ten (or more) times the number of * vertices. @@ -340,8 +342,10 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, * contain the result (x-positions in column zero and * y-positions in column one) and will be resized if needed. * \param use_seed Boolean, whether to use the values supplied in the - * \p res argument as the initial configuration. If zero then a - * random initial configuration is used. + * \p res argument as the initial configuration. If zero and there + * are any limits on the X, Y or Z coordinates, then a random initial + * configuration is used. Otherwise the vertices are placed uniformly + * on a sphere of radius 1 as the initial configuration. * \param maxiter The maximum number of iterations to perform. A reasonable * default value is at least ten (or more) times the number of * vertices. @@ -577,6 +581,7 @@ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, dz = old_z - MATRIX(*res, i, 2); dist = sqrt(dx * dx + dy * dy + dz * dz); den = dist * (dx * dx + dy * dy + dz * dz); + k_mi = MATRIX(kij, m, i); l_mi = MATRIX(lij, m, i); Axx += k_mi * (1 - l_mi * (dy * dy + dz * dz) / den); @@ -594,9 +599,16 @@ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, #define DET(a,b,c,d,e,f,g,h,i) ((a*e*i+b*f*g+c*d*h)-(c*e*g+b*d*i+a*f*h)) detnum = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Axz, Ayz, Azz); - delta_x = DET(Ax, Ay, Az, Axy, Ayy, Ayz, Axz, Ayz, Azz) / detnum; - delta_y = DET(Axx, Axy, Axz, Ax, Ay, Az, Axz, Ayz, Azz) / detnum; - delta_z = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Ax, Ay, Az ) / detnum; + if (detnum != 0) { + delta_x = DET(Ax, Ay, Az, Axy, Ayy, Ayz, Axz, Ayz, Azz) / detnum; + delta_y = DET(Axx, Axy, Axz, Ax, Ay, Az, Axz, Ayz, Azz) / detnum; + delta_z = DET(Axx, Axy, Axz, Axy, Ayy, Ayz, Ax, Ay, Az ) / detnum; + } else { + /* No new stable position for node m; this can happen in rare + * cases, e.g., if the graph has two nodes only. It's best to leave + * the node where it is. */ + delta_x = delta_y = delta_z = 0; + } new_x = old_x + delta_x; new_y = old_y + delta_y; diff --git a/src/cigraph/src/motifs.c b/src/cigraph/src/motifs.c index 9161461..f5ab73b 100644 --- a/src/cigraph/src/motifs.c +++ b/src/cigraph/src/motifs.c @@ -27,24 +27,11 @@ #include "igraph_adjlist.h" #include "igraph_interrupt_internal.h" #include "igraph_interface.h" +#include "igraph_isoclasses.h" #include "igraph_nongraph.h" #include "igraph_stack.h" #include "config.h" -/* TODO create header for these functions: */ -extern unsigned int igraph_i_isoclass_3[]; -extern unsigned int igraph_i_isoclass_4[]; -extern unsigned int igraph_i_isoclass_3u[]; -extern unsigned int igraph_i_isoclass_4u[]; -extern unsigned int igraph_i_isoclass2_3[]; -extern unsigned int igraph_i_isoclass2_4[]; -extern unsigned int igraph_i_isoclass2_3u[]; -extern unsigned int igraph_i_isoclass2_4u[]; -extern unsigned int igraph_i_isoclass_3_idx[]; -extern unsigned int igraph_i_isoclass_4_idx[]; -extern unsigned int igraph_i_isoclass_3u_idx[]; -extern unsigned int igraph_i_isoclass_4u_idx[]; - /** * Callback function for igraph_motifs_randesu that counts the motifs by * isomorphism class in a histogram. @@ -213,7 +200,7 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, long int *added; char *subg; - unsigned int *arr_idx, *arr_code; + const unsigned int *arr_idx, *arr_code; int code = 0; unsigned char mul, idx; @@ -657,12 +644,9 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, * \brief Count the total number of motifs in a graph * * - * This function counts the total number of motifs in a graph without - * assigning isomorphism classes to them. - * - * - * Directed motifs will be counted in directed graphs and undirected - * motifs in undirected graphs. + * This function counts the total number of motifs in a graph, + * i.e. the number of of (weakly) connected triplets or quadruplets, + * without assigning isomorphism classes to them. * * \param graph The graph object to study. * \param no Pointer to an integer type, the result will be stored diff --git a/src/cigraph/src/operators.c b/src/cigraph/src/operators.c index 1ef14d5..fa666d3 100644 --- a/src/cigraph/src/operators.c +++ b/src/cigraph/src/operators.c @@ -998,6 +998,10 @@ int igraph_difference(igraph_t *res, IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); n1--; + /* handle loop edges properly in undirected graphs */ + if (!directed && i == v1) { + n1--; + } } else if (v2 > v1) { n2--; } else { @@ -1014,6 +1018,11 @@ int igraph_difference(igraph_t *res, IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } } n1--; } @@ -1031,6 +1040,11 @@ int igraph_difference(igraph_t *res, IGRAPH_CHECK(igraph_vector_push_back(&edge_ids, e1)); IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); IGRAPH_CHECK(igraph_vector_push_back(&edges, v1)); + + /* handle loop edges properly in undirected graphs */ + if (!directed && v1 == i) { + n1--; + } } n1--; } diff --git a/src/cigraph/src/random.c b/src/cigraph/src/random.c index 8e15415..5284147 100644 --- a/src/cigraph/src/random.c +++ b/src/cigraph/src/random.c @@ -34,7 +34,7 @@ /** * \section about_rngs * - *
+ *
* About random numbers in igraph, use cases * * @@ -51,9 +51,9 @@ /** * \section rng_use_cases * - *
Use cases + *
Use cases * - *
Normal (default) use + *
Normal (default) use * * If the user does not use any of the RNG functions explicitly, but calls * some of the randomized igraph functions, then a default RNG is set @@ -70,7 +70,7 @@ * *
* - *
Reproducible simulations + *
Reproducible simulations * * If reproducible results are needed, then the user should set the * seed of the default random number generator explicitly, using the @@ -81,7 +81,7 @@ * *
* - *
Changing the default generator + *
Changing the default generator * * By default igraph uses the \ref igraph_rng_default() random number * generator. This can be changed any time by calling \ref @@ -91,7 +91,7 @@ * *
* - *
Using multiple generators + *
Using multiple generators * * igraph also provides functions to set up multiple random number * generators, using the \ref igraph_rng_init() function, and then @@ -107,7 +107,7 @@ * *
* - *
Example + *
Example * * \example examples/simple/random_seed.c * diff --git a/src/cigraph/src/scg.c b/src/cigraph/src/scg.c index dde1622..6331807 100644 --- a/src/cigraph/src/scg.c +++ b/src/cigraph/src/scg.c @@ -122,7 +122,7 @@ * role, as for instance is the case of dynamical processes on networks. * * - *
SCG in brief + *
SCG in brief * * The main idea of SCG is to operate on a matrix a shrinkage operation * specifically designed to preserve some of the matrix eigenpairs while @@ -210,7 +210,7 @@ * *
* - *
Functions for performing SCG + *
Functions for performing SCG * * The main functions are \ref igraph_scg_adjacency(), \ref * igraph_scg_laplacian() and \ref igraph_scg_stochastic(). @@ -241,7 +241,7 @@ * *
* - *
References + *
References * * [1] D. Morton de Lachapelle, D. Gfeller, and P. De Los Rios, * Shrinking Matrices while Preserving their Eigenpairs with Application diff --git a/src/cigraph/src/structural_properties.c b/src/cigraph/src/structural_properties.c index 4196fcf..05bfb1a 100644 --- a/src/cigraph/src/structural_properties.c +++ b/src/cigraph/src/structural_properties.c @@ -6945,15 +6945,22 @@ int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, } } -int igraph_i_is_graphical_degree_sequence_undirected( - const igraph_vector_t *degrees, igraph_bool_t *res) { +int igraph_i_is_graphical_degree_sequence_undirected(const igraph_vector_t *degrees, igraph_bool_t *res) { igraph_vector_t work; long int w, b, s, c, n, k; + n = igraph_vector_size(degrees); + + /* zero-length sequences are considered graphical */ + if (n == 0) { + *res = 1; + return IGRAPH_SUCCESS; + } + IGRAPH_CHECK(igraph_vector_copy(&work, degrees)); IGRAPH_FINALLY(igraph_vector_destroy, &work); - igraph_vector_sort(&work); + igraph_vector_reverse_sort(&work); /* This algorithm is outlined in TR-2011-11 of the Egervary Research Group, * ISSN 1587-4451. The main loop of the algorithm is O(n) but it is dominated @@ -6964,13 +6971,12 @@ int igraph_i_is_graphical_degree_sequence_undirected( * the degrees themselves. w and k are zero-based here; in the technical * report they are 1-based */ *res = 1; - n = igraph_vector_size(&work); w = n - 1; b = 0; s = 0; c = 0; for (k = 0; k < n; k++) { - b += VECTOR(*degrees)[k]; + b += VECTOR(work)[k]; c += w; - while (w > k && VECTOR(*degrees)[w] <= k + 1) { - s += VECTOR(*degrees)[w]; + while (w > k && VECTOR(work)[w] <= k + 1) { + s += VECTOR(work)[w]; c -= (k + 1); w--; } @@ -6986,7 +6992,7 @@ int igraph_i_is_graphical_degree_sequence_undirected( igraph_vector_destroy(&work); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } typedef struct { diff --git a/src/cigraph/src/structure_generators.c b/src/cigraph/src/structure_generators.c index e1c0090..e9ca920 100644 --- a/src/cigraph/src/structure_generators.c +++ b/src/cigraph/src/structure_generators.c @@ -764,23 +764,29 @@ int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, * \function igraph_lattice * \brief Arbitrary dimensional square lattices. * - * Creates n-dimensional square lattices of the given size. Optionally, + * Creates d-dimensional square lattices of the given size. Optionally, * the lattice can be made periodic, and the neighbors within a given * graph distance can be connected. * * * In the zero-dimensional case, the singleton graph is returned. * + * + * The vertices of the resulting graph are ordered such that the + * index of the vertex at position (i_0, i_1, i_2, ..., i_d) + * in a lattice of size (n_0, n_1, ..., n_d) will be + * i_0 + n_0 * i_1 + n_0 * n_1 * i_2 + .... + * * \param graph An uninitialized graph object. * \param dimvector Vector giving the sizes of the lattice in each of - * its dimensions. I.e. the dimension of the lattice will be the + * its dimensions. The dimension of the lattice will be the * same as the length of this vector. * \param nei Integer value giving the distance (number of steps) * within which two vertices will be connected. - * \param directed Boolean, whether to create a directed graph. The - * direction of the edges is determined by the generation - * algorithm and is unlikely to suit you, so this isn't a very - * useful option. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual and \c circular arguments are not set to true, + * edges will be directed from lower-index vertices towards + * higher-index ones. * \param mutual Boolean, if the graph is directed this gives whether * to create all connections as mutual. * \param circular Boolean, defines whether the generated lattice is @@ -2387,7 +2393,7 @@ int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, * invalid Prüfer sequence given * \endclist * - * \sa \ref igraph_tree(), \ref igraph_tree_game() + * \sa \ref igraph_to_prufer(), \ref igraph_tree(), \ref igraph_tree_game() * */ diff --git a/src/cigraph/src/topology.c b/src/cigraph/src/topology.c index aaf11d7..a92c5ed 100644 --- a/src/cigraph/src/topology.c +++ b/src/cigraph/src/topology.c @@ -31,6 +31,7 @@ #include "igraph_stack.h" #include "igraph_attributes.h" #include "igraph_structural.h" +#include "igraph_isoclasses.h" #include "config.h" const unsigned int igraph_i_isoclass_3[] = { 0, 1, 1, 3, 1, 5, 6, 7, @@ -689,6 +690,9 @@ const unsigned int igraph_i_classedges_4u[] = { 2, 3, 1, 3, 0, 3, 1, 2, 0, 2, 0, * (between 0 and 15), for undirected graph it is only 4. For graphs * with four vertices it is 218 (directed) and 11 (undirected). * + * + * Multi-edges and self-loops are ignored by this function. + * * \param graph The graph object. * \param isoclass Pointer to an integer, the isomorphism class will * be stored here. @@ -754,26 +758,31 @@ int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { * \brief Decides whether two graphs are isomorphic * * - * From Wikipedia: The graph isomorphism problem or GI problem is the - * graph theory problem of determining whether, given two graphs G1 - * and G2, it is possible to permute (or relabel) the vertices of one - * graph so that it is equal to the other. Such a permutation is - * called a graph isomorphism. + * In simple terms, two graphs are isomorphic if they become indistinguishable + * from each other once their vertex labels are removed (rendering the vertices + * within each graph indistiguishable). More precisely, two graphs are isomorphic + * if there is a one-to-one mapping from the vertices of the first one + * to the vertices of the second such that it transforms the edge set of the + * first graph into the edge set of the second. This mapping is called + * an \em isomorphism. * - * This function decides which graph isomorphism algorithm to be + * Currently, this function supports simple graphs and graphs + * with self-loops, but does not support multigraphs. + * + * This function decides which graph isomorphism algorithm to be * used based on the input graphs. Right now it does the following: * \olist * \oli If one graph is directed and the other undirected then an * error is triggered. + * \oli If one of the graphs has multi-edges then an error is triggered. * \oli If the two graphs does not have the same number of vertices * and edges it returns with \c FALSE. * \oli Otherwise, if the graphs have three or four vertices then an O(1) * algorithm is used with precomputed data. * \oli Otherwise BLISS is used, see \ref igraph_isomorphic_bliss(). * \endolist - * * - * Please call the VF2 and BLISS functions directly if you need + * Please call the VF2 and BLISS functions directly if you need * something more sophisticated, e.g. you need the isomorphic mapping. * * \param graph1 The first graph. @@ -793,7 +802,14 @@ int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, long int nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2); long int edges1 = igraph_ecount(graph1), edges2 = igraph_ecount(graph2); igraph_bool_t dir1 = igraph_is_directed(graph1), dir2 = igraph_is_directed(graph2); - igraph_bool_t loop1, loop2; + igraph_bool_t loop1, loop2, multi1, multi2; + + IGRAPH_CHECK(igraph_has_multiple(graph1, &multi1)); + IGRAPH_CHECK(igraph_has_multiple(graph2, &multi2)); + + if (multi1 || multi2) { + IGRAPH_ERROR("Isomorphism testing is not implemented for multigraphs", IGRAPH_UNIMPLEMENTED); + } if (dir1 != dir2) { IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); @@ -821,7 +837,8 @@ int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, * Graph isomorphism for 3-4 vertices * * This function uses precomputed indices to decide isomorphism - * problems for graphs with only 3 or 4 vertices. + * problems for graphs with only 3 or 4 vertices. Multi-edges + * and self-loops are ignored by this function. * \param graph1 The first input graph. * \param graph2 The second input graph. Must have the same * directedness as \p graph1. diff --git a/src/cigraph/src/vector.pmt b/src/cigraph/src/vector.pmt index d1759e7..37b4f7b 100644 --- a/src/cigraph/src/vector.pmt +++ b/src/cigraph/src/vector.pmt @@ -752,19 +752,29 @@ int FUNCTION(igraph_vector, sort_cmp)(const void *a, const void *b) { return (*da > *db) - (*da < *db); } +/** + * \ingroup vector + * \function igraph_vector_reverse_sort_cmp + * \brief Internal comparison function of vector elements, used by + * \ref igraph_vector_reverse_sort(). + */ + +int FUNCTION(igraph_vector, reverse_sort_cmp)(const void *a, const void *b) { + const BASE *da = (const BASE *) a; + const BASE *db = (const BASE *) b; + + return (*da < *db) - (*da > *db); +} + /** * \ingroup vector * \function igraph_vector_sort * \brief Sorts the elements of the vector into ascending order. * - * - * This function uses the built-in sort function of the C library. * \param v Pointer to an initialized vector object. * - * Time complexity: should be - * O(nlogn) for - * n - * elements. + * Time complexity: + * O(n log n) for n elements. */ void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { @@ -774,6 +784,24 @@ void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { sizeof(BASE), FUNCTION(igraph_vector, sort_cmp)); } +/** + * \ingroup vector + * \function igraph_vector_reverse_sort + * \brief Sorts the elements of the vector into descending order. + * + * \param v Pointer to an initialized vector object. + * + * Time complexity: + * O(n log n) for n elements. + */ + +void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v) { + assert(v != NULL); + assert(v->stor_begin != NULL); + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), + sizeof(BASE), FUNCTION(igraph_vector, reverse_sort_cmp)); +} + /** * Ascending comparison function passed to qsort from igraph_vector_qsort_ind */ diff --git a/src/igraph_rinterface.cpp b/src/igraph_rinterface.cpp index 5bc035e..11b23f2 100644 --- a/src/igraph_rinterface.cpp +++ b/src/igraph_rinterface.cpp @@ -31,6 +31,17 @@ #define GET_LENGTH(x) Rf_length(x) +/* + * From R igraph file src/rinterface.c + */ +void R_igraph_myhandler (const char *reason, const char *file, + int line, int igraph_errno) { + IGRAPH_FINALLY_FREE(); + error("At %s:%i : %s, %s", file, line, reason, + igraph_strerror(igraph_errno)); +} + + /* * From R igraph file src/rinterface.c */ diff --git a/src/leidenFindPartitionR2C.cpp b/src/leidenFindPartitionR2C.cpp index 18eb5fe..769fc88 100644 --- a/src/leidenFindPartitionR2C.cpp +++ b/src/leidenFindPartitionR2C.cpp @@ -67,7 +67,7 @@ #define DEBUG 0 -extern igraph_error_handler_t igraph_error_handler_printignore; +extern igraph_error_handler_t R_igraph_myhandler; int xcheckParametersRValues( SEXP initial_membership, SEXP edge_weights, SEXP node_sizes, int *fstatus ); @@ -117,7 +117,7 @@ SEXP _leiden_find_partition( SEXP igraph, SEXP partition_type, SEXP initial_memb * Print error message and return on igraph error. (Default is to * abort on error.) */ - igraph_set_error_handler( igraph_error_handler_printignore ); + igraph_set_error_handler( R_igraph_myhandler ); /* * Convert R igraph object to an igraph graph using the igraph internal functions.