diff --git a/src/vendor/cigraph/CHANGELOG.md b/src/vendor/cigraph/CHANGELOG.md index 6e4908a864..28c54ba486 100644 --- a/src/vendor/cigraph/CHANGELOG.md +++ b/src/vendor/cigraph/CHANGELOG.md @@ -18,6 +18,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. + - When `igraph_eigenvector_centrality()` receives a directed acyclic graph as input, it now produces an eigenvector which has 1s in sink vertices and 0s everywhere else. Previously, it would return an all-zero vector. Note that eigenvector centrality is not uniquely defined for graphs that are not (strongly) connected, and both of these results can be considered valid. This change is to ensure consistency with the definition of the theoretical maximum of eigenvector centralization, which assumes the in-star to be the most centralized directed network. ### Fixed diff --git a/src/vendor/cigraph/src/centrality/eigenvector.c b/src/vendor/cigraph/src/centrality/eigenvector.c index 5c9985ddc0..9dc60b0c69 100644 --- a/src/vendor/cigraph/src/centrality/eigenvector.c +++ b/src/vendor/cigraph/src/centrality/eigenvector.c @@ -278,22 +278,6 @@ static igraph_error_t igraph_i_eigenvector_centrality_directed(const igraph_t *g return IGRAPH_SUCCESS; } - /* Quick check: if the graph is a DAG, all the eigenvector centralities are - * zeros, and so is the eigenvalue */ - IGRAPH_CHECK(igraph_is_dag(graph, &dag)); - if (dag) { - /* special case: graph is a DAG */ - IGRAPH_WARNING("Graph is directed and acyclic; eigenvector centralities will be zeros."); - if (value) { - *value = 0; - } - if (vector) { - IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); - igraph_vector_fill(vector, 0); - } - return IGRAPH_SUCCESS; - } - if (weights) { igraph_real_t min, max; @@ -325,6 +309,49 @@ static igraph_error_t igraph_i_eigenvector_centrality_directed(const igraph_t *g } } + /* If the graph is a DAG, the eigenvalue is zero, and degenerate. Vectors that + * are zero in all vertices that have some out-edges, as well as their linear + * combinations, are all valid eigenvectors. + * + * ARPACK may converge to one of these, or it may converge to an all-zero + * vector, depending on chance. To eliminate this uncertainty, we return + * an eigenvector that has 1s in sinks (vertices with no out-edges) and 0s + * everywhere else. This ensures that the in-star, which is treated as the + * "most centralized network" for eigenvector centralization, has a non-zero + * centrality score in its centre. See https://github.com/igraph/igraph/issues/2679 + * for a discussion of the topic. + * + * For simplicity, we identify sinks by checking for zero out-strength. This + * also identifies effective sinks where out-edges exist, but they all have zero + * weight. This test is valid only when there are no negative weights, otherwise + * non-zero weights may still add up to a zero strength. Since negative weights + * are problematic anyway, we leave that case entirely up to ARPACK, without + * special treatment. + */ + if (! negative_weights) { + IGRAPH_CHECK(igraph_is_dag(graph, &dag)); + if (dag) { + /* special case: graph is a DAG */ + IGRAPH_WARNING("Graph is directed and acyclic; " + "returning eigenvector centralities of 1 in sink vertices, " + "and 0 everywhere else."); + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(igraph_strength(graph, vector, igraph_vss_all(), IGRAPH_OUT, true, weights)); + for (i=0; i < no_of_nodes; i++) { + if (VECTOR(*vector)[i] == 0) { + VECTOR(*vector)[i] = 1; + } else { + VECTOR(*vector)[i] = 0; + } + } + } + return IGRAPH_SUCCESS; + } + } + if (no_of_nodes > INT_MAX) { IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); } diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 396ef87e91..2ca3a2b2ae 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-123-ga9ca8a74e" +#define IGRAPH_VERSION "0.10.13-124-gf8d4760f4" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 13 -#define IGRAPH_VERSION_PRERELEASE "123-ga9ca8a74e" +#define IGRAPH_VERSION_PRERELEASE "124-gf8d4760f4" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major,