From 04bf69b988d060086aa947a4d3035b3ac4da0f40 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Fri, 4 Sep 2026 13:13:59 +0200 Subject: [PATCH 1/2] perf: lazy ALTREP names for vertex/edge sequences A vertex/edge sequence's `names` attribute is now an instance of an `igraph_lazy_names` ALTREP string class instead of a materialized character vector. It holds a reference to the graph's full name vector plus a 1-based index into it, and only builds the actual names when an element is touched (printing, named indexing, `as_ids()`). Subsetting stays lazy too: `Extract_subset` composes the indices in O(1) rather than copying a slice of the names. `V()`/`E()` build their names through `lazy_index_names()`, and `Rx_igraph_vs_list()` attaches the ALTREP directly instead of eagerly subsetting the name vector. On top of the shared weak reference, create_vs_list() and the C construction loop, this is a smaller and more selective win than the earlier layers -- it only pays off where the name vectors themselves are large or never read: ego_order2_named 4.43ms -> 3.13ms (-29%) vs_subset_positional 0.111ms -> 0.095ms (-14%) max_cliques_named 2.67ms -> 2.67ms (unchanged) all_simple_paths_named 1.94ms -> 1.92ms (unchanged) DATAPTR_RO/DATAPTR_OR_NULL are used rather than DATAPTR, which is non-API as of R 4.5. Correctness verified: integer payloads with identical values and names, no names attribute on unnamed graphs, NA/out-of-range IDs map to NA_STRING, inputs unmutated, and the shared weakref still releases the graph after rm()+gc(). Full suite passes; clean under gctorture(TRUE). Co-Authored-By: Claude Opus 4.8 (1M context) Co-Authored-By: Claude Opus 5 (1M context) --- R/iterators.R | 54 ++++++++++----- src/cpp11.cpp | 2 + src/rinterface_extra.c | 153 ++++++++++++++++++++++++++++++++++++----- touchstone/script.R | 45 ++++++++++++ 4 files changed, 219 insertions(+), 35 deletions(-) diff --git a/R/iterators.R b/R/iterators.R index 3a6bb2ce466..b381098e178 100644 --- a/R/iterators.R +++ b/R/iterators.R @@ -124,6 +124,22 @@ identical_graphs <- function( .Call(Rx_igraph_identical_graphs, g1, g2, as.logical(attrs)) } +# Build the `names` attribute of a vertex/edge sequence lazily. +# +# `source` is the graph's full vertex/edge name vector (shared by reference +# across all sequences of the graph) and `idx` is a 1-based index into it. +# The result is an ALTREP string vector that only materializes the actual +# names when they are first accessed (printing, named indexing, `as_ids()`), +# so constructing a sequence stays cheap even when many of them are returned +# at once (e.g. `max_cliques()`). Subsetting it stays lazy too, via the +# class's `Extract_subset` method. Returns `NULL` when there is no source. +lazy_index_names <- function(source, idx) { + if (is.null(source)) { + return(NULL) + } + .Call(Rx_igraph_lazy_names, source, idx) +} + add_vses_graph_ref <- function(vses, graph) { ref <- get_vs_ref(graph) if (!is.null(ref)) { @@ -290,7 +306,7 @@ V <- function(graph) { res <- seq_len(vcount(graph)) if (is_named(graph)) { - names(res) <- vertex_attr(graph)$name + names(res) <- lazy_index_names(vertex_attr(graph)$name, res) } class(res) <- "igraph.vs" res <- set_complete_iterator(res) @@ -315,10 +331,11 @@ unsafe_create_vs <- function(graph, idx, verts = NULL) { } # `idx` are vertex IDs straight from C, and `verts` is the full `V(graph)`, # so `verts[idx]` would just be `idx` again -- skip that copy and use the - # IDs directly as the payload. Names are subset from `verts`, and the graph - # reference is shared from `verts`. All attributes are set in one - # `attributes<-` call to avoid the per-object shallow copies that dominate - # when many sequences are built (e.g. `max_cliques()`). + # IDs directly as the payload. Names are taken lazily from `verts` (an + # ALTREP that composes in O(1) under subsetting), and the graph reference is + # shared from `verts`. All attributes are set in one `attributes<-` call to + # avoid the per-object shallow copies that dominate when many sequences are + # built (e.g. `max_cliques()`). vertex_names <- attr(verts, "names") res <- as.integer(idx) attributes(res) <- list( @@ -334,15 +351,16 @@ unsafe_create_vs <- function(graph, idx, verts = NULL) { # # This is the batch form of `unsafe_create_vs()` and replaces the # `lapply(idx_list, unsafe_create_vs, graph = graph, verts = V(graph))` -# pattern. The whole per-element loop runs in C, so building many sequences -# costs no per-object R overhead (no closure call, no `as.integer()`, no -# `attributes<-`). This is what brings construction of many sequences -# (e.g. `max_cliques()`) down close to the cost of returning bare indices. +# pattern. The per-graph work -- `V(graph)`, the shared weak reference, the +# graph id and the lazy name source -- is hoisted out of the loop, so each +# sequence only costs one `as.integer()` and one `attributes<-`. This is what +# brings construction of many sequences (e.g. `max_cliques()`) down close to +# the cost of returning bare numeric indices. create_vs_list <- function(graph, idx_list) { - # `verts <- V(graph)` is what mints the single shared weak reference and - # graph id; build it once and hand the pieces to C, which runs the - # per-element construction loop (payload coercion, name subsetting, - # attribute setting). + # `verts <- V(graph)` is what mints the single shared weak reference and graph + # id; build it once and hand the pieces to C, which runs the per-element + # construction loop (payload coercion, lazy-names ALTREP, attribute setting) + # without any per-object R overhead. verts <- V(graph) .Call( Rx_igraph_vs_list, @@ -501,7 +519,7 @@ E <- function( } if ("name" %in% edge_attr_names(graph)) { - names(res) <- edge_attr(graph)$name[res] + names(res) <- lazy_index_names(edge_attr(graph)$name, res) } if (is_named(graph)) { el <- ends(graph, es = res) @@ -527,10 +545,10 @@ simple_vs_index <- function(x, i, na_ok = FALSE) { # Set every attribute in a single `attributes<-` call rather than one # `attr<-`/`class<-` at a time: each incremental assignment shallow-copies # the vector, and that copying dominates when many sequences are built - # (e.g. `max_cliques()`). `names` is carried over from the subset above; - # env/graph are carried from `x`, mirroring `simple_es_index()`, so - # sequences derived from one `V(graph)` share its weak reference instead of - # each minting a fresh one. + # (e.g. `max_cliques()`). `names` is carried over from the subset above (a + # lazy ALTREP, or NULL); env/graph are carried from `x`, mirroring + # `simple_es_index()`, so sequences derived from one `V(graph)` share its + # weak reference instead of each minting a fresh one. attributes(res) <- list( names = attr(res, "names"), class = "igraph.vs", diff --git a/src/cpp11.cpp b/src/cpp11.cpp index cd8a136215e..f7e3c51694b 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -564,6 +564,7 @@ extern SEXP Rx_igraph_layout_kamada_kawai_3d(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, extern SEXP Rx_igraph_layout_lgl(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP Rx_igraph_layout_merge_dla(SEXP, SEXP); extern SEXP Rx_igraph_layout_reingold_tilford(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP Rx_igraph_lazy_names(SEXP, SEXP); extern SEXP Rx_igraph_make_weak_ref(SEXP, SEXP, SEXP); extern SEXP Rx_igraph_maximal_cliques(SEXP, SEXP, SEXP, SEXP); extern SEXP Rx_igraph_maximal_cliques_count(SEXP, SEXP, SEXP, SEXP); @@ -1145,6 +1146,7 @@ static const R_CallMethodDef CallEntries[] = { {"Rx_igraph_layout_lgl", (DL_FUNC) &Rx_igraph_layout_lgl, 8}, {"Rx_igraph_layout_merge_dla", (DL_FUNC) &Rx_igraph_layout_merge_dla, 2}, {"Rx_igraph_layout_reingold_tilford", (DL_FUNC) &Rx_igraph_layout_reingold_tilford, 5}, + {"Rx_igraph_lazy_names", (DL_FUNC) &Rx_igraph_lazy_names, 2}, {"Rx_igraph_make_weak_ref", (DL_FUNC) &Rx_igraph_make_weak_ref, 3}, {"Rx_igraph_maximal_cliques", (DL_FUNC) &Rx_igraph_maximal_cliques, 4}, {"Rx_igraph_maximal_cliques_count", (DL_FUNC) &Rx_igraph_maximal_cliques_count, 4}, diff --git a/src/rinterface_extra.c b/src/rinterface_extra.c index ec98a16b266..f7a94e98ae8 100644 --- a/src/rinterface_extra.c +++ b/src/rinterface_extra.c @@ -2611,14 +2611,134 @@ static void *Rx_igraph_altrep_to(SEXP vec, Rboolean writeable) { static R_altrep_class_t Rx_igraph_altrep_from_class; static R_altrep_class_t Rx_igraph_altrep_to_class; +/* ------------------------------------------------------------------------ + * Lazy names for vertex/edge sequences. + * + * A vertex/edge sequence carries its `names` attribute as an instance of this + * ALTREP string class instead of a materialized character vector. The actual + * names are only built when an element is touched (printing, named indexing, + * as_ids()), not when the sequence is constructed -- which is the common case + * for functions that return tens of thousands of sequences (e.g. max_cliques). + * + * data1 = list(source, idx): `source` is the graph's full vertex/edge name + * vector (shared by reference across all sequences of a graph) and `idx` is a + * 1-based integer index into `source`. data2 caches the materialized STRSXP. + * ------------------------------------------------------------------------ */ +static R_altrep_class_t Rx_igraph_lazy_names_class; + +static R_xlen_t Rx_igraph_lazy_names_length(SEXP vec) { + return XLENGTH(VECTOR_ELT(R_altrep_data1(vec), 1)); +} + +static SEXP Rx_igraph_lazy_names_materialize(SEXP vec) { + SEXP data=R_altrep_data2(vec); + if (data != R_NilValue) { + return data; + } + + SEXP d1=R_altrep_data1(vec); + SEXP source=VECTOR_ELT(d1, 0); + SEXP idx=VECTOR_ELT(d1, 1); + R_xlen_t n=XLENGTH(idx); + R_xlen_t nsource=XLENGTH(source); + const int *pidx=INTEGER(idx); + + PROTECT(data=Rf_allocVector(STRSXP, n)); + for (R_xlen_t i=0; i < n; i++) { + int j=pidx[i]; + if (j == NA_INTEGER || j < 1 || j > nsource) { + SET_STRING_ELT(data, i, NA_STRING); + } else { + SET_STRING_ELT(data, i, STRING_ELT(source, j - 1)); + } + } + R_set_altrep_data2(vec, data); + UNPROTECT(1); + return data; +} + +/* DATAPTR_RO / DATAPTR_OR_NULL are used here rather than DATAPTR: the latter + * is non-API as of R 4.5. The materialized cache is a standard read-only name + * vector, so a read-only data pointer is all callers (as.vector(), coercion) + * need. */ +static void *Rx_igraph_lazy_names_dataptr(SEXP vec, Rboolean writeable) { + return (void *) DATAPTR_RO(Rx_igraph_lazy_names_materialize(vec)); +} + +static const void *Rx_igraph_lazy_names_dataptr_or_null(SEXP vec) { + SEXP data=R_altrep_data2(vec); + if (data == R_NilValue) { + return NULL; + } + return DATAPTR_OR_NULL(data); +} + +static SEXP Rx_igraph_lazy_names_elt(SEXP vec, R_xlen_t i) { + return STRING_ELT(Rx_igraph_lazy_names_materialize(vec), i); +} + +/* Subsetting stays lazy: return a fresh lazy-names vector with composed + * indices instead of materializing. Falls back to the default (materialize) + * for index types we do not handle here by returning NULL. */ +static SEXP Rx_igraph_lazy_names_extract_subset(SEXP vec, SEXP indx, SEXP call) { + if (TYPEOF(indx) != INTSXP && TYPEOF(indx) != REALSXP) { + return NULL; + } + + SEXP d1=R_altrep_data1(vec); + SEXP source=VECTOR_ELT(d1, 0); + SEXP idx=VECTOR_ELT(d1, 1); + R_xlen_t leni=XLENGTH(idx); + const int *pidx=INTEGER(idx); + + SEXP indx_int=PROTECT(Rf_coerceVector(indx, INTSXP)); + R_xlen_t n=XLENGTH(indx_int); + const int *pind=INTEGER(indx_int); + + SEXP new_idx=PROTECT(Rf_allocVector(INTSXP, n)); + int *pnew=INTEGER(new_idx); + for (R_xlen_t k=0; k < n; k++) { + int p=pind[k]; + if (p == NA_INTEGER || p < 1 || p > leni) { + pnew[k]=NA_INTEGER; + } else { + pnew[k]=pidx[p - 1]; + } + } + + SEXP d1n=PROTECT(Rf_allocVector(VECSXP, 2)); + SET_VECTOR_ELT(d1n, 0, source); + SET_VECTOR_ELT(d1n, 1, new_idx); + SEXP res=R_new_altrep(Rx_igraph_lazy_names_class, d1n, R_NilValue); + UNPROTECT(3); + return res; +} + +/* Construct a lazy-names vector from a character `source` and a (1-based) + * integer `idx`. Returns R_NilValue when `source` is not usable, so callers + * can fall back to no names. */ +SEXP Rx_igraph_lazy_names(SEXP source, SEXP idx) { + if (TYPEOF(source) != STRSXP) { + return R_NilValue; + } + + SEXP idx_int=PROTECT(Rf_coerceVector(idx, INTSXP)); + SEXP d1=PROTECT(Rf_allocVector(VECSXP, 2)); + SET_VECTOR_ELT(d1, 0, source); + SET_VECTOR_ELT(d1, 1, idx_int); + SEXP res=R_new_altrep(Rx_igraph_lazy_names_class, d1, R_NilValue); + UNPROTECT(2); + return res; +} + /* Batch constructor for a list of vertex sequences. * * Builds the whole `lapply(idx_list, unsafe_create_vs, ...)` result in one C * pass: for each vertex-ID vector it produces a fresh integer payload, attaches - * the corresponding vertex names (when the graph is named), and sets the shared - * `env` weak reference, the `graph` id and the `igraph.vs` class. This keeps the - * per-object R overhead (closure call, `as.integer`, name subset, - * `attributes<-`) out of the loop entirely. + * a lazy-names ALTREP (when the graph is named), and sets the shared `env` + * weak reference, the `graph` id and the `igraph.vs` class. This keeps the + * per-object R overhead (closure call, `as.integer`, `.Call`, `attributes<-`) + * out of the loop entirely. * * idx_list : VECSXP of vertex-ID vectors (integer or double) * names_src : graph's full vertex-name STRSXP, or NULL for unnamed graphs @@ -2628,7 +2748,6 @@ static R_altrep_class_t Rx_igraph_altrep_to_class; SEXP Rx_igraph_vs_list(SEXP idx_list, SEXP names_src, SEXP env, SEXP graph_id) { R_xlen_t n=XLENGTH(idx_list); int named=(TYPEOF(names_src) == STRSXP); - R_xlen_t nsource=named ? XLENGTH(names_src) : 0; SEXP env_sym=Rf_install("env"); SEXP graph_sym=Rf_install("graph"); SEXP out=PROTECT(Rf_allocVector(VECSXP, n)); @@ -2646,19 +2765,12 @@ SEXP Rx_igraph_vs_list(SEXP idx_list, SEXP names_src, SEXP env, SEXP graph_id) { } if (named) { - R_xlen_t len=XLENGTH(payload); - const int *pidx=INTEGER(payload); - SEXP nm=PROTECT(Rf_allocVector(STRSXP, len)); - for (R_xlen_t k=0; k < len; k++) { - int j=pidx[k]; - if (j == NA_INTEGER || j < 1 || j > nsource) { - SET_STRING_ELT(nm, k, NA_STRING); - } else { - SET_STRING_ELT(nm, k, STRING_ELT(names_src, j - 1)); - } - } + SEXP d1=PROTECT(Rf_allocVector(VECSXP, 2)); + SET_VECTOR_ELT(d1, 0, names_src); + SET_VECTOR_ELT(d1, 1, payload); + SEXP nm=PROTECT(R_new_altrep(Rx_igraph_lazy_names_class, d1, R_NilValue)); Rf_setAttrib(payload, R_NamesSymbol, nm); - UNPROTECT(1); + UNPROTECT(2); } Rf_setAttrib(payload, env_sym, env); @@ -2685,6 +2797,13 @@ void Rx_igraph_init_vector_class(DllInfo *dll) { R_set_altrep_Length_method(Rx_igraph_altrep_to_class, Rx_igraph_altrep_length); R_set_altvec_Dataptr_method(Rx_igraph_altrep_to_class, Rx_igraph_altrep_to); + + Rx_igraph_lazy_names_class=R_make_altstring_class("igraph_lazy_names", "igraph", dll); + R_set_altrep_Length_method(Rx_igraph_lazy_names_class, Rx_igraph_lazy_names_length); + R_set_altvec_Dataptr_method(Rx_igraph_lazy_names_class, Rx_igraph_lazy_names_dataptr); + R_set_altvec_Dataptr_or_null_method(Rx_igraph_lazy_names_class, Rx_igraph_lazy_names_dataptr_or_null); + R_set_altvec_Extract_subset_method(Rx_igraph_lazy_names_class, Rx_igraph_lazy_names_extract_subset); + R_set_altstring_Elt_method(Rx_igraph_lazy_names_class, Rx_igraph_lazy_names_elt); } /* HELPER: internal C; must use IGRAPH_CHECK */ diff --git a/touchstone/script.R b/touchstone/script.R index 481a623b77d..29fa56e67eb 100644 --- a/touchstone/script.R +++ b/touchstone/script.R @@ -376,5 +376,50 @@ benchmark_run( n = 20 ) +# --------------------------------------------------------------------------- +# Group #7 - what lazy names specifically buy +# Two cases where the name vector is never actually read, so with an ALTREP +# `names` attribute it is never built: +# * construct many sequences but only ask for their lengths; +# * subset a large named vertex sequence positionally (Extract_subset +# composes indices in O(1) instead of copying a subset of the names). +# --------------------------------------------------------------------------- + +benchmark_run( + expr_before_benchmark = { + library(igraph) + set.seed(42) + g <- sample_gnp(200L, 0.16, directed = FALSE) + V(g)$name <- paste0("v", seq_len(gorder(g))) + for (i in 1:2) { + lengths(max_cliques(g)) + } + gc(full = TRUE) + }, + max_cliques_sizes_named = for (i in 1:30) { + lengths(max_cliques(g)) + }, + n = 20 +) + +benchmark_run( + expr_before_benchmark = { + library(igraph) + set.seed(42) + g <- sample_gnm(50000L, 100000L) + V(g)$name <- paste0("v", seq_len(50000L)) + v <- V(g) + pick <- sample(50000L, 10000L) + for (i in 1:5) { + v[pick] + } + gc(full = TRUE) + }, + vs_subset_positional = for (i in 1:880) { + v[pick] + }, + n = 20 +) + # Create the artifacts consumed by the GitHub Action. benchmark_analyze() From 4f5323bc24598210491bb97fc9b241742037fec5 Mon Sep 17 00:00:00 2001 From: schochastics Date: Fri, 4 Sep 2026 11:26:28 +0000 Subject: [PATCH 2/2] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/33866978007 --- man/biconnected.components.Rd | 2 +- man/biconnected_components.Rd | 2 +- man/graphlet_basis.Rd | 2 +- man/graphlets.candidate.basis.Rd | 2 +- man/k_shortest_paths.Rd | 2 +- man/simple_cycles.Rd | 2 +- man/stCuts.Rd | 2 +- man/stMincuts.Rd | 2 +- man/st_cuts.Rd | 2 +- man/st_min_cuts.Rd | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/man/biconnected.components.Rd b/man/biconnected.components.Rd index 43d62a46e92..56f6906c45b 100644 --- a/man/biconnected.components.Rd +++ b/man/biconnected.components.Rd @@ -17,7 +17,7 @@ it is directed.} consistent API. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_biconnected_components}{\code{biconnected_components()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_biconnected_components}{\code{biconnected_components()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \keyword{internal} diff --git a/man/biconnected_components.Rd b/man/biconnected_components.Rd index edab1796bfc..3ee2ebdfc8d 100644 --- a/man/biconnected_components.Rd +++ b/man/biconnected_components.Rd @@ -46,7 +46,7 @@ that this is not true for vertices: the same vertex can be part of many biconnected components. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_biconnected_components}{\code{biconnected_components()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_biconnected_components}{\code{biconnected_components()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \examples{ diff --git a/man/graphlet_basis.Rd b/man/graphlet_basis.Rd index 88ce6849e76..623da40eb67 100644 --- a/man/graphlet_basis.Rd +++ b/man/graphlet_basis.Rd @@ -73,7 +73,7 @@ the algorithm, and they are useful if the user wishes to perform them individually: \code{graphlet_basis()} and \code{graphlet_proj()}. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_candidate_basis}{\code{graphlets_candidate_basis()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_project}{\code{graphlets_project()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets}{\code{graphlets()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_candidate_basis}{\code{graphlets_candidate_basis()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_project}{\code{graphlets_project()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets}{\code{graphlets()}} } \examples{ diff --git a/man/graphlets.candidate.basis.Rd b/man/graphlets.candidate.basis.Rd index b72de6dc592..7c2f37be8d0 100644 --- a/man/graphlets.candidate.basis.Rd +++ b/man/graphlets.candidate.basis.Rd @@ -21,7 +21,7 @@ attribute is used.} consistent API. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_candidate_basis}{\code{graphlets_candidate_basis()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Graphlets.html#igraph_graphlets_candidate_basis}{\code{graphlets_candidate_basis()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \keyword{internal} diff --git a/man/k_shortest_paths.Rd b/man/k_shortest_paths.Rd index 1dfe09896ef..dbeaaa71f66 100644 --- a/man/k_shortest_paths.Rd +++ b/man/k_shortest_paths.Rd @@ -56,7 +56,7 @@ vertex in order of increasing length. Currently this function uses Yen's algorithm. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_get_k_shortest_paths}{\code{get_k_shortest_paths()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Structural.html#igraph_get_k_shortest_paths}{\code{get_k_shortest_paths()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \references{ diff --git a/man/simple_cycles.Rd b/man/simple_cycles.Rd index 2d769957c35..fe6deab4f1a 100644 --- a/man/simple_cycles.Rd +++ b/man/simple_cycles.Rd @@ -59,7 +59,7 @@ have exponentially many cycles and the presence of multi-edges exacerbates this combinatorial explosion. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Cycles.html#igraph_simple_cycles}{\code{simple_cycles()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Cycles.html#igraph_simple_cycles}{\code{simple_cycles()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \examples{ diff --git a/man/stCuts.Rd b/man/stCuts.Rd index 437d55c739f..8460ca93d26 100644 --- a/man/stCuts.Rd +++ b/man/stCuts.Rd @@ -20,7 +20,7 @@ stCuts(graph, source, target) consistent API. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_cuts}{\code{all_st_cuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_cuts}{\code{all_st_cuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \keyword{internal} diff --git a/man/stMincuts.Rd b/man/stMincuts.Rd index 855722247d1..993071048de 100644 --- a/man/stMincuts.Rd +++ b/man/stMincuts.Rd @@ -26,7 +26,7 @@ here.} consistent API. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_mincuts}{\code{all_st_mincuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_mincuts}{\code{all_st_mincuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \keyword{internal} diff --git a/man/st_cuts.Rd b/man/st_cuts.Rd index 1a19298155f..b4f5ac16afd 100644 --- a/man/st_cuts.Rd +++ b/man/st_cuts.Rd @@ -38,7 +38,7 @@ removing these edges from \eqn{G} there is no directed path from \eqn{s} to \eqn{t}. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_cuts}{\code{all_st_cuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_cuts}{\code{all_st_cuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \examples{ diff --git a/man/st_min_cuts.Rd b/man/st_min_cuts.Rd index 9f0bbba1dcd..c01292ec993 100644 --- a/man/st_min_cuts.Rd +++ b/man/st_min_cuts.Rd @@ -56,7 +56,7 @@ simply the number of edges. An \eqn{(s,t)}-cut is minimum if it is of the smallest possible size. } \section{Related documentation in the C library}{ -\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_mincuts}{\code{all_st_mincuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} +\href{https://igraph.org/c/html/0.10.17/igraph-Flows.html#igraph_all_st_mincuts}{\code{all_st_mincuts()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_edges}{\code{edges()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_get_eids}{\code{get_eids()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_ecount}{\code{ecount()}} } \examples{