From f4b1736b3101882918c19780ac49708de0727f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szabolcs=20Horva=CC=81t?= Date: Thu, 12 Sep 2024 08:54:21 +0000 Subject: [PATCH] chore: Update vendored sources to igraph/igraph@bd9306d12c3493cf9edc292e07c236cb7bb8d15f chore: improve changelog fix: eigenvector centralization now always assumes scaled vertex-level centrality scores doc: more cross-linking for harmonic centrality doc: add reference to betweenness docs ci: apt-get update to work around outdated index ci: test with clang 20 doc: doc cleanup in centralization functions refactor: minor cleanup / IDE-friendliness for community_to_membership() ci: re-enable travis on ppc64 / s390x --- src/vendor/cigraph/.travis.yml | 20 +-- src/vendor/cigraph/CHANGELOG.md | 1 + src/vendor/cigraph/azure-pipelines.yml | 10 +- .../cigraph/src/centrality/betweenness.c | 16 +++ .../cigraph/src/centrality/centralization.c | 115 ++++++++++++------ src/vendor/cigraph/src/centrality/closeness.c | 3 +- .../cigraph/src/community/community_misc.c | 37 +++--- src/vendor/igraph_version.h | 4 +- 8 files changed, 133 insertions(+), 73 deletions(-) diff --git a/src/vendor/cigraph/.travis.yml b/src/vendor/cigraph/.travis.yml index afce718b7f..295272b1d3 100644 --- a/src/vendor/cigraph/.travis.yml +++ b/src/vendor/cigraph/.travis.yml @@ -76,17 +76,17 @@ jobs: - mkdir build && cd build - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=OFF -DIGRAPH_USE_INTERNAL_LAPACK=OFF -DIGRAPH_USE_INTERNAL_ARPACK=OFF -DIGRAPH_USE_INTERNAL_GLPK=OFF -DIGRAPH_USE_INTERNAL_GMP=OFF -DIGRAPH_VERIFY_FINALLY_STACK=OFF -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address - # - name: "Linux ppc64" - # os: linux - # arch: ppc64le + - name: "Linux ppc64" + os: linux + arch: ppc64le - # - name: "Linux s390x" - # os: linux - # arch: s390x - # # Do not enable ASan, as it leads to linking errors. - # before_script: - # - mkdir build && cd build - # - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON + - name: "Linux s390x" + os: linux + arch: s390x + # Do not enable ASan, as it leads to linking errors. + before_script: + - mkdir build && cd build + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON #notifications: # email: diff --git a/src/vendor/cigraph/CHANGELOG.md b/src/vendor/cigraph/CHANGELOG.md index 49e7103ec3..5f5e54890f 100644 --- a/src/vendor/cigraph/CHANGELOG.md +++ b/src/vendor/cigraph/CHANGELOG.md @@ -17,6 +17,7 @@ - `igraph_feedback_arc_set()` uses a much faster method for solving the exact minimum feedback arc set problem. The new method (`IGRAPH_FAS_EXACT_IP_CG`) is used by default (i.e. with `IGRAPH_FAS_EXACT_IP`), but the previous method is also kept available (`IGRAPH_FAS_EXACT_IP_TI`). - `igraph_motifs_randesu()`, `igraph_motifs_randesu_callback()`, `igraph_motifs_randesu_estimate()` and `igraph_motifs_randesu_no()` now accept `NULL` for their `cut_prob` parameter, signifying that a complete search should be performed. + - `igraph_centralization_eigenvector_centrality_tmax()` and `igraph_centralization_eigenvector_centrality()` cannot produce meaningful results without normalizing vertex-level eigenvector centrality in a well-defined way. This was not the case when using `scale=false`. These functions now ignore the value of the `scale` parameter and always scale vertex-level centrality scores to have a maximum of 1. If you require a different type of normalization for the vertex-level eigenvector centrality scores, perform this normalization manually, and call `igraph_centralization()` to compute the centralization. ### Fixed diff --git a/src/vendor/cigraph/azure-pipelines.yml b/src/vendor/cigraph/azure-pipelines.yml index c5ed7b6e06..c38b9e7a4d 100644 --- a/src/vendor/cigraph/azure-pipelines.yml +++ b/src/vendor/cigraph/azure-pipelines.yml @@ -14,7 +14,9 @@ jobs: # In this test we install and generate locales so that igraph_enter/exit_safelocale() can be tested - job: linux_static_vendored steps: - - script: sudo apt-get install ninja-build ccache language-pack-de -y + - script: | + sudo apt-get update + sudo apt-get install ninja-build ccache language-pack-de -y displayName: Install dependencies - script: | @@ -75,19 +77,19 @@ jobs: extra_cmake_args: '-DBLA_VENDOR=OpenBLAS' build_shared: true - - job: linux_clang_19 + - job: linux_clang_20 steps: - script: | sudo apt-get install ninja-build ccache -y wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh - sudo ./llvm.sh 19 + sudo ./llvm.sh 20 displayName: Install dependencies - template: .azure/build.yml parameters: build_type: Debug - extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_C_COMPILER=clang-19 -DCMAKE_CXX_COMPILER=clang++-19' + extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_C_COMPILER=clang-20 -DCMAKE_CXX_COMPILER=clang++-20' - job: linux_x87 steps: diff --git a/src/vendor/cigraph/src/centrality/betweenness.c b/src/vendor/cigraph/src/centrality/betweenness.c index 9adff201e2..8d14dae1e2 100644 --- a/src/vendor/cigraph/src/centrality/betweenness.c +++ b/src/vendor/cigraph/src/centrality/betweenness.c @@ -461,6 +461,14 @@ static igraph_error_t igraph_i_betweenness_check_weights( * vertices, the value of these geodesics are weighted by one over the * number of geodesics. * + * + * Reference: + * + * + * Ulrik Brandes: A faster algorithm for betweenness centrality. + * The Journal of Mathematical Sociology, 25(2), 163–177 (2001). + * https://doi.org/10.1080/0022250X.2001.9990249 + * * \param graph The graph object. * \param res The result of the computation, a vector containing the * betweenness scores for the specified vertices. @@ -695,6 +703,14 @@ igraph_error_t igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t * vertices, the value of these geodesics are weighted by one over the * number of geodesics. * + * + * Reference: + * + * + * Ulrik Brandes: A faster algorithm for betweenness centrality. + * The Journal of Mathematical Sociology, 25(2), 163–177 (2001). + * https://doi.org/10.1080/0022250X.2001.9990249 + * * \param graph The graph object. * \param result The result of the computation, vector containing the * betweenness scores for the edges. diff --git a/src/vendor/cigraph/src/centrality/centralization.c b/src/vendor/cigraph/src/centrality/centralization.c index 1f27ca1107..e6cf1ab7e9 100644 --- a/src/vendor/cigraph/src/centrality/centralization.c +++ b/src/vendor/cigraph/src/centrality/centralization.c @@ -168,15 +168,15 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector * * * There are two ways to call this function, the first is to supply a - * graph as the graph argument, and then the number of + * graph as the \p graph argument, and then the number of * vertices is taken from this object, and its directedness is - * considered as well. The nodes argument is ignored in - * this case. The mode argument is also ignored if the + * considered as well. The \p nodes argument is ignored in + * this case. The \p mode argument is also ignored if the * supplied graph is undirected. * * - * The other way is to supply a null pointer as the graph - * argument. In this case the nodes and mode + * The other way is to supply a null pointer as the \p graph + * argument. In this case the \p nodes and \p mode * arguments are considered. * * @@ -187,11 +187,11 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the - * graph argument is not a null pointer. + * \p graph argument is not a null pointer. * \param mode Constant, whether the calculation is based on in-degree - * (IGRAPH_IN), out-degree (IGRAPH_OUT) - * or total degree (IGRAPH_ALL). This is ignored if - * the graph argument is not a null pointer and the + * (\c IGRAPH_IN), out-degree (\c IGRAPH_OUT) + * or total degree (\c IGRAPH_ALL). This is ignored if + * the \p graph argument is not a null pointer and the * given graph is undirected. * \param loops Boolean scalar, whether to consider loop edges in the * calculation. @@ -324,15 +324,15 @@ igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, * * * There are two ways to call this function, the first is to supply a - * graph as the graph argument, and then the number of + * graph as the \p graph argument, and then the number of * vertices is taken from this object, and its directedness is - * considered as well. The nodes argument is ignored in - * this case. The directed argument is also ignored if the + * considered as well. The \p nodes argument is ignored in + * this case. The \p directed argument is also ignored if the * supplied graph is undirected. * * - * The other way is to supply a null pointer as the graph - * argument. In this case the nodes and directed + * The other way is to supply a null pointer as the \p graph + * argument. In this case the \p nodes and \p directed * arguments are considered. * * @@ -341,10 +341,10 @@ igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the - * graph argument is not a null pointer. + * \p graph argument is not a null pointer. * \param directed Boolean scalar, whether to use directed paths in * the betweenness calculation. This argument is ignored if - * graph is not a null pointer and it is undirected. + * \p graph is not a null pointer and it is undirected. * \param res Pointer to a real variable, the result is stored here. * \return Error code. * @@ -455,27 +455,28 @@ igraph_error_t igraph_centralization_closeness(const igraph_t *graph, * * * There are two ways to call this function, the first is to supply a - * graph as the graph argument, and then the number of + * graph as the \p graph argument, and then the number of * vertices is taken from this object, and its directedness is - * considered as well. The nodes argument is ignored in - * this case. The mode argument is also ignored if the + * considered as well. The \p nodes argument is ignored in + * this case. The \p mode argument is also ignored if the * supplied graph is undirected. * * - * The other way is to supply a null pointer as the graph - * argument. In this case the nodes and mode + * The other way is to supply a null pointer as the \p graph + * argument. In this case the \p nodes and \p mode * arguments are considered. * * * The most centralized structure is the star. + * * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the - * graph argument is not a null pointer. - * \param mode Constant, specifies what kinf of distances to consider - * to calculate closeness. See the mode argument of + * \p graph argument is not a null pointer. + * \param mode Constant, specifies what kind of distances to consider + * to calculate closeness. See the \p mode argument of * \ref igraph_closeness() for details. This argument is ignored - * if graph is not a null pointer and it is + * if \p graph is not a null pointer and it is * undirected. * \param res Pointer to a real variable, the result is stored here. * \return Error code. @@ -518,13 +519,26 @@ igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, * by passing its arguments to \ref igraph_eigenvector_centrality); * and it calculates the graph level centralization index based on the * results by calling \ref igraph_centralization(). + * + * + * Note that vertex-level eigenvector centrality scores do not have + * a natural scale. As with any eigenvector, their interpretation is + * invariant to scaling by a constant factor. However, due to how + * graph-level \em centralization is defined, its value depends on the + * specific scale/normalization used for vertex-level scores. This is + * true even when the graph-level centralization itself is normalized + * by its theoretical maximum value. This function makes the specific + * choice of scaling vertex-level centrality scores by their maximum + * (i.e. it uses the ∞-norm). Other normalization choices, such as the + * 1-norm or 2-norm are not currently implemented. + * * \param graph The input graph. * \param vector A vector if you need the node-level eigenvector * centrality scores, or a null pointer otherwise. * \param value If not a null pointer, then the leading eigenvalue is * stored here. - * \param scale If not zero then the result will be scaled, such that - * the absolute value of the maximum centrality is one. + * \param scale This parameter is deprecated and ignored since igraph 0.10.14. + * Vertex-level centrality scores are always scaled to have a maximum of one. * \param options Options to ARPACK. See \ref igraph_arpack_options_t * for details. Note that the function overwrites the * n (number of vertices) parameter and @@ -564,6 +578,14 @@ igraph_error_t igraph_centralization_eigenvector_centrality( igraph_real_t realvalue, *myvalue = value; igraph_real_t *tmax = theoretical_max, mytmax; + if (! scale) { + scale = true; + IGRAPH_WARNING("Computing eigenvector centralization requires normalized " + "eigenvector centrality scores. Normalizing eigenvector centralities " + "by their maximum even though 'scale=false' was requested. The 'scale' " + "parameter will be removed in the future."); + } + if (!tmax) { tmax = &mytmax; } @@ -604,28 +626,42 @@ igraph_error_t igraph_centralization_eigenvector_centrality( * * * There are two ways to call this function, the first is to supply a - * graph as the graph argument, and then the number of + * graph as the \p graph argument, and then the number of * vertices is taken from this object, and its directedness is - * considered as well. The nodes argument is ignored in - * this case. The directed argument is also ignored if the + * considered as well. The \p nodes argument is ignored in + * this case. The \p directed argument is also ignored if the * supplied graph is undirected. * * - * The other way is to supply a null pointer as the graph - * argument. In this case the nodes and directed + * The other way is to supply a null pointer as the \p graph + * argument. In this case the \p nodes and \p directed * arguments are considered. * * * The most centralized directed structure is the in-star. The most * centralized undirected structure is the graph with a single edge. + * + * + * Note that vertex-level eigenvector centrality scores do not have + * a natural scale. As with any eigenvector, their interpretation is + * invariant to scaling by a constant factor. However, due to how + * graph-level \em centralization is defined, its value depends on the + * specific scale/normalization used for vertex-level scores. This is + * true even when the graph-level centralization itself is normalized + * by its theoretical maximum value. This function makes the specific + * choice of scaling vertex-level centrality scores by their maximum + * (i.e. it uses the ∞-norm). Other normalization choices, such as the + * 1-norm or 2-norm are not currently implemented. + * * \param graph A graph object or a null pointer, see the description * above. * \param nodes The number of nodes. This is ignored if the - * graph argument is not a null pointer. + * \p graph argument is not a null pointer. * \param directed Boolean scalar, whether to consider edge * directions. This argument is ignored if - * graph is not a null pointer and it is undirected. - * \param scale Whether to rescale the node-level centrality scores to + * \p graph is not a null pointer and it is undirected. + * \param scale This parameter is deprecated and ignored since igraph 0.10.14. + * Vertex-level centrality scores are always assumed to be scaled to * have a maximum of one. * \param res Pointer to a real variable, the result is stored here. * \return Error code. @@ -643,6 +679,15 @@ igraph_error_t igraph_centralization_eigenvector_centrality_tmax( igraph_bool_t scale, igraph_real_t *res) { + if (! scale) { + scale = true; + IGRAPH_WARNING("Theoretical maximum for eigenvector centralization can " + "only be computed with normalized eigenvector centrality " + "scores. Assuming that eigenvector centralities are normalized " + "by their maximum even though 'scale=false' was passed. The 'scale' " + "parameter will be removed in the future."); + } + if (graph) { nodes = igraph_vcount(graph); directed = directed && igraph_is_directed(graph); diff --git a/src/vendor/cigraph/src/centrality/closeness.c b/src/vendor/cigraph/src/centrality/closeness.c index 1bb7d3b4a5..52d62ba14a 100644 --- a/src/vendor/cigraph/src/centrality/closeness.c +++ b/src/vendor/cigraph/src/centrality/closeness.c @@ -719,7 +719,8 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap * and |E| is the number of edges in the graph. The timing decreases with smaller * cutoffs in a way that depends on the graph structure. * - * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). + * \sa \ref igraph_harmonic_centrality() to calculate the exact harmonic centrality. + * Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). */ igraph_error_t igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, diff --git a/src/vendor/cigraph/src/community/community_misc.c b/src/vendor/cigraph/src/community/community_misc.c index a283991f49..6bc3e1aef1 100644 --- a/src/vendor/cigraph/src/community/community_misc.c +++ b/src/vendor/cigraph/src/community/community_misc.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ -/* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2007-2020 The igraph development team + Copyright (C) 2007-2024 The igraph development team 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 @@ -15,10 +13,7 @@ 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 - + along with this program. If not, see . */ #include "igraph_community.h" @@ -106,9 +101,9 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, igraph_vector_int_t *membership, igraph_vector_int_t *csize) { - igraph_integer_t no_of_nodes = nodes; - igraph_integer_t components = no_of_nodes - steps; - igraph_integer_t i, found = 0; + const igraph_integer_t no_of_nodes = nodes; + const igraph_integer_t components = no_of_nodes - steps; + igraph_integer_t found = 0; igraph_vector_int_t tmp; igraph_vector_bool_t already_merged; igraph_vector_int_t own_membership; @@ -127,7 +122,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, IGRAPH_ERRORF("Number of steps should be non-negative, found %" IGRAPH_PRId ".", IGRAPH_EINVAL, steps); } - if (csize != 0 && membership == 0) { + if (csize != NULL && membership == NULL) { /* we need a membership vector to calculate 'csize' but the user did * not provide one; let's allocate one ourselves */ IGRAPH_VECTOR_INT_INIT_FINALLY(&own_membership, no_of_nodes); @@ -147,9 +142,9 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, IGRAPH_VECTOR_BOOL_INIT_FINALLY(&already_merged, steps + no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, steps); - for (i = steps - 1; i >= 0; i--) { - igraph_integer_t c1 = MATRIX(*merges, i, 0); - igraph_integer_t c2 = MATRIX(*merges, i, 1); + for (igraph_integer_t i = steps - 1; i >= 0; i--) { + const igraph_integer_t c1 = MATRIX(*merges, i, 0); + const igraph_integer_t c2 = MATRIX(*merges, i, 1); if (VECTOR(already_merged)[c1] == 0) { VECTOR(already_merged)[c1] = true; @@ -169,7 +164,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, } if (c1 < no_of_nodes) { - igraph_integer_t cid = VECTOR(tmp)[i] - 1; + const igraph_integer_t cid = VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c1] = cid + 1; } @@ -181,7 +176,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, } if (c2 < no_of_nodes) { - igraph_integer_t cid = VECTOR(tmp)[i] - 1; + const igraph_integer_t cid = VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c2] = cid + 1; } @@ -195,13 +190,13 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, } if (membership || csize) { - /* it can never happen that csize != 0 and membership == 0; we have + /* it can never happen that csize != NULL and membership == NULL; we have * handled that case above */ - for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t tmp = VECTOR(*membership)[i]; - if (tmp != 0) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + const igraph_integer_t c = VECTOR(*membership)[i]; + if (c != 0) { if (membership) { - VECTOR(*membership)[i] = tmp - 1; + VECTOR(*membership)[i] = c - 1; } } else { if (csize) { diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 383789a7f4..3639aca5a1 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.13-109-g03760a09c" +#define IGRAPH_VERSION "0.10.13-118-gbd9306d12" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 13 -#define IGRAPH_VERSION_PRERELEASE "109-g03760a09c" +#define IGRAPH_VERSION_PRERELEASE "118-gbd9306d12" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major,