From c75e174e99e6a3d7d82ceee8a36e1c769c5af5f0 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 28 May 2026 13:36:18 +0200 Subject: [PATCH 01/16] Add attribute combination support to graph operators (union, intersection, compose, disjoint_union) --- R/attributes.R | 57 ++++--- R/operators.R | 262 ++++++++++++++++++++++++++------ tests/testthat/test-operators.R | 109 +++++++++++++ 3 files changed, 358 insertions(+), 70 deletions(-) diff --git a/R/attributes.R b/R/attributes.R index 45fb741e0c7..dbc4e0aceba 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1290,7 +1290,7 @@ is_bipartite <- function(graph) { ############# -igraph.i.attribute.combination <- function(comb) { +igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { if (is.function(comb)) { comb <- list(comb) } @@ -1310,34 +1310,40 @@ igraph.i.attribute.combination <- function(comb) { if (anyDuplicated(names(comb)) > 0) { cli::cli_warn("Some attributes are duplicated") } + known_names <- c( + "ignore", + "sum", + "prod", + "min", + "max", + "random", + "first", + "last", + "mean", + "median", + "concat" + ) + known_codes <- c(0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + if (allow_rename) { + known_names <- c(known_names, "rename") + known_codes <- c(known_codes, NA_integer_) + } comb <- lapply(comb, function(x) { if (!is.character(x)) { x } else { - known <- data.frame( - n = c( - "ignore", - "sum", - "prod", - "min", - "max", - "random", - "first", - "last", - "mean", - "median", - "concat" - ), - i = c(0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), - stringsAsFactors = FALSE - ) - x <- pmatch(tolower(x), known[, 1]) - if (is.na(x)) { + idx <- pmatch(tolower(x), known_names) + if (is.na(idx)) { + if (!allow_rename && identical(tolower(x), "rename")) { + cli::cli_abort( + "{.val rename} is only supported by graph operators ({.fn union}, {.fn intersection}, {.fn compose}, {.fn disjoint_union}), not by this function." + ) + } cli::cli_abort( "Unknown/unambigous attribute combination specification." ) } - known[, 2][x] + if (is.na(known_codes[idx])) "rename" else known_codes[idx] } }) @@ -1435,6 +1441,15 @@ igraph.i.attribute.combination <- function(comb) { #' Concatenate the attributes, using the [c()] function. #' This results almost always a complex attribute. #' } +#' \item{"rename"}{ +#' Keep clashing attributes side-by-side under disambiguated names by +#' appending `_1`, `_2`, ... suffixes. This is the default for the +#' graph operators [union()], [intersection()], [compose()] and +#' [disjoint_union()] and preserves their historical behaviour. +#' Only those operators accept `"rename"`; [simplify()] and +#' [contract()] will reject it because the rename strategy has no +#' per-element interpretation when many input values collapse into one. +#' } #' } #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @seealso [graph_attr()], [vertex_attr()], diff --git a/R/operators.R b/R/operators.R index b5d603c58f1..f3d2478d14c 100644 --- a/R/operators.R +++ b/R/operators.R @@ -118,13 +118,14 @@ graph.complementer <- function(graph, loops = FALSE) { # ################################################################### -rename.attr.if.needed <- function( +combine.attrs <- function( type = c("g", "v", "e"), graphs, newsize = NULL, maps = NULL, maps2 = NULL, - ignore = character() + ignore = character(), + comb = list("rename") ) { type <- igraph_match_arg(type) @@ -146,6 +147,9 @@ rename.attr.if.needed <- function( getval <- function(which, name) { newval <- getfun(graphs[[which]], name) + if (type == "g") { + return(newval) + } if (!is.null(maps)) { tmpval <- newval[maps[[which]] >= 0] mm <- maps[[which]][maps[[which]] >= 0] + 1 @@ -161,22 +165,85 @@ rename.attr.if.needed <- function( newval } + default_idx <- which(names(comb) == "" | is.na(names(comb))) + default_comb <- if (length(default_idx) > 0) { + comb[[default_idx[1]]] + } else { + "rename" + } + resolve_comb <- function(name) { + if (nzchar(name) && name %in% names(comb)) { + comb[[name]] + } else { + default_comb + } + } + attr <- list() for (name in an) { w <- which(sapply(alist, function(x) name %in% x)) - if (length(w) == 1) { - attr[[name]] <- getval(w, name) - } else { - for (w2 in w) { - nname <- paste(name, sep = "_", w2) - newval <- getval(w2, name) - attr[[nname]] <- newval + this_comb <- resolve_comb(name) + + if (identical(this_comb, "rename")) { + if (length(w) == 1) { + attr[[name]] <- getval(w, name) + } else { + for (w2 in w) { + nname <- paste(name, sep = "_", w2) + attr[[nname]] <- getval(w2, name) + } } + } else if (identical(this_comb, 0) || identical(this_comb, 0L)) { + # ignore: drop the attribute + } else { + vals <- lapply(w, function(w2) getval(w2, name)) + attr[[name]] <- apply_attr_combiner(this_comb, vals, type) } } attr } +apply_attr_combiner <- function(comb, vals, type) { + if (type == "g") { + x <- unlist(vals, recursive = FALSE) + return(apply_one_combiner(comb, x)) + } + m <- do.call(cbind, vals) + out <- lapply(seq_len(nrow(m)), function(i) { + x <- m[i, ] + x <- x[!is.na(x)] + apply_one_combiner(comb, x) + }) + if (all(vapply(out, length, integer(1)) == 1L)) { + unlist(out) + } else { + out + } +} + +apply_one_combiner <- function(comb, x) { + if (is.function(comb)) { + return(comb(x)) + } + if (length(x) == 0) { + return(NA) + } + switch( + as.character(comb), + "3" = sum(x), + "4" = prod(x), + "5" = min(x), + "6" = max(x), + "7" = sample(x, 1), + "8" = x[[1]], + "9" = x[[length(x)]], + "10" = mean(x), + "11" = stats::median(x), + "12" = if (length(x) == 1) x[[1]] else x, + cli::cli_abort("Unknown attribute combiner code: {.val {comb}}") + ) +} + #' Disjoint union of graphs #' @@ -192,9 +259,10 @@ rename.attr.if.needed <- function( #' particular, it merges vertex and edge attributes using the [vctrs::vec_c()] #' function. For graphs that lack some vertex/edge attribute, the corresponding #' values in the new graph are set to a missing value (`NA` for scalar attributes, -#' `NULL` for list attributes). Graph attributes are simply -#' copied to the result. If this would result a name clash, then they are -#' renamed by adding suffixes: _1, _2, etc. +#' `NULL` for list attributes). Graph attributes are combined according to +#' `graph.attr.comb`; by default any name clash is resolved by adding +#' suffixes (`_1`, `_2`, ...). See [igraph-attribute-combination] for the +#' available combiners. #' #' Note that if both graphs have vertex names (i.e. a `name` vertex #' attribute), then the concatenated vertex names might be non-unique in the @@ -206,6 +274,10 @@ rename.attr.if.needed <- function( #' @aliases %du% #' @param \dots Graph objects or lists of graph objects. #' @param x,y Graph objects. +#' @param graph.attr.comb Specification for combining shared graph attributes. +#' Defaults to `"rename"`, which preserves the historical behaviour of +#' appending `_1`, `_2`, ... suffixes to clashing attribute names. See +#' [igraph-attribute-combination] for the available combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @export @@ -219,7 +291,7 @@ rename.attr.if.needed <- function( #' V(g2)$name <- letters[11:20] #' print_all(g1 %du% g2) #' @export -disjoint_union <- function(...) { +disjoint_union <- function(..., graph.attr.comb = "rename") { graphs <- unlist( recursive = FALSE, lapply(list(...), function(l) { @@ -232,7 +304,11 @@ disjoint_union <- function(...) { res <- .Call(Rx_igraph_disjoint_union, graphs) ## Graph attributes - graph.attributes(res) <- rename.attr.if.needed("g", graphs) + graph.attr.comb <- igraph.i.attribute.combination( + graph.attr.comb, + allow_rename = TRUE + ) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph.attr.comb) ## Vertex attributes attr <- list() @@ -306,7 +382,10 @@ disjoint_union <- function(...) { call, ..., byname, - keep.all.vertices + keep.all.vertices, + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" ) { graphs <- unlist( recursive = FALSE, @@ -330,6 +409,19 @@ disjoint_union <- function(...) { cli::cli_abort("Some graphs are not named.") } + graph.attr.comb <- igraph.i.attribute.combination( + graph.attr.comb, + allow_rename = TRUE + ) + vertex.attr.comb <- igraph.i.attribute.combination( + vertex.attr.comb, + allow_rename = TRUE + ) + edge.attr.comb <- igraph.i.attribute.combination( + edge.attr.comb, + allow_rename = TRUE + ) + edgemaps <- length(unlist(lapply(graphs, edge_attr_names))) != 0 if (byname) { @@ -357,23 +449,28 @@ disjoint_union <- function(...) { maps <- res$edgemaps res <- res$graph - ## We might need to rename all attributes - graph.attributes(res) <- rename.attr.if.needed("g", newgraphs) - vertex.attributes(res) <- rename.attr.if.needed( + graph.attributes(res) <- combine.attrs( + "g", + newgraphs, + comb = graph.attr.comb + ) + vertex.attributes(res) <- combine.attrs( "v", newgraphs, vcount(res), - ignore = "name" + ignore = "name", + comb = vertex.attr.comb ) V(res)$name <- uninames ## Edges are a bit more difficult, we need a mapping if (edgemaps) { - edge.attributes(res) <- rename.attr.if.needed( + edge.attributes(res) <- combine.attrs( "e", newgraphs, ecount(res), - maps = maps + maps = maps, + comb = edge.attr.comb ) } } else { @@ -397,21 +494,26 @@ disjoint_union <- function(...) { maps <- res$edgemaps res <- res$graph - ## We might need to rename all attributes - graph.attributes(res) <- rename.attr.if.needed("g", graphs) - vertex.attributes(res) <- rename.attr.if.needed( + graph.attributes(res) <- combine.attrs( + "g", + graphs, + comb = graph.attr.comb + ) + vertex.attributes(res) <- combine.attrs( "v", graphs, - vcount(res) + vcount(res), + comb = vertex.attr.comb ) ## Edges are a bit more difficult, we need a mapping if (edgemaps) { - edge.attributes(res) <- rename.attr.if.needed( + edge.attributes(res) <- combine.attrs( "e", graphs, ecount(res), - maps = maps + maps = maps, + comb = edge.attr.comb ) } } @@ -459,9 +561,12 @@ union.default <- function(...) { #' of the internal numeric vertex ids. #' #' `union()` keeps the attributes of all graphs. All graph, vertex and -#' edge attributes are copied to the result. If an attribute is present in -#' multiple graphs and would result a name clash, then this attribute is -#' renamed by adding suffixes: _1, _2, etc. +#' edge attributes are copied to the result. By default, if an attribute is +#' present in multiple graphs and would result in a name clash, that attribute +#' is renamed by adding suffixes: `_1`, `_2`, etc. Pass `graph.attr.comb`, +#' `vertex.attr.comb` or `edge.attr.comb` to combine clashing attributes +#' instead, e.g. by summing or by taking the first non-`NA` value. See +#' [igraph-attribute-combination] for the available combiners. #' #' The `name` vertex attribute is treated specially if the operation is #' performed based on symbolic vertex names. In this case `name` must be @@ -477,6 +582,11 @@ union.default <- function(...) { #' `auto`, that means `TRUE` if all graphs are named and `FALSE` #' otherwise. A warning is generated if `auto` and some (but not all) #' graphs are named. +#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' combining clashing graph, vertex and edge attributes. Each defaults to +#' `"rename"`, which preserves the historical behaviour of appending +#' `_1`, `_2`, ... suffixes. See [igraph-attribute-combination] for the +#' available combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @method union igraph @@ -492,12 +602,21 @@ union.default <- function(...) { #' ) #' net2 <- graph_from_literal(D - A:F:Y, B - A - X - F - H - Z, F - Y) #' print_all(net1 %u% net2) -union.igraph <- function(..., byname = "auto") { +union.igraph <- function( + ..., + byname = "auto", + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" +) { .igraph.graph.union.or.intersection( "union", ..., byname = byname, - keep.all.vertices = TRUE + keep.all.vertices = TRUE, + graph.attr.comb = graph.attr.comb, + vertex.attr.comb = vertex.attr.comb, + edge.attr.comb = edge.attr.comb ) } @@ -540,9 +659,12 @@ intersection <- function(...) { #' of the internal numeric vertex ids. #' #' `intersection()` keeps the attributes of all graphs. All graph, -#' vertex and edge attributes are copied to the result. If an attribute is -#' present in multiple graphs and would result a name clash, then this -#' attribute is renamed by adding suffixes: _1, _2, etc. +#' vertex and edge attributes are copied to the result. By default, if an +#' attribute is present in multiple graphs and would result in a name clash, +#' that attribute is renamed by adding suffixes: `_1`, `_2`, etc. Pass +#' `graph.attr.comb`, `vertex.attr.comb` or `edge.attr.comb` to combine +#' clashing attributes instead; see [igraph-attribute-combination] for the +#' available combiners. #' #' The `name` vertex attribute is treated specially if the operation is #' performed based on symbolic vertex names. In this case `name` must be @@ -560,6 +682,10 @@ intersection <- function(...) { #' graphs are named. #' @param keep.all.vertices Logical scalar, whether to keep vertices that only #' appear in a subset of the input graphs. +#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' combining clashing graph, vertex and edge attributes. Each defaults to +#' `"rename"`. See [igraph-attribute-combination] for the available +#' combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @method intersection igraph @@ -578,13 +704,19 @@ intersection <- function(...) { intersection.igraph <- function( ..., byname = "auto", - keep.all.vertices = TRUE + keep.all.vertices = TRUE, + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" ) { .igraph.graph.union.or.intersection( "intersection", ..., byname = byname, - keep.all.vertices = keep.all.vertices + keep.all.vertices = keep.all.vertices, + graph.attr.comb = graph.attr.comb, + vertex.attr.comb = vertex.attr.comb, + edge.attr.comb = edge.attr.comb ) } @@ -765,9 +897,11 @@ complementer <- function(graph, loops = FALSE) { #' names. Otherwise numeric vertex ids are used. #' #' `compose()` keeps the attributes of both graphs. All graph, vertex -#' and edge attributes are copied to the result. If an attribute is present in -#' multiple graphs and would result a name clash, then this attribute is -#' renamed by adding suffixes: _1, _2, etc. +#' and edge attributes are copied to the result. By default, if an attribute +#' is present in both graphs and would result in a name clash, that attribute +#' is renamed by adding suffixes: `_1`, `_2`. Pass `graph.attr.comb`, +#' `vertex.attr.comb` or `edge.attr.comb` to combine clashing attributes +#' instead; see [igraph-attribute-combination] for the available combiners. #' #' The `name` vertex attribute is treated specially if the operation is #' performed based on symbolic vertex names. In this case `name` must be @@ -796,6 +930,10 @@ complementer <- function(graph, loops = FALSE) { #' `auto`, that means `TRUE` if both graphs are named and #' `FALSE` otherwise. A warning is generated if `auto` and one graph, #' but not both graphs are named. +#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' combining clashing graph, vertex and edge attributes. Each defaults to +#' `"rename"`. See [igraph-attribute-combination] for the available +#' combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @family functions for manipulating graph structure @@ -809,7 +947,14 @@ complementer <- function(graph, loops = FALSE) { #' print_all(gc) #' print_all(simplify(gc)) #' -compose <- function(g1, g2, byname = "auto") { +compose <- function( + g1, + g2, + byname = "auto", + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" +) { ensure_igraph(g1) ensure_igraph(g2) @@ -828,6 +973,19 @@ compose <- function(g1, g2, byname = "auto") { cli::cli_abort("Some graphs are not named.") } + graph.attr.comb <- igraph.i.attribute.combination( + graph.attr.comb, + allow_rename = TRUE + ) + vertex.attr.comb <- igraph.i.attribute.combination( + vertex.attr.comb, + allow_rename = TRUE + ) + edge.attr.comb <- igraph.i.attribute.combination( + edge.attr.comb, + allow_rename = TRUE + ) + if (byname) { uninames <- unique(c(V(g1)$name, V(g2)$name)) if (vcount(g1) < length(uninames)) { @@ -852,28 +1010,34 @@ compose <- function(g1, g2, byname = "auto") { maps <- list(res$edge_map1, res$edge_map2) res <- res$graph - ## We might need to rename all attributes graphs <- list(g1, g2) - graph.attributes(res) <- rename.attr.if.needed("g", graphs) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph.attr.comb) if (byname) { - vertex.attributes(res) <- - rename.attr.if.needed("v", graphs, vcount(res), ignore = "name") + vertex.attributes(res) <- combine.attrs( + "v", + graphs, + vcount(res), + ignore = "name", + comb = vertex.attr.comb + ) V(res)$name <- uninames } else { - vertex.attributes(res) <- rename.attr.if.needed( + vertex.attributes(res) <- combine.attrs( "v", graphs, - vcount(res) + vcount(res), + comb = vertex.attr.comb ) } if (edgemaps) { - edge.attributes(res) <- rename.attr.if.needed( + edge.attributes(res) <- combine.attrs( "e", graphs, ecount(res), - maps2 = maps + maps2 = maps, + comb = edge.attr.comb ) } diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index 6620c260086..d1ac897eb44 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -1311,3 +1311,112 @@ test_that("unique on detached vs, names", { expect_equal(ignore_attr = TRUE, vg, vr) }) }) + +# attribute combination on graph operators ------------------------------------- + +make_named_pair <- function() { + g1 <- graph_from_literal(A - B, B - C, C - A) + g2 <- graph_from_literal(A - B, B - C, C - A) + V(g1)$weight <- c(1, 2, 3) + V(g2)$weight <- c(10, 20, 30) + E(g1)$weight <- c(1, 2, 3) + E(g2)$weight <- c(10, 20, 30) + list(g1 = g1, g2 = g2) +} + +test_that("union() defaults to rename behaviour", { + gs <- make_named_pair() + u <- union(gs$g1, gs$g2) + expect_setequal(vertex_attr_names(u), c("name", "weight_1", "weight_2")) + expect_setequal(edge_attr_names(u), c("weight_1", "weight_2")) +}) + +test_that("union() combines vertex attributes with sum", { + gs <- make_named_pair() + u <- union(gs$g1, gs$g2, vertex.attr.comb = "sum") + expect_setequal(vertex_attr_names(u), c("name", "weight")) + expect_equal(sort(V(u)$weight), sort(c(11, 22, 33))) +}) + +test_that("union() combines edge attributes with sum", { + gs <- make_named_pair() + u <- union(gs$g1, gs$g2, edge.attr.comb = "sum") + expect_setequal(edge_attr_names(u), c("weight")) + expect_equal(sort(E(u)$weight), sort(c(11, 22, 33))) +}) + +test_that("union() honours per-attribute list spec with rename fallback", { + gs <- make_named_pair() + V(gs$g1)$color <- letters[1:3] + V(gs$g2)$color <- LETTERS[1:3] + u <- union( + gs$g1, + gs$g2, + vertex.attr.comb = list(weight = "sum", "rename") + ) + expect_setequal( + vertex_attr_names(u), + c("name", "weight", "color_1", "color_2") + ) +}) + +test_that("union() can drop clashing attributes with ignore", { + gs <- make_named_pair() + u <- union(gs$g1, gs$g2, edge.attr.comb = "ignore") + expect_length(edge_attr_names(u), 0) +}) + +test_that("union() supports custom function combiner", { + gs <- make_named_pair() + u <- union( + gs$g1, + gs$g2, + vertex.attr.comb = list(weight = function(x) mean(x)) + ) + expect_equal(sort(V(u)$weight), sort(c(5.5, 11, 16.5))) +}) + +test_that("union() picks first non-NA when only one input has the attr", { + gs <- make_named_pair() + u <- union(gs$g1, gs$g2, vertex.attr.comb = "first", byname = TRUE) + expect_setequal(vertex_attr_names(u), c("name", "weight")) + expect_equal( + V(u)$weight[match(c("A", "B", "C"), V(u)$name)], + c(1, 2, 3) + ) +}) + +test_that("intersection() takes attr.comb args", { + gs <- make_named_pair() + i <- intersection(gs$g1, gs$g2, edge.attr.comb = "sum") + expect_setequal(edge_attr_names(i), c("weight")) + expect_equal(sort(E(i)$weight), sort(c(11, 22, 33))) +}) + +test_that("compose() takes attr.comb args", { + g1 <- graph_from_literal(A - B:D:E, B - C:D, C - D, D - E) + g2 <- graph_from_literal(A - B - E - A) + V(g1)$foo <- seq_len(vcount(g1)) + V(g2)$foo <- 10 * seq_len(vcount(g2)) + g <- compose(g1, g2, vertex.attr.comb = "sum") + expect_true("foo" %in% vertex_attr_names(g)) + expect_false("foo_1" %in% vertex_attr_names(g)) +}) + +test_that("disjoint_union() combines graph attrs via comb", { + g1 <- make_ring(3) + g2 <- make_ring(3) + g1$label <- "first" + g2$label <- "second" + u <- disjoint_union(g1, g2, graph.attr.comb = "concat") + expect_equal(u$label, c("first", "second")) +}) + +test_that("simplify() rejects 'rename' combiner", { + g <- make_graph(c(1, 2, 1, 2, 1, 2, 2, 3, 3, 4)) + E(g)$weight <- 1:5 + expect_error( + simplify(g, edge.attr.comb = "rename"), + "rename" + ) +}) From 5f7521ae4256d8ee594019326861eaf837fbe471 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 28 May 2026 20:58:32 +0200 Subject: [PATCH 02/16] make document work locally --- man/compose.Rd | 22 ++++++++++++++++++---- man/disjoint_union.Rd | 14 ++++++++++---- man/intersection.igraph.Rd | 23 +++++++++++++++++++---- man/union.igraph.Rd | 23 +++++++++++++++++++---- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/man/compose.Rd b/man/compose.Rd index 67eadd00a05..abf9603d9dc 100644 --- a/man/compose.Rd +++ b/man/compose.Rd @@ -5,7 +5,14 @@ \alias{\%c\%} \title{Compose two graphs as binary relations} \usage{ -compose(g1, g2, byname = "auto") +compose( + g1, + g2, + byname = "auto", + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" +) } \arguments{ \item{g1}{The first input graph.} @@ -17,6 +24,11 @@ to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if both graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and one graph, but not both graphs are named.} + +\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +combining clashing graph, vertex and edge attributes. Each defaults to +\code{"rename"}. See \link{igraph-attribute-combination} for the available +combiners.} } \value{ A new graph object. @@ -38,9 +50,11 @@ are all named), then the operation is performed based on symbolic vertex names. Otherwise numeric vertex ids are used. \code{compose()} keeps the attributes of both graphs. All graph, vertex -and edge attributes are copied to the result. If an attribute is present in -multiple graphs and would result a name clash, then this attribute is -renamed by adding suffixes: _1, _2, etc. +and edge attributes are copied to the result. By default, if an attribute +is present in both graphs and would result in a name clash, that attribute +is renamed by adding suffixes: \verb{_1}, \verb{_2}. Pass \code{graph.attr.comb}, +\code{vertex.attr.comb} or \code{edge.attr.comb} to combine clashing attributes +instead; see \link{igraph-attribute-combination} for the available combiners. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be diff --git a/man/disjoint_union.Rd b/man/disjoint_union.Rd index 220e39c546a..23f301d792d 100644 --- a/man/disjoint_union.Rd +++ b/man/disjoint_union.Rd @@ -5,13 +5,18 @@ \alias{\%du\%} \title{Disjoint union of graphs} \usage{ -disjoint_union(...) +disjoint_union(..., graph.attr.comb = "rename") x \%du\% y } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} +\item{graph.attr.comb}{Specification for combining shared graph attributes. +Defaults to \code{"rename"}, which preserves the historical behaviour of +appending \verb{_1}, \verb{_2}, ... suffixes to clashing attribute names. See +\link{igraph-attribute-combination} for the available combiners.} + \item{x, y}{Graph objects.} } \value{ @@ -31,9 +36,10 @@ function can also be used via the \verb{\%du\%} operator. particular, it merges vertex and edge attributes using the \code{\link[vctrs:vec_c]{vctrs::vec_c()}} function. For graphs that lack some vertex/edge attribute, the corresponding values in the new graph are set to a missing value (\code{NA} for scalar attributes, -\code{NULL} for list attributes). Graph attributes are simply -copied to the result. If this would result a name clash, then they are -renamed by adding suffixes: _1, _2, etc. +\code{NULL} for list attributes). Graph attributes are combined according to +\code{graph.attr.comb}; by default any name clash is resolved by adding +suffixes (\verb{_1}, \verb{_2}, ...). See \link{igraph-attribute-combination} for the +available combiners. Note that if both graphs have vertex names (i.e. a \code{name} vertex attribute), then the concatenated vertex names might be non-unique in the diff --git a/man/intersection.igraph.Rd b/man/intersection.igraph.Rd index 244120940d6..c1cb19c353b 100644 --- a/man/intersection.igraph.Rd +++ b/man/intersection.igraph.Rd @@ -5,7 +5,14 @@ \alias{\%s\%} \title{Intersection of graphs} \usage{ -\method{intersection}{igraph}(..., byname = "auto", keep.all.vertices = TRUE) +\method{intersection}{igraph}( + ..., + byname = "auto", + keep.all.vertices = TRUE, + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" +) } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} @@ -18,6 +25,11 @@ graphs are named.} \item{keep.all.vertices}{Logical scalar, whether to keep vertices that only appear in a subset of the input graphs.} + +\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +combining clashing graph, vertex and edge attributes. Each defaults to +\code{"rename"}. See \link{igraph-attribute-combination} for the available +combiners.} } \value{ A new graph object. @@ -36,9 +48,12 @@ are named), then the operation is performed on symbolic vertex names instead of the internal numeric vertex ids. \code{intersection()} keeps the attributes of all graphs. All graph, -vertex and edge attributes are copied to the result. If an attribute is -present in multiple graphs and would result a name clash, then this -attribute is renamed by adding suffixes: _1, _2, etc. +vertex and edge attributes are copied to the result. By default, if an +attribute is present in multiple graphs and would result in a name clash, +that attribute is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass +\code{graph.attr.comb}, \code{vertex.attr.comb} or \code{edge.attr.comb} to combine +clashing attributes instead; see \link{igraph-attribute-combination} for the +available combiners. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be diff --git a/man/union.igraph.Rd b/man/union.igraph.Rd index ac44d8f2f95..6b487070ffc 100644 --- a/man/union.igraph.Rd +++ b/man/union.igraph.Rd @@ -5,7 +5,13 @@ \alias{\%u\%} \title{Union of graphs} \usage{ -\method{union}{igraph}(..., byname = "auto") +\method{union}{igraph}( + ..., + byname = "auto", + graph.attr.comb = "rename", + vertex.attr.comb = "rename", + edge.attr.comb = "rename" +) } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} @@ -15,6 +21,12 @@ to perform the operation based on symbolic vertex names. If it is \code{auto}, that means \code{TRUE} if all graphs are named and \code{FALSE} otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} + +\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +combining clashing graph, vertex and edge attributes. Each defaults to +\code{"rename"}, which preserves the historical behaviour of appending +\verb{_1}, \verb{_2}, ... suffixes. See \link{igraph-attribute-combination} for the +available combiners.} } \value{ A new graph object. @@ -33,9 +45,12 @@ are named), then the operation is performed on symbolic vertex names instead of the internal numeric vertex ids. \code{union()} keeps the attributes of all graphs. All graph, vertex and -edge attributes are copied to the result. If an attribute is present in -multiple graphs and would result a name clash, then this attribute is -renamed by adding suffixes: _1, _2, etc. +edge attributes are copied to the result. By default, if an attribute is +present in multiple graphs and would result in a name clash, that attribute +is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass \code{graph.attr.comb}, +\code{vertex.attr.comb} or \code{edge.attr.comb} to combine clashing attributes +instead, e.g. by summing or by taking the first non-\code{NA} value. See +\link{igraph-attribute-combination} for the available combiners. The \code{name} vertex attribute is treated specially if the operation is performed based on symbolic vertex names. In this case \code{name} must be From d7e8bdc139061447bbdc156b75eead54d498272d Mon Sep 17 00:00:00 2001 From: schochastics Date: Sat, 6 Jun 2026 18:28:42 +0000 Subject: [PATCH 03/16] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/27070207213 --- man/igraph-attribute-combination.Rd | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/man/igraph-attribute-combination.Rd b/man/igraph-attribute-combination.Rd index f6e2e22a5b3..20354f01636 100644 --- a/man/igraph-attribute-combination.Rd +++ b/man/igraph-attribute-combination.Rd @@ -93,6 +93,15 @@ Calls the R \code{\link[=median]{median()}} function for all attribute types. Concatenate the attributes, using the \code{\link[=c]{c()}} function. This results almost always a complex attribute. } +\item{"rename"}{ +Keep clashing attributes side-by-side under disambiguated names by +appending \verb{_1}, \verb{_2}, ... suffixes. This is the default for the +graph operators \code{\link[=union]{union()}}, \code{\link[=intersection]{intersection()}}, \code{\link[=compose]{compose()}} and +\code{\link[=disjoint_union]{disjoint_union()}} and preserves their historical behaviour. +Only those operators accept \code{"rename"}; \code{\link[=simplify]{simplify()}} and +\code{\link[=contract]{contract()}} will reject it because the rename strategy has no +per-element interpretation when many input values collapse into one. +} } } From f9ad5b38620d8a940a7aee0e0972b922e35deb01 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Wed, 10 Jun 2026 12:27:53 +0200 Subject: [PATCH 04/16] refactor: snake_case graph-operator attr-comb args, extract rename helper Address review feedback on #2676: - Rename the new graph.attr.comb/vertex.attr.comb/edge.attr.comb arguments of union()/intersection()/compose()/disjoint_union() to snake_case (these args are new in this PR, so no deprecation is needed). - Extract rename_attr_if_needed() back out of combine.attrs() for the "rename" strategy, preserving the historical overwrite-on-clash semantics under chains of %du%. - Add a clarifying comment on the backward-compatible "rename" default. - Move the make_named_pair() test helper into helper.R. Co-Authored-By: Claude Opus 4.8 (1M context) --- R/operators.R | 137 ++++++++++++++++++-------------- tests/testthat/helper.R | 12 +++ tests/testthat/test-operators.R | 30 +++---- 3 files changed, 98 insertions(+), 81 deletions(-) diff --git a/R/operators.R b/R/operators.R index 5a6caf9a9e8..8363471efbe 100644 --- a/R/operators.R +++ b/R/operators.R @@ -169,6 +169,7 @@ combine.attrs <- function( default_comb <- if (length(default_idx) > 0) { comb[[default_idx[1]]] } else { + # Backward-compatible standard "rename" } resolve_comb <- function(name) { @@ -185,13 +186,12 @@ combine.attrs <- function( this_comb <- resolve_comb(name) if (identical(this_comb, "rename")) { - if (length(w) == 1) { - attr[[name]] <- getval(w, name) - } else { - for (w2 in w) { - nname <- paste(name, sep = "_", w2) - attr[[nname]] <- getval(w2, name) - } + renamed <- rename_attr_if_needed(name, w, getval) + # Assign element-wise (not `c()`) so a later clash that resolves to an + # already-used `name_` overwrites rather than duplicating it -- the + # historical behaviour, e.g. under a chain of `%du%`. + for (nm in names(renamed)) { + attr[[nm]] <- renamed[[nm]] } } else if (identical(this_comb, 0) || identical(this_comb, 0L)) { # ignore: drop the attribute @@ -203,6 +203,21 @@ combine.attrs <- function( attr } +# Historical behaviour for clashing attributes: a value present in a single +# input graph is copied as-is, otherwise each copy is kept side-by-side under a +# disambiguated `name_1`, `name_2`, ... name. Returns a named list to splice +# into the result; `getval` is the per-graph accessor closure from +# `combine.attrs()`. +rename_attr_if_needed <- function(name, w, getval) { + if (length(w) == 1) { + stats::setNames(list(getval(w, name)), name) + } else { + out <- lapply(w, function(w2) getval(w2, name)) + names(out) <- paste(name, w, sep = "_") + out + } +} + apply_attr_combiner <- function(comb, vals, type) { if (type == "g") { x <- unlist(vals, recursive = FALSE) @@ -260,7 +275,7 @@ apply_one_combiner <- function(comb, x) { #' function. For graphs that lack some vertex/edge attribute, the corresponding #' values in the new graph are set to a missing value (`NA` for scalar attributes, #' `NULL` for list attributes). Graph attributes are combined according to -#' `graph.attr.comb`; by default any name clash is resolved by adding +#' `graph_attr_comb`; by default any name clash is resolved by adding #' suffixes (`_1`, `_2`, ...). See [igraph-attribute-combination] for the #' available combiners. #' @@ -274,7 +289,7 @@ apply_one_combiner <- function(comb, x) { #' @aliases %du% #' @param \dots Graph objects or lists of graph objects. #' @param x,y Graph objects. -#' @param graph.attr.comb Specification for combining shared graph attributes. +#' @param graph_attr_comb Specification for combining shared graph attributes. #' Defaults to `"rename"`, which preserves the historical behaviour of #' appending `_1`, `_2`, ... suffixes to clashing attribute names. See #' [igraph-attribute-combination] for the available combiners. @@ -291,7 +306,7 @@ apply_one_combiner <- function(comb, x) { #' V(g2)$name <- letters[11:20] #' print_all(g1 %du% g2) #' @export -disjoint_union <- function(..., graph.attr.comb = "rename") { +disjoint_union <- function(..., graph_attr_comb = "rename") { graphs <- unlist( recursive = FALSE, lapply(list(...), function(l) { @@ -304,11 +319,11 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { res <- .Call(Rx_igraph_disjoint_union, graphs) ## Graph attributes - graph.attr.comb <- igraph.i.attribute.combination( - graph.attr.comb, + graph_attr_comb <- igraph.i.attribute.combination( + graph_attr_comb, allow_rename = TRUE ) - graph.attributes(res) <- combine.attrs("g", graphs, comb = graph.attr.comb) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_comb) ## Vertex attributes attr <- list() @@ -383,9 +398,9 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { ..., byname, keep.all.vertices, - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) { graphs <- unlist( recursive = FALSE, @@ -409,16 +424,16 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { cli::cli_abort("Some graphs are not named.") } - graph.attr.comb <- igraph.i.attribute.combination( - graph.attr.comb, + graph_attr_comb <- igraph.i.attribute.combination( + graph_attr_comb, allow_rename = TRUE ) - vertex.attr.comb <- igraph.i.attribute.combination( - vertex.attr.comb, + vertex_attr_comb <- igraph.i.attribute.combination( + vertex_attr_comb, allow_rename = TRUE ) - edge.attr.comb <- igraph.i.attribute.combination( - edge.attr.comb, + edge_attr_comb <- igraph.i.attribute.combination( + edge_attr_comb, allow_rename = TRUE ) @@ -452,14 +467,14 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { graph.attributes(res) <- combine.attrs( "g", newgraphs, - comb = graph.attr.comb + comb = graph_attr_comb ) vertex.attributes(res) <- combine.attrs( "v", newgraphs, vcount(res), ignore = "name", - comb = vertex.attr.comb + comb = vertex_attr_comb ) V(res)$name <- uninames @@ -470,7 +485,7 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { newgraphs, ecount(res), maps = maps, - comb = edge.attr.comb + comb = edge_attr_comb ) } } else { @@ -497,13 +512,13 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { graph.attributes(res) <- combine.attrs( "g", graphs, - comb = graph.attr.comb + comb = graph_attr_comb ) vertex.attributes(res) <- combine.attrs( "v", graphs, vcount(res), - comb = vertex.attr.comb + comb = vertex_attr_comb ) ## Edges are a bit more difficult, we need a mapping @@ -513,7 +528,7 @@ disjoint_union <- function(..., graph.attr.comb = "rename") { graphs, ecount(res), maps = maps, - comb = edge.attr.comb + comb = edge_attr_comb ) } } @@ -563,8 +578,8 @@ union.default <- function(...) { #' `union()` keeps the attributes of all graphs. All graph, vertex and #' edge attributes are copied to the result. By default, if an attribute is #' present in multiple graphs and would result in a name clash, that attribute -#' is renamed by adding suffixes: `_1`, `_2`, etc. Pass `graph.attr.comb`, -#' `vertex.attr.comb` or `edge.attr.comb` to combine clashing attributes +#' is renamed by adding suffixes: `_1`, `_2`, etc. Pass `graph_attr_comb`, +#' `vertex_attr_comb` or `edge_attr_comb` to combine clashing attributes #' instead, e.g. by summing or by taking the first non-`NA` value. See #' [igraph-attribute-combination] for the available combiners. #' @@ -582,7 +597,7 @@ union.default <- function(...) { #' `auto`, that means `TRUE` if all graphs are named and `FALSE` #' otherwise. A warning is generated if `auto` and some (but not all) #' graphs are named. -#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for #' combining clashing graph, vertex and edge attributes. Each defaults to #' `"rename"`, which preserves the historical behaviour of appending #' `_1`, `_2`, ... suffixes. See [igraph-attribute-combination] for the @@ -605,18 +620,18 @@ union.default <- function(...) { union.igraph <- function( ..., byname = "auto", - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) { .igraph.graph.union.or.intersection( "union", ..., byname = byname, keep.all.vertices = TRUE, - graph.attr.comb = graph.attr.comb, - vertex.attr.comb = vertex.attr.comb, - edge.attr.comb = edge.attr.comb + graph_attr_comb = graph_attr_comb, + vertex_attr_comb = vertex_attr_comb, + edge_attr_comb = edge_attr_comb ) } @@ -662,7 +677,7 @@ intersection <- function(...) { #' vertex and edge attributes are copied to the result. By default, if an #' attribute is present in multiple graphs and would result in a name clash, #' that attribute is renamed by adding suffixes: `_1`, `_2`, etc. Pass -#' `graph.attr.comb`, `vertex.attr.comb` or `edge.attr.comb` to combine +#' `graph_attr_comb`, `vertex_attr_comb` or `edge_attr_comb` to combine #' clashing attributes instead; see [igraph-attribute-combination] for the #' available combiners. #' @@ -682,7 +697,7 @@ intersection <- function(...) { #' graphs are named. #' @param keep.all.vertices Logical scalar, whether to keep vertices that only #' appear in a subset of the input graphs. -#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for #' combining clashing graph, vertex and edge attributes. Each defaults to #' `"rename"`. See [igraph-attribute-combination] for the available #' combiners. @@ -705,18 +720,18 @@ intersection.igraph <- function( ..., byname = "auto", keep.all.vertices = TRUE, - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) { .igraph.graph.union.or.intersection( "intersection", ..., byname = byname, keep.all.vertices = keep.all.vertices, - graph.attr.comb = graph.attr.comb, - vertex.attr.comb = vertex.attr.comb, - edge.attr.comb = edge.attr.comb + graph_attr_comb = graph_attr_comb, + vertex_attr_comb = vertex_attr_comb, + edge_attr_comb = edge_attr_comb ) } @@ -899,8 +914,8 @@ complementer <- function(graph, loops = FALSE) { #' `compose()` keeps the attributes of both graphs. All graph, vertex #' and edge attributes are copied to the result. By default, if an attribute #' is present in both graphs and would result in a name clash, that attribute -#' is renamed by adding suffixes: `_1`, `_2`. Pass `graph.attr.comb`, -#' `vertex.attr.comb` or `edge.attr.comb` to combine clashing attributes +#' is renamed by adding suffixes: `_1`, `_2`. Pass `graph_attr_comb`, +#' `vertex_attr_comb` or `edge_attr_comb` to combine clashing attributes #' instead; see [igraph-attribute-combination] for the available combiners. #' #' The `name` vertex attribute is treated specially if the operation is @@ -930,7 +945,7 @@ complementer <- function(graph, loops = FALSE) { #' `auto`, that means `TRUE` if both graphs are named and #' `FALSE` otherwise. A warning is generated if `auto` and one graph, #' but not both graphs are named. -#' @param graph.attr.comb,vertex.attr.comb,edge.attr.comb Specification for +#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for #' combining clashing graph, vertex and edge attributes. Each defaults to #' `"rename"`. See [igraph-attribute-combination] for the available #' combiners. @@ -951,9 +966,9 @@ compose <- function( g1, g2, byname = "auto", - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) { ensure_igraph(g1) ensure_igraph(g2) @@ -973,16 +988,16 @@ compose <- function( cli::cli_abort("Some graphs are not named.") } - graph.attr.comb <- igraph.i.attribute.combination( - graph.attr.comb, + graph_attr_comb <- igraph.i.attribute.combination( + graph_attr_comb, allow_rename = TRUE ) - vertex.attr.comb <- igraph.i.attribute.combination( - vertex.attr.comb, + vertex_attr_comb <- igraph.i.attribute.combination( + vertex_attr_comb, allow_rename = TRUE ) - edge.attr.comb <- igraph.i.attribute.combination( - edge.attr.comb, + edge_attr_comb <- igraph.i.attribute.combination( + edge_attr_comb, allow_rename = TRUE ) @@ -1011,7 +1026,7 @@ compose <- function( res <- res$graph graphs <- list(g1, g2) - graph.attributes(res) <- combine.attrs("g", graphs, comb = graph.attr.comb) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_comb) if (byname) { vertex.attributes(res) <- combine.attrs( @@ -1019,7 +1034,7 @@ compose <- function( graphs, vcount(res), ignore = "name", - comb = vertex.attr.comb + comb = vertex_attr_comb ) V(res)$name <- uninames } else { @@ -1027,7 +1042,7 @@ compose <- function( "v", graphs, vcount(res), - comb = vertex.attr.comb + comb = vertex_attr_comb ) } @@ -1037,7 +1052,7 @@ compose <- function( graphs, ecount(res), maps2 = maps, - comb = edge.attr.comb + comb = edge_attr_comb ) } diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index f2a6a00c7aa..a31cd873b5b 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -62,3 +62,15 @@ expect_snapshot_igraph <- function(x, ...) { ... )) } + +# Two identically-shaped named triangles sharing a `weight` vertex/edge +# attribute, used by the attribute-combination tests for graph operators. +make_named_pair <- function() { + g1 <- graph_from_literal(A - B, B - C, C - A) + g2 <- graph_from_literal(A - B, B - C, C - A) + V(g1)$weight <- c(1, 2, 3) + V(g2)$weight <- c(10, 20, 30) + E(g1)$weight <- c(1, 2, 3) + E(g2)$weight <- c(10, 20, 30) + list(g1 = g1, g2 = g2) +} diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index 129f699ce80..57940d6ce66 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -1314,16 +1314,6 @@ test_that("unique on detached vs, names", { # attribute combination on graph operators ------------------------------------- -make_named_pair <- function() { - g1 <- graph_from_literal(A - B, B - C, C - A) - g2 <- graph_from_literal(A - B, B - C, C - A) - V(g1)$weight <- c(1, 2, 3) - V(g2)$weight <- c(10, 20, 30) - E(g1)$weight <- c(1, 2, 3) - E(g2)$weight <- c(10, 20, 30) - list(g1 = g1, g2 = g2) -} - test_that("union() defaults to rename behaviour", { gs <- make_named_pair() u <- union(gs$g1, gs$g2) @@ -1333,14 +1323,14 @@ test_that("union() defaults to rename behaviour", { test_that("union() combines vertex attributes with sum", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, vertex.attr.comb = "sum") + u <- union(gs$g1, gs$g2, vertex_attr_comb = "sum") expect_setequal(vertex_attr_names(u), c("name", "weight")) expect_equal(sort(V(u)$weight), sort(c(11, 22, 33))) }) test_that("union() combines edge attributes with sum", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, edge.attr.comb = "sum") + u <- union(gs$g1, gs$g2, edge_attr_comb = "sum") expect_setequal(edge_attr_names(u), c("weight")) expect_equal(sort(E(u)$weight), sort(c(11, 22, 33))) }) @@ -1352,7 +1342,7 @@ test_that("union() honours per-attribute list spec with rename fallback", { u <- union( gs$g1, gs$g2, - vertex.attr.comb = list(weight = "sum", "rename") + vertex_attr_comb = list(weight = "sum", "rename") ) expect_setequal( vertex_attr_names(u), @@ -1362,7 +1352,7 @@ test_that("union() honours per-attribute list spec with rename fallback", { test_that("union() can drop clashing attributes with ignore", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, edge.attr.comb = "ignore") + u <- union(gs$g1, gs$g2, edge_attr_comb = "ignore") expect_length(edge_attr_names(u), 0) }) @@ -1371,14 +1361,14 @@ test_that("union() supports custom function combiner", { u <- union( gs$g1, gs$g2, - vertex.attr.comb = list(weight = function(x) mean(x)) + vertex_attr_comb = list(weight = function(x) mean(x)) ) expect_equal(sort(V(u)$weight), sort(c(5.5, 11, 16.5))) }) test_that("union() picks first non-NA when only one input has the attr", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, vertex.attr.comb = "first", byname = TRUE) + u <- union(gs$g1, gs$g2, vertex_attr_comb = "first", byname = TRUE) expect_setequal(vertex_attr_names(u), c("name", "weight")) expect_equal( V(u)$weight[match(c("A", "B", "C"), V(u)$name)], @@ -1388,7 +1378,7 @@ test_that("union() picks first non-NA when only one input has the attr", { test_that("intersection() takes attr.comb args", { gs <- make_named_pair() - i <- intersection(gs$g1, gs$g2, edge.attr.comb = "sum") + i <- intersection(gs$g1, gs$g2, edge_attr_comb = "sum") expect_setequal(edge_attr_names(i), c("weight")) expect_equal(sort(E(i)$weight), sort(c(11, 22, 33))) }) @@ -1398,7 +1388,7 @@ test_that("compose() takes attr.comb args", { g2 <- graph_from_literal(A - B - E - A) V(g1)$foo <- seq_len(vcount(g1)) V(g2)$foo <- 10 * seq_len(vcount(g2)) - g <- compose(g1, g2, vertex.attr.comb = "sum") + g <- compose(g1, g2, vertex_attr_comb = "sum") expect_true("foo" %in% vertex_attr_names(g)) expect_false("foo_1" %in% vertex_attr_names(g)) }) @@ -1408,7 +1398,7 @@ test_that("disjoint_union() combines graph attrs via comb", { g2 <- make_ring(3) g1$label <- "first" g2$label <- "second" - u <- disjoint_union(g1, g2, graph.attr.comb = "concat") + u <- disjoint_union(g1, g2, graph_attr_comb = "concat") expect_equal(u$label, c("first", "second")) }) @@ -1416,7 +1406,7 @@ test_that("simplify() rejects 'rename' combiner", { g <- make_graph(c(1, 2, 1, 2, 1, 2, 2, 3, 3, 4)) E(g)$weight <- 1:5 expect_error( - simplify(g, edge.attr.comb = "rename"), + simplify(g, edge_attr_comb = "rename"), "rename" ) }) From b58da443ab87611de66fe9125369ec6024e7235e Mon Sep 17 00:00:00 2001 From: David Schoch Date: Wed, 10 Jun 2026 12:28:03 +0200 Subject: [PATCH 05/16] feat: snake_case *.attr.comb arguments and options, soft-deprecate dotted names Follow-up to review feedback on #2676 ("clean up edge.attr.comb"): - simplify(), as_undirected() and contract() gain snake_case edge_attr_comb / vertex_attr_comb arguments via the argument-migration infrastructure (tools/migrations.R). The old dotted names still work and emit a single lifecycle::deprecate_soft() warning. - Rename the matching igraph option keys edge.attr.comb -> edge_attr_comb and vertex.attr.comb -> vertex_attr_comb, with back-compatible aliasing in igraph_opt()/igraph_options(); the dotted keys still read and set, soft- deprecated. Update the stimulus codegen default (types-RR.yaml) and the regenerated *_impl defaults to match. Deprecated wrappers (as.undirected(), contract.vertices()) are left frozen; their snapshot now records the resulting layered deprecation warnings. Co-Authored-By: Claude Opus 4.8 (1M context) --- R/aaa-operators.R | 4 +-- R/aaa-structural.R | 2 +- R/community.R | 34 ++++++++++++++++++---- R/conversion.R | 32 ++++++++++++++++++--- R/par.R | 44 ++++++++++++++++++++++++----- R/simple.R | 32 ++++++++++++++++++--- tests/testthat/_snaps/conversion.md | 7 +++++ tests/testthat/test-attributes.R | 4 +-- tests/testthat/test-community.R | 2 +- tools/migrations.R | 40 ++++++++++++++++++++++++++ tools/stimulus/types-RR.yaml | 4 +-- 11 files changed, 177 insertions(+), 28 deletions(-) diff --git a/R/aaa-operators.R b/R/aaa-operators.R index 1c81fcd5262..e927c6ee25d 100644 --- a/R/aaa-operators.R +++ b/R/aaa-operators.R @@ -35,7 +35,7 @@ connect_neighborhood_impl <- function( contract_vertices_impl <- function( graph, mapping, - vertex_attr_comb = igraph_opt("vertex.attr.comb") + vertex_attr_comb = igraph_opt("vertex_attr_comb") ) { # Argument checks ensure_igraph(graph) @@ -245,7 +245,7 @@ simplify_impl <- function( graph, remove_multiple = TRUE, remove_loops = TRUE, - edge_attr_comb = igraph_opt("edge.attr.comb") + edge_attr_comb = igraph_opt("edge_attr_comb") ) { # Argument checks ensure_igraph(graph) diff --git a/R/aaa-structural.R b/R/aaa-structural.R index de9543e847b..ef382c7dffd 100644 --- a/R/aaa-structural.R +++ b/R/aaa-structural.R @@ -166,7 +166,7 @@ to_directed_impl <- function( to_undirected_impl <- function( graph, mode = c("collapse", "each", "mutual"), - edge_attr_comb = igraph_opt("edge.attr.comb") + edge_attr_comb = igraph_opt("edge_attr_comb") ) { # Argument checks ensure_igraph(graph) diff --git a/R/community.R b/R/community.R index 65889c3c3e9..b3b958a7851 100644 --- a/R/community.R +++ b/R/community.R @@ -3201,13 +3201,14 @@ communities <- groups.communities #' #' The attributes of the graph are kept. Graph and edge attributes are #' unchanged, vertex attributes are combined, according to the -#' `vertex.attr.comb` parameter. +#' `vertex_attr_comb` parameter. #' #' @param graph The input graph, it can be directed or undirected. #' @param mapping A numeric vector that specifies the mapping. Its elements #' correspond to the vertices, and for each element the ID in the new graph is #' given. -#' @param vertex.attr.comb Specifies how to combine the vertex attributes in +#' @inheritParams rlang::args_dots_empty +#' @param vertex_attr_comb Specifies how to combine the vertex attributes in #' the new graph. Please see [attribute.combination()] for details. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} @@ -3220,7 +3221,7 @@ communities <- groups.communities #' E(g)$weight <- runif(ecount(g)) #' #' g2 <- contract(g, rep(1:5, each = 2), -#' vertex.attr.comb = toString +#' vertex_attr_comb = toString #' ) #' #' ## graph and edge attributes are kept, vertex attributes are @@ -3232,12 +3233,35 @@ communities <- groups.communities contract <- function( graph, mapping, - vertex.attr.comb = igraph_opt("vertex.attr.comb") + ..., + vertex_attr_comb = igraph_opt("vertex_attr_comb") ) { + # BEGIN GENERATED ARG_HANDLE: contract, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(vertex_attr_comb = vertex_attr_comb), + recover_new = c("vertex_attr_comb"), + recover_old = c("vertex.attr.comb"), + match_names = c("vertex.attr.comb", "vertex_attr_comb"), + match_to = c("vertex_attr_comb", "vertex_attr_comb"), + defaults = list(vertex_attr_comb = igraph_opt("vertex_attr_comb")), + head_args = c("graph", "mapping"), + fn_name = "contract" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + contract_vertices_impl( graph = graph, mapping = mapping, - vertex_attr_comb = vertex.attr.comb + vertex_attr_comb = vertex_attr_comb ) } diff --git a/R/conversion.R b/R/conversion.R index f6e6cd062c5..4f0518545a7 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -555,7 +555,7 @@ as_edgelist <- function(graph, names = TRUE) { #' E(g4)$weight <- seq_len(ecount(g4)) #' ug4 <- as_undirected(g4, #' mode = "mutual", -#' edge.attr.comb = list(weight = length) +#' edge_attr_comb = list(weight = length) #' ) #' print(ug4, e = TRUE) #' @@ -570,7 +570,8 @@ as_directed <- function( } #' @rdname as_directed -#' @param edge.attr.comb Specifies what to do with edge attributes, if +#' @inheritParams rlang::args_dots_empty +#' @param edge_attr_comb Specifies what to do with edge attributes, if #' `mode="collapse"` or `mode="mutual"`. In these cases many edges #' might be mapped to a single one in the new graph, and their attributes are #' combined. Please see [attribute.combination()] for details on @@ -579,8 +580,31 @@ as_directed <- function( as_undirected <- function( graph, mode = c("collapse", "each", "mutual"), - edge.attr.comb = igraph_opt("edge.attr.comb") + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") ) { + # BEGIN GENERATED ARG_HANDLE: as_undirected, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(edge_attr_comb = edge_attr_comb), + recover_new = c("edge_attr_comb"), + recover_old = c("edge.attr.comb"), + match_names = c("edge.attr.comb", "edge_attr_comb"), + match_to = c("edge_attr_comb", "edge_attr_comb"), + defaults = list(edge_attr_comb = igraph_opt("edge_attr_comb")), + head_args = c("graph", "mode"), + fn_name = "as_undirected" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + # Argument checks ensure_igraph(graph) mode <- igraph_match_arg(mode) @@ -589,7 +613,7 @@ as_undirected <- function( res <- to_undirected_impl( graph = graph, mode = mode, - edge_attr_comb = edge.attr.comb + edge_attr_comb = edge_attr_comb ) res diff --git a/R/par.R b/R/par.R index b76f25024a4..0c5bda1b637 100644 --- a/R/par.R +++ b/R/par.R @@ -61,8 +61,8 @@ getIgraphOpt <- function(x, default = NULL) { "print.edge.attributes" = FALSE, "print.graph.attributes" = FALSE, "verbose" = FALSE, - "vertex.attr.comb" = list(name = "concat", "ignore"), - "edge.attr.comb" = list(weight = "sum", name = "concat", "ignore"), + "vertex_attr_comb" = list(name = "concat", "ignore"), + "edge_attr_comb" = list(weight = "sum", name = "concat", "ignore"), "sparsematrices" = TRUE, "add.params" = TRUE, "add.vertex.names" = TRUE, @@ -75,6 +75,31 @@ getIgraphOpt <- function(x, default = NULL) { "print.style" = "cli" ) +# Option keys that were renamed to snake_case. Reading or setting an option by +# its old dotted name still works, but maps to the canonical key and emits a +# soft-deprecation. +.igraph.pars.aliases <- c( + "vertex.attr.comb" = "vertex_attr_comb", + "edge.attr.comb" = "edge_attr_comb" +) + +# Map any deprecated option names in `x` to their canonical keys, warning once +# per deprecated name encountered. +igraph_normalize_par_name <- function(x) { + for (i in seq_along(x)) { + new <- unname(.igraph.pars.aliases[x[i]]) + if (!is.na(new)) { + lifecycle::deprecate_soft( + "3.0.0", + I(paste0("The igraph option `", x[i], "`")), + I(paste0("the `", new, "` option")) + ) + x[i] <- new + } + } + x +} + igraph.pars.set.verbose <- function(verbose) { if (is.logical(verbose)) { .Call(Rx_igraph_set_verbose, verbose) @@ -138,10 +163,11 @@ igraph.pars.callbacks <- list("verbose" = igraph.pars.set.verbose) #' Possible values are \sQuote{auto} (the default), \sQuote{phylo}, \sQuote{hclust} and \sQuote{dendrogram}. #' See [plot_dendrogram()] for details. #' } -#' \item{edge.attr.comb}{ +#' \item{edge_attr_comb}{ #' Specifies what to do with the edge attributes if the graph is modified. #' The default value is `list(weight="sum", name="concat", "ignore")`. -#' See [attribute.combination()] for details on this. +#' See [attribute.combination()] for details on this. The former dotted +#' name `edge.attr.comb` still works but is soft-deprecated. #' } #' \item{print.edge.attributes}{ #' Logical constant, whether to print edge attributes when printing graphs. @@ -180,10 +206,11 @@ igraph.pars.callbacks <- list("verbose" = igraph.pars.set.verbose) #' Logical constant, whether igraph functions should talk more than minimal. #' E.g. if `TRUE` then some functions will use progress bars while computing. Defaults to `FALSE`. #' } -#' \item{vertex.attr.comb}{ +#' \item{vertex_attr_comb}{ #' Specifies what to do with the vertex attributes if the graph is modified. #' The default value is `list(name="concat", "ignore")`. -#' See [attribute.combination()] for details on this. +#' See [attribute.combination()] for details on this. The former dotted +#' name `vertex.attr.comb` still works but is soft-deprecated. #' } #' } #' @@ -235,7 +262,7 @@ igraph_i_options <- function(..., .in = parent.frame()) { arg <- temp[[1]] if (mode(arg) == "character") { - return(.igraph.pars[arg]) + return(.igraph.pars[igraph_normalize_par_name(arg)]) } if (mode(arg) != "list") { @@ -254,6 +281,8 @@ igraph_i_options <- function(..., .in = parent.frame()) { if (is.null(n)) { cli::cli_abort("options must be given by name.") } + names(temp) <- igraph_normalize_par_name(n) + n <- names(temp) cb <- intersect(names(igraph.pars.callbacks), n) for (cn in cb) { temp[[cn]] <- igraph.pars.callbacks[[cn]](temp[[cn]]) @@ -289,6 +318,7 @@ get_all_options <- function() { #' @rdname igraph_options #' @export igraph_opt <- function(x, default = NULL) { + x <- igraph_normalize_par_name(x) if (missing(default)) { get_config(paste0("igraph::", x), .igraph.pars[[x]]) } else { diff --git a/R/simple.R b/R/simple.R index dcc91205422..cc1dbb0d1e6 100644 --- a/R/simple.R +++ b/R/simple.R @@ -68,7 +68,8 @@ is.simple <- function(graph) { #' @param remove.loops Logical, whether the loop edges are to be removed. #' @param remove.multiple Logical, whether the multiple edges are to be #' removed. -#' @param edge.attr.comb Specifies what to do with edge attributes, if +#' @inheritParams rlang::args_dots_empty +#' @param edge_attr_comb Specifies what to do with edge attributes, if #' `remove.multiple=TRUE`. In this case many edges might be mapped to a #' single one in the new graph, and their attributes are combined. Please see #' [attribute.combination()] for details on this. @@ -94,11 +95,34 @@ simplify <- function( graph, remove.multiple = TRUE, remove.loops = TRUE, - edge.attr.comb = igraph_opt("edge.attr.comb") + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") ) { + # BEGIN GENERATED ARG_HANDLE: simplify, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(edge_attr_comb = edge_attr_comb), + recover_new = c("edge_attr_comb"), + recover_old = c("edge.attr.comb"), + match_names = c("edge.attr.comb", "edge_attr_comb"), + match_to = c("edge_attr_comb", "edge_attr_comb"), + defaults = list(edge_attr_comb = igraph_opt("edge_attr_comb")), + head_args = c("graph", "remove.multiple", "remove.loops"), + fn_name = "simplify" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + # A graph that is already simple has no loops and no multiple edges, so # simplify_impl() would not change its structure regardless of the - # remove.* / edge.attr.comb arguments. Short-circuiting here avoids the + # remove.* / edge_attr_comb arguments. Short-circuiting here avoids the # cost of rebuilding the graph in the (common) already-simple case; # is_simple() is orders of magnitude cheaper than simplify(). if (is_simple(graph)) { @@ -108,7 +132,7 @@ simplify <- function( graph = graph, remove_multiple = remove.multiple, remove_loops = remove.loops, - edge_attr_comb = edge.attr.comb + edge_attr_comb = edge_attr_comb ) } diff --git a/tests/testthat/_snaps/conversion.md b/tests/testthat/_snaps/conversion.md index a7afcea5935..03d3f9c5c2b 100644 --- a/tests/testthat/_snaps/conversion.md +++ b/tests/testthat/_snaps/conversion.md @@ -17,6 +17,13 @@ Warning: `as.undirected()` was deprecated in igraph 2.1.0. i Please use `as_undirected()` instead. + Warning: + The igraph option `edge.attr.comb` was deprecated in igraph 3.0.0. + i Please use the `edge_attr_comb` option instead. + Warning: + Calling `as_undirected()` with positional or abbreviated arguments was deprecated in igraph 3.0.0. + i Detected call: as_undirected(graph, mode, edge.attr.comb) + i Use instead: as_undirected(graph, mode, edge_attr_comb = ) Output [1] FALSE diff --git a/tests/testthat/test-attributes.R b/tests/testthat/test-attributes.R index 9b2bd83021b..213cb081b18 100644 --- a/tests/testthat/test-attributes.R +++ b/tests/testthat/test-attributes.R @@ -208,11 +208,11 @@ test_that("attribute combinations handle errors correctly", { g <- make_graph(c(1, 2, 2, 1)) E(g)$weight <- c("a", "b") expect_error( - as_undirected(g, edge.attr.comb = list(weight = "sum")), + as_undirected(g, edge_attr_comb = list(weight = "sum")), "invalid 'type'" ) expect_error( - as_undirected(g, edge.attr.comb = list(weight = sum)), + as_undirected(g, edge_attr_comb = list(weight = sum)), "invalid 'type'" ) }) diff --git a/tests/testthat/test-community.R b/tests/testthat/test-community.R index 5089cd739a0..22fffc2e118 100644 --- a/tests/testthat/test-community.R +++ b/tests/testthat/test-community.R @@ -643,7 +643,7 @@ test_that("contract works", { V(g)$name <- letters[1:vcount(g)] E(g)$weight <- sample(ecount(g)) - g2 <- contract(g, rep(1:5, each = 2), vertex.attr.comb = toString) + g2 <- contract(g, rep(1:5, each = 2), vertex_attr_comb = toString) expect_equal(g2$name, g$name) expect_equal(V(g2)$name, c("a, b", "c, d", "e, f", "g, h", "i, j")) diff --git a/tools/migrations.R b/tools/migrations.R index 82be769605a..5361e5d4679 100644 --- a/tools/migrations.R +++ b/tools/migrations.R @@ -72,6 +72,46 @@ migrations <- list( # when = "3.0.0" # ), + # --- real migrations ----------------------------------------------------- + # Dotted `*.attr.comb` arguments renamed to snake_case. The old dotted names + # keep working (recovered from `...`) under a single soft-deprecation. + simplify = list( + old = function( + graph, + remove.multiple, + remove.loops, + edge.attr.comb = edge_attr_comb + ) {}, + new = function( + graph, + remove.multiple = TRUE, + remove.loops = TRUE, + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") + ) {}, + when = "3.0.0" + ), + as_undirected = list( + old = function(graph, mode, edge.attr.comb = edge_attr_comb) {}, + new = function( + graph, + mode = c("collapse", "each", "mutual"), + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") + ) {}, + when = "3.0.0" + ), + contract = list( + old = function(graph, mapping, vertex.attr.comb = vertex_attr_comb) {}, + new = function( + graph, + mapping, + ..., + vertex_attr_comb = igraph_opt("vertex_attr_comb") + ) {}, + when = "3.0.0" + ), + # --- test fixture -------------------------------------------------------- # Exercises the generator end-to-end without migrating a real function. The # arg names are chosen to cover every recovery path: two renames (`weight -> diff --git a/tools/stimulus/types-RR.yaml b/tools/stimulus/types-RR.yaml index bfb5ca69ed8..140db96a776 100644 --- a/tools/stimulus/types-RR.yaml +++ b/tools/stimulus/types-RR.yaml @@ -531,12 +531,12 @@ SUBGRAPH_IMPL: EDGE_ATTRIBUTE_COMBINATION: DEFAULT: - Default: igraph_opt("edge.attr.comb") + Default: igraph_opt("edge_attr_comb") INCONV: '%I% <- igraph.i.attribute.combination(%I%)' VERTEX_ATTRIBUTE_COMBINATION: DEFAULT: - Default: igraph_opt("vertex.attr.comb") + Default: igraph_opt("vertex_attr_comb") INCONV: '%I% <- igraph.i.attribute.combination(%I%)' ADD_WEIGHTS: From 04ab71522a7cee4ca5ded9a3f0389480713e0f74 Mon Sep 17 00:00:00 2001 From: schochastics Date: Wed, 10 Jun 2026 10:36:24 +0000 Subject: [PATCH 06/16] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/27270008480 --- man/as.undirected.Rd | 6 ------ man/as_directed.Rd | 9 ++++++--- man/compose.Rd | 12 ++++++------ man/contract.Rd | 15 +++++++++++---- man/contract.vertices.Rd | 3 --- man/disjoint_union.Rd | 6 +++--- man/igraph_options.Rd | 10 ++++++---- man/intersection.igraph.Rd | 10 +++++----- man/simplify.Rd | 7 +++++-- man/union.igraph.Rd | 12 ++++++------ 10 files changed, 48 insertions(+), 42 deletions(-) diff --git a/man/as.undirected.Rd b/man/as.undirected.Rd index 1a0ae66f3f4..66f36118514 100644 --- a/man/as.undirected.Rd +++ b/man/as.undirected.Rd @@ -17,12 +17,6 @@ as.undirected( \code{as_directed()} it can be \code{mutual} or \code{arbitrary}. For \code{as_undirected()} it can be \code{each}, \code{collapse} or \code{mutual}. See details below.} - -\item{edge.attr.comb}{Specifies what to do with edge attributes, if -\code{mode="collapse"} or \code{mode="mutual"}. In these cases many edges -might be mapped to a single one in the new graph, and their attributes are -combined. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details on -this.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} diff --git a/man/as_directed.Rd b/man/as_directed.Rd index e496f6022b0..a2452ae5b19 100644 --- a/man/as_directed.Rd +++ b/man/as_directed.Rd @@ -10,7 +10,8 @@ as_directed(graph, mode = c("mutual", "arbitrary", "random", "acyclic")) as_undirected( graph, mode = c("collapse", "each", "mutual"), - edge.attr.comb = igraph_opt("edge.attr.comb") + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") ) } \arguments{ @@ -21,7 +22,9 @@ as_undirected( \code{as_undirected()} it can be \code{each}, \code{collapse} or \code{mutual}. See details below.} -\item{edge.attr.comb}{Specifies what to do with edge attributes, if +\item{...}{These dots are for future extensions and must be empty.} + +\item{edge_attr_comb}{Specifies what to do with edge attributes, if \code{mode="collapse"} or \code{mode="mutual"}. In these cases many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details on @@ -115,7 +118,7 @@ g4 <- make_graph(c( E(g4)$weight <- seq_len(ecount(g4)) ug4 <- as_undirected(g4, mode = "mutual", - edge.attr.comb = list(weight = length) + edge_attr_comb = list(weight = length) ) print(ug4, e = TRUE) diff --git a/man/compose.Rd b/man/compose.Rd index 2500c1620d8..85393805d31 100644 --- a/man/compose.Rd +++ b/man/compose.Rd @@ -9,9 +9,9 @@ compose( g1, g2, byname = "auto", - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) } \arguments{ @@ -25,7 +25,7 @@ to perform the operation based on symbolic vertex names. If it is \code{FALSE} otherwise. A warning is generated if \code{auto} and one graph, but not both graphs are named.} -\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for combining clashing graph, vertex and edge attributes. Each defaults to \code{"rename"}. See \link{igraph-attribute-combination} for the available combiners.} @@ -52,8 +52,8 @@ names. Otherwise numeric vertex IDs are used. \code{compose()} keeps the attributes of both graphs. All graph, vertex and edge attributes are copied to the result. By default, if an attribute is present in both graphs and would result in a name clash, that attribute -is renamed by adding suffixes: \verb{_1}, \verb{_2}. Pass \code{graph.attr.comb}, -\code{vertex.attr.comb} or \code{edge.attr.comb} to combine clashing attributes +is renamed by adding suffixes: \verb{_1}, \verb{_2}. Pass \code{graph_attr_comb}, +\code{vertex_attr_comb} or \code{edge_attr_comb} to combine clashing attributes instead; see \link{igraph-attribute-combination} for the available combiners. The \code{name} vertex attribute is treated specially if the operation is diff --git a/man/contract.Rd b/man/contract.Rd index 677567c3d0c..c15b387879a 100644 --- a/man/contract.Rd +++ b/man/contract.Rd @@ -4,7 +4,12 @@ \alias{contract} \title{Contract several vertices into a single one} \usage{ -contract(graph, mapping, vertex.attr.comb = igraph_opt("vertex.attr.comb")) +contract( + graph, + mapping, + ..., + vertex_attr_comb = igraph_opt("vertex_attr_comb") +) } \arguments{ \item{graph}{The input graph, it can be directed or undirected.} @@ -13,7 +18,9 @@ contract(graph, mapping, vertex.attr.comb = igraph_opt("vertex.attr.comb")) correspond to the vertices, and for each element the ID in the new graph is given.} -\item{vertex.attr.comb}{Specifies how to combine the vertex attributes in +\item{...}{These dots are for future extensions and must be empty.} + +\item{vertex_attr_comb}{Specifies how to combine the vertex attributes in the new graph. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details.} } \value{ @@ -26,7 +33,7 @@ vertices in the new graph correspond to sets of vertices in the input graph. \details{ The attributes of the graph are kept. Graph and edge attributes are unchanged, vertex attributes are combined, according to the -\code{vertex.attr.comb} parameter. +\code{vertex_attr_comb} parameter. } \section{Related documentation in the C library}{ \href{https://igraph.org/c/html/0.10.17/igraph-Operators.html#igraph_contract_vertices}{\code{contract_vertices()}} @@ -40,7 +47,7 @@ V(g)$name <- letters[1:vcount(g)] E(g)$weight <- runif(ecount(g)) g2 <- contract(g, rep(1:5, each = 2), - vertex.attr.comb = toString + vertex_attr_comb = toString ) ## graph and edge attributes are kept, vertex attributes are diff --git a/man/contract.vertices.Rd b/man/contract.vertices.Rd index 41c87032023..cbb9cd10580 100644 --- a/man/contract.vertices.Rd +++ b/man/contract.vertices.Rd @@ -16,9 +16,6 @@ contract.vertices( \item{mapping}{A numeric vector that specifies the mapping. Its elements correspond to the vertices, and for each element the ID in the new graph is given.} - -\item{vertex.attr.comb}{Specifies how to combine the vertex attributes in -the new graph. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} diff --git a/man/disjoint_union.Rd b/man/disjoint_union.Rd index 23f301d792d..ad13fdc09ce 100644 --- a/man/disjoint_union.Rd +++ b/man/disjoint_union.Rd @@ -5,14 +5,14 @@ \alias{\%du\%} \title{Disjoint union of graphs} \usage{ -disjoint_union(..., graph.attr.comb = "rename") +disjoint_union(..., graph_attr_comb = "rename") x \%du\% y } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} -\item{graph.attr.comb}{Specification for combining shared graph attributes. +\item{graph_attr_comb}{Specification for combining shared graph attributes. Defaults to \code{"rename"}, which preserves the historical behaviour of appending \verb{_1}, \verb{_2}, ... suffixes to clashing attribute names. See \link{igraph-attribute-combination} for the available combiners.} @@ -37,7 +37,7 @@ particular, it merges vertex and edge attributes using the \code{\link[vctrs:vec function. For graphs that lack some vertex/edge attribute, the corresponding values in the new graph are set to a missing value (\code{NA} for scalar attributes, \code{NULL} for list attributes). Graph attributes are combined according to -\code{graph.attr.comb}; by default any name clash is resolved by adding +\code{graph_attr_comb}; by default any name clash is resolved by adding suffixes (\verb{_1}, \verb{_2}, ...). See \link{igraph-attribute-combination} for the available combiners. diff --git a/man/igraph_options.Rd b/man/igraph_options.Rd index cc8af78745f..55313ffc55d 100644 --- a/man/igraph_options.Rd +++ b/man/igraph_options.Rd @@ -65,10 +65,11 @@ The plotting function to use when plotting community structure dendrograms via \ Possible values are \sQuote{auto} (the default), \sQuote{phylo}, \sQuote{hclust} and \sQuote{dendrogram}. See \code{\link[=plot_dendrogram]{plot_dendrogram()}} for details. } -\item{edge.attr.comb}{ +\item{edge_attr_comb}{ Specifies what to do with the edge attributes if the graph is modified. The default value is \code{list(weight="sum", name="concat", "ignore")}. -See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. +See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. The former dotted +name \code{edge.attr.comb} still works but is soft-deprecated. } \item{print.edge.attributes}{ Logical constant, whether to print edge attributes when printing graphs. @@ -107,10 +108,11 @@ It is recommended, if the user works with larger graphs. Logical constant, whether igraph functions should talk more than minimal. E.g. if \code{TRUE} then some functions will use progress bars while computing. Defaults to \code{FALSE}. } -\item{vertex.attr.comb}{ +\item{vertex_attr_comb}{ Specifies what to do with the vertex attributes if the graph is modified. The default value is \code{list(name="concat", "ignore")}. -See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. +See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. The former dotted +name \code{vertex.attr.comb} still works but is soft-deprecated. } } } diff --git a/man/intersection.igraph.Rd b/man/intersection.igraph.Rd index 06b265b1188..3e024a3e311 100644 --- a/man/intersection.igraph.Rd +++ b/man/intersection.igraph.Rd @@ -9,9 +9,9 @@ ..., byname = "auto", keep.all.vertices = TRUE, - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) } \arguments{ @@ -26,7 +26,7 @@ graphs are named.} \item{keep.all.vertices}{Logical scalar, whether to keep vertices that only appear in a subset of the input graphs.} -\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for combining clashing graph, vertex and edge attributes. Each defaults to \code{"rename"}. See \link{igraph-attribute-combination} for the available combiners.} @@ -51,7 +51,7 @@ of the internal numeric vertex IDs. vertex and edge attributes are copied to the result. By default, if an attribute is present in multiple graphs and would result in a name clash, that attribute is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass -\code{graph.attr.comb}, \code{vertex.attr.comb} or \code{edge.attr.comb} to combine +\code{graph_attr_comb}, \code{vertex_attr_comb} or \code{edge_attr_comb} to combine clashing attributes instead; see \link{igraph-attribute-combination} for the available combiners. diff --git a/man/simplify.Rd b/man/simplify.Rd index bf1643497a5..6432891eed8 100644 --- a/man/simplify.Rd +++ b/man/simplify.Rd @@ -10,7 +10,8 @@ simplify( graph, remove.multiple = TRUE, remove.loops = TRUE, - edge.attr.comb = igraph_opt("edge.attr.comb") + ..., + edge_attr_comb = igraph_opt("edge_attr_comb") ) is_simple(graph) @@ -25,7 +26,9 @@ removed.} \item{remove.loops}{Logical, whether the loop edges are to be removed.} -\item{edge.attr.comb}{Specifies what to do with edge attributes, if +\item{...}{These dots are for future extensions and must be empty.} + +\item{edge_attr_comb}{Specifies what to do with edge attributes, if \code{remove.multiple=TRUE}. In this case many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details on this.} diff --git a/man/union.igraph.Rd b/man/union.igraph.Rd index c3ae729ed69..46b32510e1a 100644 --- a/man/union.igraph.Rd +++ b/man/union.igraph.Rd @@ -8,9 +8,9 @@ \method{union}{igraph}( ..., byname = "auto", - graph.attr.comb = "rename", - vertex.attr.comb = "rename", - edge.attr.comb = "rename" + graph_attr_comb = "rename", + vertex_attr_comb = "rename", + edge_attr_comb = "rename" ) } \arguments{ @@ -22,7 +22,7 @@ to perform the operation based on symbolic vertex names. If it is otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} -\item{graph.attr.comb, vertex.attr.comb, edge.attr.comb}{Specification for +\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for combining clashing graph, vertex and edge attributes. Each defaults to \code{"rename"}, which preserves the historical behaviour of appending \verb{_1}, \verb{_2}, ... suffixes. See \link{igraph-attribute-combination} for the @@ -47,8 +47,8 @@ of the internal numeric vertex IDs. \code{union()} keeps the attributes of all graphs. All graph, vertex and edge attributes are copied to the result. By default, if an attribute is present in multiple graphs and would result in a name clash, that attribute -is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass \code{graph.attr.comb}, -\code{vertex.attr.comb} or \code{edge.attr.comb} to combine clashing attributes +is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass \code{graph_attr_comb}, +\code{vertex_attr_comb} or \code{edge_attr_comb} to combine clashing attributes instead, e.g. by summing or by taking the first non-\code{NA} value. See \link{igraph-attribute-combination} for the available combiners. From 6d94668ae1d4d44dd53fc8a191c3be606aded5ed Mon Sep 17 00:00:00 2001 From: David Schoch Date: Tue, 16 Jun 2026 20:59:02 +0200 Subject: [PATCH 07/16] Add `graph_attr_comb` igraph option to control default graph attribute combination in operators The `graph_attr_comb` parameter in `union()`, `intersection()`, `disjoint_union()`, and `compose()` now defaults to `igraph_opt("graph_attr_comb")` (which is `"rename"` by default) instead of a hard-coded `"rename"`. This allows users to globally configure graph attribute combination behavior via `igraph_options()`. --- R/operators.R | 33 ++++++++++++++++++++------------- R/par.R | 8 ++++++++ tests/testthat/test-operators.R | 20 ++++++++++++++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/R/operators.R b/R/operators.R index 8363471efbe..f4397ccf6d4 100644 --- a/R/operators.R +++ b/R/operators.R @@ -290,7 +290,8 @@ apply_one_combiner <- function(comb, x) { #' @param \dots Graph objects or lists of graph objects. #' @param x,y Graph objects. #' @param graph_attr_comb Specification for combining shared graph attributes. -#' Defaults to `"rename"`, which preserves the historical behaviour of +#' Defaults to the `graph_attr_comb` igraph option (`"rename"` unless changed +#' via [igraph_options()]), which preserves the historical behaviour of #' appending `_1`, `_2`, ... suffixes to clashing attribute names. See #' [igraph-attribute-combination] for the available combiners. #' @return A new graph object. @@ -306,7 +307,7 @@ apply_one_combiner <- function(comb, x) { #' V(g2)$name <- letters[11:20] #' print_all(g1 %du% g2) #' @export -disjoint_union <- function(..., graph_attr_comb = "rename") { +disjoint_union <- function(..., graph_attr_comb = igraph_opt("graph_attr_comb")) { graphs <- unlist( recursive = FALSE, lapply(list(...), function(l) { @@ -598,10 +599,12 @@ union.default <- function(...) { #' otherwise. A warning is generated if `auto` and some (but not all) #' graphs are named. #' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. Each defaults to -#' `"rename"`, which preserves the historical behaviour of appending -#' `_1`, `_2`, ... suffixes. See [igraph-attribute-combination] for the -#' available combiners. +#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` +#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to +#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' [igraph_options()]). `"rename"` preserves the historical behaviour of +#' appending `_1`, `_2`, ... suffixes. See [igraph-attribute-combination] for +#' the available combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} #' @method union igraph @@ -620,7 +623,7 @@ union.default <- function(...) { union.igraph <- function( ..., byname = "auto", - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) { @@ -698,8 +701,10 @@ intersection <- function(...) { #' @param keep.all.vertices Logical scalar, whether to keep vertices that only #' appear in a subset of the input graphs. #' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. Each defaults to -#' `"rename"`. See [igraph-attribute-combination] for the available +#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` +#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to +#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' [igraph_options()]). See [igraph-attribute-combination] for the available #' combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} @@ -720,7 +725,7 @@ intersection.igraph <- function( ..., byname = "auto", keep.all.vertices = TRUE, - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) { @@ -946,8 +951,10 @@ complementer <- function(graph, loops = FALSE) { #' `FALSE` otherwise. A warning is generated if `auto` and one graph, #' but not both graphs are named. #' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. Each defaults to -#' `"rename"`. See [igraph-attribute-combination] for the available +#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` +#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to +#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' [igraph_options()]). See [igraph-attribute-combination] for the available #' combiners. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} @@ -966,7 +973,7 @@ compose <- function( g1, g2, byname = "auto", - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) { diff --git a/R/par.R b/R/par.R index 0c5bda1b637..531c9f791d3 100644 --- a/R/par.R +++ b/R/par.R @@ -61,6 +61,7 @@ getIgraphOpt <- function(x, default = NULL) { "print.edge.attributes" = FALSE, "print.graph.attributes" = FALSE, "verbose" = FALSE, + "graph_attr_comb" = "rename", "vertex_attr_comb" = list(name = "concat", "ignore"), "edge_attr_comb" = list(weight = "sum", name = "concat", "ignore"), "sparsematrices" = TRUE, @@ -169,6 +170,13 @@ igraph.pars.callbacks <- list("verbose" = igraph.pars.set.verbose) #' See [attribute.combination()] for details on this. The former dotted #' name `edge.attr.comb` still works but is soft-deprecated. #' } +#' \item{graph_attr_comb}{ +#' Specifies what to do with the graph attributes when graphs are +#' combined, e.g. via [union()], [intersection()], [disjoint_union()] +#' or [compose()]. The default value is `"rename"`, which resolves any +#' name clash by appending `_1`, `_2`, ... suffixes. +#' See [attribute.combination()] for details on this. +#' } #' \item{print.edge.attributes}{ #' Logical constant, whether to print edge attributes when printing graphs. #' Defaults to `FALSE`. diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index 57940d6ce66..f498c36d5bb 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -1402,6 +1402,26 @@ test_that("disjoint_union() combines graph attrs via comb", { expect_equal(u$label, c("first", "second")) }) +test_that("graph_attr_comb defaults to the graph_attr_comb igraph option", { + expect_equal(igraph_opt("graph_attr_comb"), "rename") + + g1 <- make_ring(3) + g2 <- make_ring(3) + g1$label <- "first" + g2$label <- "second" + + # Default option ("rename") preserves the historical suffixing behaviour. + u <- union(g1, g2) + expect_true(all(c("label_1", "label_2") %in% graph_attr_names(u))) + + # Setting the option changes the default for the graph operators. + local_igraph_options(graph_attr_comb = "ignore") + expect_length(graph_attr_names(union(g1, g2)), 0) + expect_length(graph_attr_names(intersection(g1, g2)), 0) + expect_length(graph_attr_names(disjoint_union(g1, g2)), 0) + expect_length(graph_attr_names(compose(g1, g2)), 0) +}) + test_that("simplify() rejects 'rename' combiner", { g <- make_graph(c(1, 2, 1, 2, 1, 2, 2, 3, 3, 4)) E(g)$weight <- 1:5 From 05336ba60f420cc09fe2c1af1e2add7e85108dd9 Mon Sep 17 00:00:00 2001 From: schochastics Date: Tue, 16 Jun 2026 19:07:44 +0000 Subject: [PATCH 08/16] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/27640941656 --- R/operators.R | 5 ++++- man/compose.Rd | 8 +++++--- man/disjoint_union.Rd | 5 +++-- man/igraph_options.Rd | 7 +++++++ man/intersection.igraph.Rd | 8 +++++--- man/union.igraph.Rd | 12 +++++++----- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/R/operators.R b/R/operators.R index f4397ccf6d4..328d9db07a1 100644 --- a/R/operators.R +++ b/R/operators.R @@ -307,7 +307,10 @@ apply_one_combiner <- function(comb, x) { #' V(g2)$name <- letters[11:20] #' print_all(g1 %du% g2) #' @export -disjoint_union <- function(..., graph_attr_comb = igraph_opt("graph_attr_comb")) { +disjoint_union <- function( + ..., + graph_attr_comb = igraph_opt("graph_attr_comb") +) { graphs <- unlist( recursive = FALSE, lapply(list(...), function(l) { diff --git a/man/compose.Rd b/man/compose.Rd index 85393805d31..56a6377af3d 100644 --- a/man/compose.Rd +++ b/man/compose.Rd @@ -9,7 +9,7 @@ compose( g1, g2, byname = "auto", - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) @@ -26,8 +26,10 @@ to perform the operation based on symbolic vertex names. If it is but not both graphs are named.} \item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. Each defaults to -\code{"rename"}. See \link{igraph-attribute-combination} for the available +combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} +and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to +the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\code{\link[=igraph_options]{igraph_options()}}). See \link{igraph-attribute-combination} for the available combiners.} } \value{ diff --git a/man/disjoint_union.Rd b/man/disjoint_union.Rd index ad13fdc09ce..9d9f7ab4937 100644 --- a/man/disjoint_union.Rd +++ b/man/disjoint_union.Rd @@ -5,7 +5,7 @@ \alias{\%du\%} \title{Disjoint union of graphs} \usage{ -disjoint_union(..., graph_attr_comb = "rename") +disjoint_union(..., graph_attr_comb = igraph_opt("graph_attr_comb")) x \%du\% y } @@ -13,7 +13,8 @@ x \%du\% y \item{\dots}{Graph objects or lists of graph objects.} \item{graph_attr_comb}{Specification for combining shared graph attributes. -Defaults to \code{"rename"}, which preserves the historical behaviour of +Defaults to the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed +via \code{\link[=igraph_options]{igraph_options()}}), which preserves the historical behaviour of appending \verb{_1}, \verb{_2}, ... suffixes to clashing attribute names. See \link{igraph-attribute-combination} for the available combiners.} diff --git a/man/igraph_options.Rd b/man/igraph_options.Rd index 55313ffc55d..e69b713d10d 100644 --- a/man/igraph_options.Rd +++ b/man/igraph_options.Rd @@ -71,6 +71,13 @@ The default value is \code{list(weight="sum", name="concat", "ignore")}. See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. The former dotted name \code{edge.attr.comb} still works but is soft-deprecated. } +\item{graph_attr_comb}{ +Specifies what to do with the graph attributes when graphs are +combined, e.g. via \code{\link[=union]{union()}}, \code{\link[=intersection]{intersection()}}, \code{\link[=disjoint_union]{disjoint_union()}} +or \code{\link[=compose]{compose()}}. The default value is \code{"rename"}, which resolves any +name clash by appending \verb{_1}, \verb{_2}, ... suffixes. +See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. +} \item{print.edge.attributes}{ Logical constant, whether to print edge attributes when printing graphs. Defaults to \code{FALSE}. diff --git a/man/intersection.igraph.Rd b/man/intersection.igraph.Rd index 3e024a3e311..bb9d3324ce9 100644 --- a/man/intersection.igraph.Rd +++ b/man/intersection.igraph.Rd @@ -9,7 +9,7 @@ ..., byname = "auto", keep.all.vertices = TRUE, - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) @@ -27,8 +27,10 @@ graphs are named.} appear in a subset of the input graphs.} \item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. Each defaults to -\code{"rename"}. See \link{igraph-attribute-combination} for the available +combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} +and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to +the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\code{\link[=igraph_options]{igraph_options()}}). See \link{igraph-attribute-combination} for the available combiners.} } \value{ diff --git a/man/union.igraph.Rd b/man/union.igraph.Rd index 46b32510e1a..032d009d425 100644 --- a/man/union.igraph.Rd +++ b/man/union.igraph.Rd @@ -8,7 +8,7 @@ \method{union}{igraph}( ..., byname = "auto", - graph_attr_comb = "rename", + graph_attr_comb = igraph_opt("graph_attr_comb"), vertex_attr_comb = "rename", edge_attr_comb = "rename" ) @@ -23,10 +23,12 @@ otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} \item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. Each defaults to -\code{"rename"}, which preserves the historical behaviour of appending -\verb{_1}, \verb{_2}, ... suffixes. See \link{igraph-attribute-combination} for the -available combiners.} +combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} +and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to +the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\code{\link[=igraph_options]{igraph_options()}}). \code{"rename"} preserves the historical behaviour of +appending \verb{_1}, \verb{_2}, ... suffixes. See \link{igraph-attribute-combination} for +the available combiners.} } \value{ A new graph object. From a85a78c576a50706d694dcbf326996cb5e03380a Mon Sep 17 00:00:00 2001 From: David Schoch Date: Tue, 16 Jun 2026 21:26:29 +0200 Subject: [PATCH 09/16] rename *_comb to *_combine --- R/aaa-operators.R | 4 +- R/aaa-structural.R | 2 +- R/attributes.R | 12 +-- R/community.R | 20 ++--- R/conversion.R | 18 ++-- R/operators.R | 128 ++++++++++++++-------------- R/par.R | 16 ++-- R/simple.R | 18 ++-- R/structural-properties.R | 2 +- tests/testthat/_snaps/conversion.md | 4 +- tests/testthat/test-attributes.R | 4 +- tests/testthat/test-community.R | 2 +- tests/testthat/test-operators.R | 26 +++--- tools/migrations.R | 12 +-- tools/stimulus/types-RR.yaml | 4 +- 15 files changed, 136 insertions(+), 136 deletions(-) diff --git a/R/aaa-operators.R b/R/aaa-operators.R index e927c6ee25d..94994feeaad 100644 --- a/R/aaa-operators.R +++ b/R/aaa-operators.R @@ -35,7 +35,7 @@ connect_neighborhood_impl <- function( contract_vertices_impl <- function( graph, mapping, - vertex_attr_comb = igraph_opt("vertex_attr_comb") + vertex_attr_comb = igraph_opt("vertex_attr_combine") ) { # Argument checks ensure_igraph(graph) @@ -245,7 +245,7 @@ simplify_impl <- function( graph, remove_multiple = TRUE, remove_loops = TRUE, - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_comb = igraph_opt("edge_attr_combine") ) { # Argument checks ensure_igraph(graph) diff --git a/R/aaa-structural.R b/R/aaa-structural.R index ef382c7dffd..21d3795e887 100644 --- a/R/aaa-structural.R +++ b/R/aaa-structural.R @@ -166,7 +166,7 @@ to_directed_impl <- function( to_undirected_impl <- function( graph, mode = c("collapse", "each", "mutual"), - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_comb = igraph_opt("edge_attr_combine") ) { # Argument checks ensure_igraph(graph) diff --git a/R/attributes.R b/R/attributes.R index a413190b8c4..480a5a04377 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1359,7 +1359,7 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { #' vertex/edge attributes in these cases. #' #' The functions that support the combination of attributes have one or two -#' extra arguments called `vertex.attr.comb` and/or `edge.attr.comb` +#' extra arguments called `vertex_attr_combine` and/or `edge_attr_combine` #' that specify how to perform the mapping of the attributes. E.g. #' [contract()] contracts many vertices into a single one, the #' attributes of the vertices can be combined and stores as the vertex @@ -1467,22 +1467,22 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { #' igraph_options(print.edge.attributes = TRUE) #' #' ## new attribute is the sum of the old ones -#' simplify(g, edge.attr.comb = "sum") +#' simplify(g, edge_attr_combine = "sum") #' #' ## collect attributes into a string -#' simplify(g, edge.attr.comb = toString) +#' simplify(g, edge_attr_combine = toString) #' #' ## concatenate them into a vector, this creates a complex #' ## attribute -#' simplify(g, edge.attr.comb = "concat") +#' simplify(g, edge_attr_combine = "concat") #' #' E(g)$name <- letters[seq_len(ecount(g))] #' #' ## both attributes are collected into strings -#' simplify(g, edge.attr.comb = toString) +#' simplify(g, edge_attr_combine = toString) #' #' ## harmonic average of weights, names are dropped -#' simplify(g, edge.attr.comb = list( +#' simplify(g, edge_attr_combine = list( #' weight = function(x) length(x) / sum(1 / x), #' name = "ignore" #' )) diff --git a/R/community.R b/R/community.R index b3b958a7851..a12e2d46f80 100644 --- a/R/community.R +++ b/R/community.R @@ -3201,14 +3201,14 @@ communities <- groups.communities #' #' The attributes of the graph are kept. Graph and edge attributes are #' unchanged, vertex attributes are combined, according to the -#' `vertex_attr_comb` parameter. +#' `vertex_attr_combine` parameter. #' #' @param graph The input graph, it can be directed or undirected. #' @param mapping A numeric vector that specifies the mapping. Its elements #' correspond to the vertices, and for each element the ID in the new graph is #' given. #' @inheritParams rlang::args_dots_empty -#' @param vertex_attr_comb Specifies how to combine the vertex attributes in +#' @param vertex_attr_combine Specifies how to combine the vertex attributes in #' the new graph. Please see [attribute.combination()] for details. #' @return A new graph object. #' @author Gabor Csardi \email{csardi.gabor@@gmail.com} @@ -3221,7 +3221,7 @@ communities <- groups.communities #' E(g)$weight <- runif(ecount(g)) #' #' g2 <- contract(g, rep(1:5, each = 2), -#' vertex_attr_comb = toString +#' vertex_attr_combine = toString #' ) #' #' ## graph and edge attributes are kept, vertex attributes are @@ -3234,18 +3234,18 @@ contract <- function( graph, mapping, ..., - vertex_attr_comb = igraph_opt("vertex_attr_comb") + vertex_attr_combine = igraph_opt("vertex_attr_combine") ) { # BEGIN GENERATED ARG_HANDLE: contract, do not edit, see tools/generate-migrations.R if (...length() > 0L) { .arg_handle <- migrate_recover_args( list(...), - current = list(vertex_attr_comb = vertex_attr_comb), - recover_new = c("vertex_attr_comb"), + current = list(vertex_attr_combine = vertex_attr_combine), + recover_new = c("vertex_attr_combine"), recover_old = c("vertex.attr.comb"), - match_names = c("vertex.attr.comb", "vertex_attr_comb"), - match_to = c("vertex_attr_comb", "vertex_attr_comb"), - defaults = list(vertex_attr_comb = igraph_opt("vertex_attr_comb")), + match_names = c("vertex.attr.comb", "vertex_attr_combine"), + match_to = c("vertex_attr_combine", "vertex_attr_combine"), + defaults = list(vertex_attr_combine = igraph_opt("vertex_attr_combine")), head_args = c("graph", "mapping"), fn_name = "contract" ) @@ -3261,7 +3261,7 @@ contract <- function( contract_vertices_impl( graph = graph, mapping = mapping, - vertex_attr_comb = vertex_attr_comb + vertex_attr_comb = vertex_attr_combine ) } diff --git a/R/conversion.R b/R/conversion.R index 0e30fc831db..1b3ddce83f1 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -670,7 +670,7 @@ as_edgelist <- function(graph, names = TRUE) { #' E(g4)$weight <- seq_len(ecount(g4)) #' ug4 <- as_undirected(g4, #' mode = "mutual", -#' edge_attr_comb = list(weight = length) +#' edge_attr_combine = list(weight = length) #' ) #' print(ug4, e = TRUE) #' @@ -686,7 +686,7 @@ as_directed <- function( #' @rdname as_directed #' @inheritParams rlang::args_dots_empty -#' @param edge_attr_comb Specifies what to do with edge attributes, if +#' @param edge_attr_combine Specifies what to do with edge attributes, if #' `mode="collapse"` or `mode="mutual"`. In these cases many edges #' might be mapped to a single one in the new graph, and their attributes are #' combined. Please see [attribute.combination()] for details on @@ -696,18 +696,18 @@ as_undirected <- function( graph, mode = c("collapse", "each", "mutual"), ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) { # BEGIN GENERATED ARG_HANDLE: as_undirected, do not edit, see tools/generate-migrations.R if (...length() > 0L) { .arg_handle <- migrate_recover_args( list(...), - current = list(edge_attr_comb = edge_attr_comb), - recover_new = c("edge_attr_comb"), + current = list(edge_attr_combine = edge_attr_combine), + recover_new = c("edge_attr_combine"), recover_old = c("edge.attr.comb"), - match_names = c("edge.attr.comb", "edge_attr_comb"), - match_to = c("edge_attr_comb", "edge_attr_comb"), - defaults = list(edge_attr_comb = igraph_opt("edge_attr_comb")), + match_names = c("edge.attr.comb", "edge_attr_combine"), + match_to = c("edge_attr_combine", "edge_attr_combine"), + defaults = list(edge_attr_combine = igraph_opt("edge_attr_combine")), head_args = c("graph", "mode"), fn_name = "as_undirected" ) @@ -728,7 +728,7 @@ as_undirected <- function( res <- to_undirected_impl( graph = graph, mode = mode, - edge_attr_comb = edge_attr_comb + edge_attr_comb = edge_attr_combine ) res diff --git a/R/operators.R b/R/operators.R index 328d9db07a1..9e69759a7ef 100644 --- a/R/operators.R +++ b/R/operators.R @@ -275,7 +275,7 @@ apply_one_combiner <- function(comb, x) { #' function. For graphs that lack some vertex/edge attribute, the corresponding #' values in the new graph are set to a missing value (`NA` for scalar attributes, #' `NULL` for list attributes). Graph attributes are combined according to -#' `graph_attr_comb`; by default any name clash is resolved by adding +#' `graph_attr_combine`; by default any name clash is resolved by adding #' suffixes (`_1`, `_2`, ...). See [igraph-attribute-combination] for the #' available combiners. #' @@ -289,8 +289,8 @@ apply_one_combiner <- function(comb, x) { #' @aliases %du% #' @param \dots Graph objects or lists of graph objects. #' @param x,y Graph objects. -#' @param graph_attr_comb Specification for combining shared graph attributes. -#' Defaults to the `graph_attr_comb` igraph option (`"rename"` unless changed +#' @param graph_attr_combine Specification for combining shared graph attributes. +#' Defaults to the `graph_attr_combine` igraph option (`"rename"` unless changed #' via [igraph_options()]), which preserves the historical behaviour of #' appending `_1`, `_2`, ... suffixes to clashing attribute names. See #' [igraph-attribute-combination] for the available combiners. @@ -309,7 +309,7 @@ apply_one_combiner <- function(comb, x) { #' @export disjoint_union <- function( ..., - graph_attr_comb = igraph_opt("graph_attr_comb") + graph_attr_combine = igraph_opt("graph_attr_combine") ) { graphs <- unlist( recursive = FALSE, @@ -323,11 +323,11 @@ disjoint_union <- function( res <- .Call(Rx_igraph_disjoint_union, graphs) ## Graph attributes - graph_attr_comb <- igraph.i.attribute.combination( - graph_attr_comb, + graph_attr_combine <- igraph.i.attribute.combination( + graph_attr_combine, allow_rename = TRUE ) - graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_comb) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_combine) ## Vertex attributes attr <- list() @@ -402,9 +402,9 @@ disjoint_union <- function( ..., byname, keep.all.vertices, - graph_attr_comb = "rename", - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = "rename", + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) { graphs <- unlist( recursive = FALSE, @@ -428,16 +428,16 @@ disjoint_union <- function( cli::cli_abort("Some graphs are not named.") } - graph_attr_comb <- igraph.i.attribute.combination( - graph_attr_comb, + graph_attr_combine <- igraph.i.attribute.combination( + graph_attr_combine, allow_rename = TRUE ) - vertex_attr_comb <- igraph.i.attribute.combination( - vertex_attr_comb, + vertex_attr_combine <- igraph.i.attribute.combination( + vertex_attr_combine, allow_rename = TRUE ) - edge_attr_comb <- igraph.i.attribute.combination( - edge_attr_comb, + edge_attr_combine <- igraph.i.attribute.combination( + edge_attr_combine, allow_rename = TRUE ) @@ -471,14 +471,14 @@ disjoint_union <- function( graph.attributes(res) <- combine.attrs( "g", newgraphs, - comb = graph_attr_comb + comb = graph_attr_combine ) vertex.attributes(res) <- combine.attrs( "v", newgraphs, vcount(res), ignore = "name", - comb = vertex_attr_comb + comb = vertex_attr_combine ) V(res)$name <- uninames @@ -489,7 +489,7 @@ disjoint_union <- function( newgraphs, ecount(res), maps = maps, - comb = edge_attr_comb + comb = edge_attr_combine ) } } else { @@ -516,13 +516,13 @@ disjoint_union <- function( graph.attributes(res) <- combine.attrs( "g", graphs, - comb = graph_attr_comb + comb = graph_attr_combine ) vertex.attributes(res) <- combine.attrs( "v", graphs, vcount(res), - comb = vertex_attr_comb + comb = vertex_attr_combine ) ## Edges are a bit more difficult, we need a mapping @@ -532,7 +532,7 @@ disjoint_union <- function( graphs, ecount(res), maps = maps, - comb = edge_attr_comb + comb = edge_attr_combine ) } } @@ -582,8 +582,8 @@ union.default <- function(...) { #' `union()` keeps the attributes of all graphs. All graph, vertex and #' edge attributes are copied to the result. By default, if an attribute is #' present in multiple graphs and would result in a name clash, that attribute -#' is renamed by adding suffixes: `_1`, `_2`, etc. Pass `graph_attr_comb`, -#' `vertex_attr_comb` or `edge_attr_comb` to combine clashing attributes +#' is renamed by adding suffixes: `_1`, `_2`, etc. Pass `graph_attr_combine`, +#' `vertex_attr_combine` or `edge_attr_combine` to combine clashing attributes #' instead, e.g. by summing or by taking the first non-`NA` value. See #' [igraph-attribute-combination] for the available combiners. #' @@ -601,10 +601,10 @@ union.default <- function(...) { #' `auto`, that means `TRUE` if all graphs are named and `FALSE` #' otherwise. A warning is generated if `auto` and some (but not all) #' graphs are named. -#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` -#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to -#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' @param graph_attr_combine,vertex_attr_combine,edge_attr_combine Specification for +#' combining clashing graph, vertex and edge attributes. `vertex_attr_combine` +#' and `edge_attr_combine` default to `"rename"`; `graph_attr_combine` defaults to +#' the `graph_attr_combine` igraph option (`"rename"` unless changed via #' [igraph_options()]). `"rename"` preserves the historical behaviour of #' appending `_1`, `_2`, ... suffixes. See [igraph-attribute-combination] for #' the available combiners. @@ -626,18 +626,18 @@ union.default <- function(...) { union.igraph <- function( ..., byname = "auto", - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) { .igraph.graph.union.or.intersection( "union", ..., byname = byname, keep.all.vertices = TRUE, - graph_attr_comb = graph_attr_comb, - vertex_attr_comb = vertex_attr_comb, - edge_attr_comb = edge_attr_comb + graph_attr_combine = graph_attr_combine, + vertex_attr_combine = vertex_attr_combine, + edge_attr_combine = edge_attr_combine ) } @@ -683,7 +683,7 @@ intersection <- function(...) { #' vertex and edge attributes are copied to the result. By default, if an #' attribute is present in multiple graphs and would result in a name clash, #' that attribute is renamed by adding suffixes: `_1`, `_2`, etc. Pass -#' `graph_attr_comb`, `vertex_attr_comb` or `edge_attr_comb` to combine +#' `graph_attr_combine`, `vertex_attr_combine` or `edge_attr_combine` to combine #' clashing attributes instead; see [igraph-attribute-combination] for the #' available combiners. #' @@ -703,10 +703,10 @@ intersection <- function(...) { #' graphs are named. #' @param keep.all.vertices Logical scalar, whether to keep vertices that only #' appear in a subset of the input graphs. -#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` -#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to -#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' @param graph_attr_combine,vertex_attr_combine,edge_attr_combine Specification for +#' combining clashing graph, vertex and edge attributes. `vertex_attr_combine` +#' and `edge_attr_combine` default to `"rename"`; `graph_attr_combine` defaults to +#' the `graph_attr_combine` igraph option (`"rename"` unless changed via #' [igraph_options()]). See [igraph-attribute-combination] for the available #' combiners. #' @return A new graph object. @@ -728,18 +728,18 @@ intersection.igraph <- function( ..., byname = "auto", keep.all.vertices = TRUE, - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) { .igraph.graph.union.or.intersection( "intersection", ..., byname = byname, keep.all.vertices = keep.all.vertices, - graph_attr_comb = graph_attr_comb, - vertex_attr_comb = vertex_attr_comb, - edge_attr_comb = edge_attr_comb + graph_attr_combine = graph_attr_combine, + vertex_attr_combine = vertex_attr_combine, + edge_attr_combine = edge_attr_combine ) } @@ -922,8 +922,8 @@ complementer <- function(graph, loops = FALSE) { #' `compose()` keeps the attributes of both graphs. All graph, vertex #' and edge attributes are copied to the result. By default, if an attribute #' is present in both graphs and would result in a name clash, that attribute -#' is renamed by adding suffixes: `_1`, `_2`. Pass `graph_attr_comb`, -#' `vertex_attr_comb` or `edge_attr_comb` to combine clashing attributes +#' is renamed by adding suffixes: `_1`, `_2`. Pass `graph_attr_combine`, +#' `vertex_attr_combine` or `edge_attr_combine` to combine clashing attributes #' instead; see [igraph-attribute-combination] for the available combiners. #' #' The `name` vertex attribute is treated specially if the operation is @@ -953,10 +953,10 @@ complementer <- function(graph, loops = FALSE) { #' `auto`, that means `TRUE` if both graphs are named and #' `FALSE` otherwise. A warning is generated if `auto` and one graph, #' but not both graphs are named. -#' @param graph_attr_comb,vertex_attr_comb,edge_attr_comb Specification for -#' combining clashing graph, vertex and edge attributes. `vertex_attr_comb` -#' and `edge_attr_comb` default to `"rename"`; `graph_attr_comb` defaults to -#' the `graph_attr_comb` igraph option (`"rename"` unless changed via +#' @param graph_attr_combine,vertex_attr_combine,edge_attr_combine Specification for +#' combining clashing graph, vertex and edge attributes. `vertex_attr_combine` +#' and `edge_attr_combine` default to `"rename"`; `graph_attr_combine` defaults to +#' the `graph_attr_combine` igraph option (`"rename"` unless changed via #' [igraph_options()]). See [igraph-attribute-combination] for the available #' combiners. #' @return A new graph object. @@ -976,9 +976,9 @@ compose <- function( g1, g2, byname = "auto", - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) { ensure_igraph(g1) ensure_igraph(g2) @@ -998,16 +998,16 @@ compose <- function( cli::cli_abort("Some graphs are not named.") } - graph_attr_comb <- igraph.i.attribute.combination( - graph_attr_comb, + graph_attr_combine <- igraph.i.attribute.combination( + graph_attr_combine, allow_rename = TRUE ) - vertex_attr_comb <- igraph.i.attribute.combination( - vertex_attr_comb, + vertex_attr_combine <- igraph.i.attribute.combination( + vertex_attr_combine, allow_rename = TRUE ) - edge_attr_comb <- igraph.i.attribute.combination( - edge_attr_comb, + edge_attr_combine <- igraph.i.attribute.combination( + edge_attr_combine, allow_rename = TRUE ) @@ -1036,7 +1036,7 @@ compose <- function( res <- res$graph graphs <- list(g1, g2) - graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_comb) + graph.attributes(res) <- combine.attrs("g", graphs, comb = graph_attr_combine) if (byname) { vertex.attributes(res) <- combine.attrs( @@ -1044,7 +1044,7 @@ compose <- function( graphs, vcount(res), ignore = "name", - comb = vertex_attr_comb + comb = vertex_attr_combine ) V(res)$name <- uninames } else { @@ -1052,7 +1052,7 @@ compose <- function( "v", graphs, vcount(res), - comb = vertex_attr_comb + comb = vertex_attr_combine ) } @@ -1062,7 +1062,7 @@ compose <- function( graphs, ecount(res), maps2 = maps, - comb = edge_attr_comb + comb = edge_attr_combine ) } diff --git a/R/par.R b/R/par.R index 531c9f791d3..1a5beadd02c 100644 --- a/R/par.R +++ b/R/par.R @@ -61,9 +61,9 @@ getIgraphOpt <- function(x, default = NULL) { "print.edge.attributes" = FALSE, "print.graph.attributes" = FALSE, "verbose" = FALSE, - "graph_attr_comb" = "rename", - "vertex_attr_comb" = list(name = "concat", "ignore"), - "edge_attr_comb" = list(weight = "sum", name = "concat", "ignore"), + "graph_attr_combine" = "rename", + "vertex_attr_combine" = list(name = "concat", "ignore"), + "edge_attr_combine" = list(weight = "sum", name = "concat", "ignore"), "sparsematrices" = TRUE, "add.params" = TRUE, "add.vertex.names" = TRUE, @@ -80,8 +80,8 @@ getIgraphOpt <- function(x, default = NULL) { # its old dotted name still works, but maps to the canonical key and emits a # soft-deprecation. .igraph.pars.aliases <- c( - "vertex.attr.comb" = "vertex_attr_comb", - "edge.attr.comb" = "edge_attr_comb" + "vertex.attr.comb" = "vertex_attr_combine", + "edge.attr.comb" = "edge_attr_combine" ) # Map any deprecated option names in `x` to their canonical keys, warning once @@ -164,13 +164,13 @@ igraph.pars.callbacks <- list("verbose" = igraph.pars.set.verbose) #' Possible values are \sQuote{auto} (the default), \sQuote{phylo}, \sQuote{hclust} and \sQuote{dendrogram}. #' See [plot_dendrogram()] for details. #' } -#' \item{edge_attr_comb}{ +#' \item{edge_attr_combine}{ #' Specifies what to do with the edge attributes if the graph is modified. #' The default value is `list(weight="sum", name="concat", "ignore")`. #' See [attribute.combination()] for details on this. The former dotted #' name `edge.attr.comb` still works but is soft-deprecated. #' } -#' \item{graph_attr_comb}{ +#' \item{graph_attr_combine}{ #' Specifies what to do with the graph attributes when graphs are #' combined, e.g. via [union()], [intersection()], [disjoint_union()] #' or [compose()]. The default value is `"rename"`, which resolves any @@ -214,7 +214,7 @@ igraph.pars.callbacks <- list("verbose" = igraph.pars.set.verbose) #' Logical constant, whether igraph functions should talk more than minimal. #' E.g. if `TRUE` then some functions will use progress bars while computing. Defaults to `FALSE`. #' } -#' \item{vertex_attr_comb}{ +#' \item{vertex_attr_combine}{ #' Specifies what to do with the vertex attributes if the graph is modified. #' The default value is `list(name="concat", "ignore")`. #' See [attribute.combination()] for details on this. The former dotted diff --git a/R/simple.R b/R/simple.R index cc1dbb0d1e6..5b1680a7edd 100644 --- a/R/simple.R +++ b/R/simple.R @@ -69,7 +69,7 @@ is.simple <- function(graph) { #' @param remove.multiple Logical, whether the multiple edges are to be #' removed. #' @inheritParams rlang::args_dots_empty -#' @param edge_attr_comb Specifies what to do with edge attributes, if +#' @param edge_attr_combine Specifies what to do with edge attributes, if #' `remove.multiple=TRUE`. In this case many edges might be mapped to a #' single one in the new graph, and their attributes are combined. Please see #' [attribute.combination()] for details on this. @@ -96,18 +96,18 @@ simplify <- function( remove.multiple = TRUE, remove.loops = TRUE, ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) { # BEGIN GENERATED ARG_HANDLE: simplify, do not edit, see tools/generate-migrations.R if (...length() > 0L) { .arg_handle <- migrate_recover_args( list(...), - current = list(edge_attr_comb = edge_attr_comb), - recover_new = c("edge_attr_comb"), + current = list(edge_attr_combine = edge_attr_combine), + recover_new = c("edge_attr_combine"), recover_old = c("edge.attr.comb"), - match_names = c("edge.attr.comb", "edge_attr_comb"), - match_to = c("edge_attr_comb", "edge_attr_comb"), - defaults = list(edge_attr_comb = igraph_opt("edge_attr_comb")), + match_names = c("edge.attr.comb", "edge_attr_combine"), + match_to = c("edge_attr_combine", "edge_attr_combine"), + defaults = list(edge_attr_combine = igraph_opt("edge_attr_combine")), head_args = c("graph", "remove.multiple", "remove.loops"), fn_name = "simplify" ) @@ -122,7 +122,7 @@ simplify <- function( # A graph that is already simple has no loops and no multiple edges, so # simplify_impl() would not change its structure regardless of the - # remove.* / edge_attr_comb arguments. Short-circuiting here avoids the + # remove.* / edge_attr_combine arguments. Short-circuiting here avoids the # cost of rebuilding the graph in the (common) already-simple case; # is_simple() is orders of magnitude cheaper than simplify(). if (is_simple(graph)) { @@ -132,7 +132,7 @@ simplify <- function( graph = graph, remove_multiple = remove.multiple, remove_loops = remove.loops, - edge_attr_comb = edge_attr_comb + edge_attr_comb = edge_attr_combine ) } diff --git a/R/structural-properties.R b/R/structural-properties.R index 9182950592e..5dacd27cb24 100644 --- a/R/structural-properties.R +++ b/R/structural-properties.R @@ -2517,7 +2517,7 @@ girth <- function(graph, circle = TRUE) { #' # Remove multiple edges but keep multiplicity #' g <- sample_pa(10, m = 3, algorithm = "bag") #' E(g)$weight <- count_multiple(g) -#' g <- simplify(g, edge.attr.comb = list(weight = "min")) +#' g <- simplify(g, edge_attr_combine = list(weight = "min")) #' any(which_multiple(g)) #' E(g)$weight #' diff --git a/tests/testthat/_snaps/conversion.md b/tests/testthat/_snaps/conversion.md index f84ab5f63c5..7e39ca56ba6 100644 --- a/tests/testthat/_snaps/conversion.md +++ b/tests/testthat/_snaps/conversion.md @@ -19,11 +19,11 @@ i Please use `as_undirected()` instead. Warning: The igraph option `edge.attr.comb` was deprecated in igraph 3.0.0. - i Please use the `edge_attr_comb` option instead. + i Please use the `edge_attr_combine` option instead. Warning: Calling `as_undirected()` with positional or abbreviated arguments was deprecated in igraph 3.0.0. i Detected call: as_undirected(graph, mode, edge.attr.comb) - i Use instead: as_undirected(graph, mode, edge_attr_comb = ) + i Use instead: as_undirected(graph, mode, edge_attr_combine = ) Output [1] FALSE diff --git a/tests/testthat/test-attributes.R b/tests/testthat/test-attributes.R index 213cb081b18..ab39eec359b 100644 --- a/tests/testthat/test-attributes.R +++ b/tests/testthat/test-attributes.R @@ -208,11 +208,11 @@ test_that("attribute combinations handle errors correctly", { g <- make_graph(c(1, 2, 2, 1)) E(g)$weight <- c("a", "b") expect_error( - as_undirected(g, edge_attr_comb = list(weight = "sum")), + as_undirected(g, edge_attr_combine = list(weight = "sum")), "invalid 'type'" ) expect_error( - as_undirected(g, edge_attr_comb = list(weight = sum)), + as_undirected(g, edge_attr_combine = list(weight = sum)), "invalid 'type'" ) }) diff --git a/tests/testthat/test-community.R b/tests/testthat/test-community.R index 22fffc2e118..b761c608615 100644 --- a/tests/testthat/test-community.R +++ b/tests/testthat/test-community.R @@ -643,7 +643,7 @@ test_that("contract works", { V(g)$name <- letters[1:vcount(g)] E(g)$weight <- sample(ecount(g)) - g2 <- contract(g, rep(1:5, each = 2), vertex_attr_comb = toString) + g2 <- contract(g, rep(1:5, each = 2), vertex_attr_combine = toString) expect_equal(g2$name, g$name) expect_equal(V(g2)$name, c("a, b", "c, d", "e, f", "g, h", "i, j")) diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index f498c36d5bb..ed2d3872e63 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -1323,14 +1323,14 @@ test_that("union() defaults to rename behaviour", { test_that("union() combines vertex attributes with sum", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, vertex_attr_comb = "sum") + u <- union(gs$g1, gs$g2, vertex_attr_combine = "sum") expect_setequal(vertex_attr_names(u), c("name", "weight")) expect_equal(sort(V(u)$weight), sort(c(11, 22, 33))) }) test_that("union() combines edge attributes with sum", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, edge_attr_comb = "sum") + u <- union(gs$g1, gs$g2, edge_attr_combine = "sum") expect_setequal(edge_attr_names(u), c("weight")) expect_equal(sort(E(u)$weight), sort(c(11, 22, 33))) }) @@ -1342,7 +1342,7 @@ test_that("union() honours per-attribute list spec with rename fallback", { u <- union( gs$g1, gs$g2, - vertex_attr_comb = list(weight = "sum", "rename") + vertex_attr_combine = list(weight = "sum", "rename") ) expect_setequal( vertex_attr_names(u), @@ -1352,7 +1352,7 @@ test_that("union() honours per-attribute list spec with rename fallback", { test_that("union() can drop clashing attributes with ignore", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, edge_attr_comb = "ignore") + u <- union(gs$g1, gs$g2, edge_attr_combine = "ignore") expect_length(edge_attr_names(u), 0) }) @@ -1361,14 +1361,14 @@ test_that("union() supports custom function combiner", { u <- union( gs$g1, gs$g2, - vertex_attr_comb = list(weight = function(x) mean(x)) + vertex_attr_combine = list(weight = function(x) mean(x)) ) expect_equal(sort(V(u)$weight), sort(c(5.5, 11, 16.5))) }) test_that("union() picks first non-NA when only one input has the attr", { gs <- make_named_pair() - u <- union(gs$g1, gs$g2, vertex_attr_comb = "first", byname = TRUE) + u <- union(gs$g1, gs$g2, vertex_attr_combine = "first", byname = TRUE) expect_setequal(vertex_attr_names(u), c("name", "weight")) expect_equal( V(u)$weight[match(c("A", "B", "C"), V(u)$name)], @@ -1378,7 +1378,7 @@ test_that("union() picks first non-NA when only one input has the attr", { test_that("intersection() takes attr.comb args", { gs <- make_named_pair() - i <- intersection(gs$g1, gs$g2, edge_attr_comb = "sum") + i <- intersection(gs$g1, gs$g2, edge_attr_combine = "sum") expect_setequal(edge_attr_names(i), c("weight")) expect_equal(sort(E(i)$weight), sort(c(11, 22, 33))) }) @@ -1388,7 +1388,7 @@ test_that("compose() takes attr.comb args", { g2 <- graph_from_literal(A - B - E - A) V(g1)$foo <- seq_len(vcount(g1)) V(g2)$foo <- 10 * seq_len(vcount(g2)) - g <- compose(g1, g2, vertex_attr_comb = "sum") + g <- compose(g1, g2, vertex_attr_combine = "sum") expect_true("foo" %in% vertex_attr_names(g)) expect_false("foo_1" %in% vertex_attr_names(g)) }) @@ -1398,12 +1398,12 @@ test_that("disjoint_union() combines graph attrs via comb", { g2 <- make_ring(3) g1$label <- "first" g2$label <- "second" - u <- disjoint_union(g1, g2, graph_attr_comb = "concat") + u <- disjoint_union(g1, g2, graph_attr_combine = "concat") expect_equal(u$label, c("first", "second")) }) -test_that("graph_attr_comb defaults to the graph_attr_comb igraph option", { - expect_equal(igraph_opt("graph_attr_comb"), "rename") +test_that("graph_attr_combine defaults to the graph_attr_combine igraph option", { + expect_equal(igraph_opt("graph_attr_combine"), "rename") g1 <- make_ring(3) g2 <- make_ring(3) @@ -1415,7 +1415,7 @@ test_that("graph_attr_comb defaults to the graph_attr_comb igraph option", { expect_true(all(c("label_1", "label_2") %in% graph_attr_names(u))) # Setting the option changes the default for the graph operators. - local_igraph_options(graph_attr_comb = "ignore") + local_igraph_options(graph_attr_combine = "ignore") expect_length(graph_attr_names(union(g1, g2)), 0) expect_length(graph_attr_names(intersection(g1, g2)), 0) expect_length(graph_attr_names(disjoint_union(g1, g2)), 0) @@ -1426,7 +1426,7 @@ test_that("simplify() rejects 'rename' combiner", { g <- make_graph(c(1, 2, 1, 2, 1, 2, 2, 3, 3, 4)) E(g)$weight <- 1:5 expect_error( - simplify(g, edge_attr_comb = "rename"), + simplify(g, edge_attr_combine = "rename"), "rename" ) }) diff --git a/tools/migrations.R b/tools/migrations.R index b45abc34cab..a9afe932e2f 100644 --- a/tools/migrations.R +++ b/tools/migrations.R @@ -103,34 +103,34 @@ migrations <- list( graph, remove.multiple, remove.loops, - edge.attr.comb = edge_attr_comb + edge.attr.comb = edge_attr_combine ) {}, new = function( graph, remove.multiple = TRUE, remove.loops = TRUE, ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) {}, when = "3.0.0" ), as_undirected = list( - old = function(graph, mode, edge.attr.comb = edge_attr_comb) {}, + old = function(graph, mode, edge.attr.comb = edge_attr_combine) {}, new = function( graph, mode = c("collapse", "each", "mutual"), ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) {}, when = "3.0.0" ), contract = list( - old = function(graph, mapping, vertex.attr.comb = vertex_attr_comb) {}, + old = function(graph, mapping, vertex.attr.comb = vertex_attr_combine) {}, new = function( graph, mapping, ..., - vertex_attr_comb = igraph_opt("vertex_attr_comb") + vertex_attr_combine = igraph_opt("vertex_attr_combine") ) {}, when = "3.0.0" ), diff --git a/tools/stimulus/types-RR.yaml b/tools/stimulus/types-RR.yaml index 140db96a776..0594b749682 100644 --- a/tools/stimulus/types-RR.yaml +++ b/tools/stimulus/types-RR.yaml @@ -531,12 +531,12 @@ SUBGRAPH_IMPL: EDGE_ATTRIBUTE_COMBINATION: DEFAULT: - Default: igraph_opt("edge_attr_comb") + Default: igraph_opt("edge_attr_combine") INCONV: '%I% <- igraph.i.attribute.combination(%I%)' VERTEX_ATTRIBUTE_COMBINATION: DEFAULT: - Default: igraph_opt("vertex_attr_comb") + Default: igraph_opt("vertex_attr_combine") INCONV: '%I% <- igraph.i.attribute.combination(%I%)' ADD_WEIGHTS: From d1ac089b466b43974c859a90548527b6d55ab506 Mon Sep 17 00:00:00 2001 From: schochastics Date: Tue, 16 Jun 2026 19:38:46 +0000 Subject: [PATCH 10/16] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/27642731642 --- man/as_directed.Rd | 6 +++--- man/compose.Rd | 18 +++++++++--------- man/contract.Rd | 8 ++++---- man/disjoint_union.Rd | 8 ++++---- man/igraph-attribute-combination.Rd | 12 ++++++------ man/igraph_options.Rd | 6 +++--- man/intersection.igraph.Rd | 16 ++++++++-------- man/simplify.Rd | 4 ++-- man/union.igraph.Rd | 18 +++++++++--------- man/which_multiple.Rd | 2 +- 10 files changed, 49 insertions(+), 49 deletions(-) diff --git a/man/as_directed.Rd b/man/as_directed.Rd index a2452ae5b19..df00951531a 100644 --- a/man/as_directed.Rd +++ b/man/as_directed.Rd @@ -11,7 +11,7 @@ as_undirected( graph, mode = c("collapse", "each", "mutual"), ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) } \arguments{ @@ -24,7 +24,7 @@ as_undirected( \item{...}{These dots are for future extensions and must be empty.} -\item{edge_attr_comb}{Specifies what to do with edge attributes, if +\item{edge_attr_combine}{Specifies what to do with edge attributes, if \code{mode="collapse"} or \code{mode="mutual"}. In these cases many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details on @@ -118,7 +118,7 @@ g4 <- make_graph(c( E(g4)$weight <- seq_len(ecount(g4)) ug4 <- as_undirected(g4, mode = "mutual", - edge_attr_comb = list(weight = length) + edge_attr_combine = list(weight = length) ) print(ug4, e = TRUE) diff --git a/man/compose.Rd b/man/compose.Rd index 56a6377af3d..061b9e2ecc5 100644 --- a/man/compose.Rd +++ b/man/compose.Rd @@ -9,9 +9,9 @@ compose( g1, g2, byname = "auto", - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) } \arguments{ @@ -25,10 +25,10 @@ to perform the operation based on symbolic vertex names. If it is \code{FALSE} otherwise. A warning is generated if \code{auto} and one graph, but not both graphs are named.} -\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} -and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to -the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\item{graph_attr_combine, vertex_attr_combine, edge_attr_combine}{Specification for +combining clashing graph, vertex and edge attributes. \code{vertex_attr_combine} +and \code{edge_attr_combine} default to \code{"rename"}; \code{graph_attr_combine} defaults to +the \code{graph_attr_combine} igraph option (\code{"rename"} unless changed via \code{\link[=igraph_options]{igraph_options()}}). See \link{igraph-attribute-combination} for the available combiners.} } @@ -54,8 +54,8 @@ names. Otherwise numeric vertex IDs are used. \code{compose()} keeps the attributes of both graphs. All graph, vertex and edge attributes are copied to the result. By default, if an attribute is present in both graphs and would result in a name clash, that attribute -is renamed by adding suffixes: \verb{_1}, \verb{_2}. Pass \code{graph_attr_comb}, -\code{vertex_attr_comb} or \code{edge_attr_comb} to combine clashing attributes +is renamed by adding suffixes: \verb{_1}, \verb{_2}. Pass \code{graph_attr_combine}, +\code{vertex_attr_combine} or \code{edge_attr_combine} to combine clashing attributes instead; see \link{igraph-attribute-combination} for the available combiners. The \code{name} vertex attribute is treated specially if the operation is diff --git a/man/contract.Rd b/man/contract.Rd index c15b387879a..05488daa625 100644 --- a/man/contract.Rd +++ b/man/contract.Rd @@ -8,7 +8,7 @@ contract( graph, mapping, ..., - vertex_attr_comb = igraph_opt("vertex_attr_comb") + vertex_attr_combine = igraph_opt("vertex_attr_combine") ) } \arguments{ @@ -20,7 +20,7 @@ given.} \item{...}{These dots are for future extensions and must be empty.} -\item{vertex_attr_comb}{Specifies how to combine the vertex attributes in +\item{vertex_attr_combine}{Specifies how to combine the vertex attributes in the new graph. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details.} } \value{ @@ -33,7 +33,7 @@ vertices in the new graph correspond to sets of vertices in the input graph. \details{ The attributes of the graph are kept. Graph and edge attributes are unchanged, vertex attributes are combined, according to the -\code{vertex_attr_comb} parameter. +\code{vertex_attr_combine} parameter. } \section{Related documentation in the C library}{ \href{https://igraph.org/c/html/0.10.17/igraph-Operators.html#igraph_contract_vertices}{\code{contract_vertices()}} @@ -47,7 +47,7 @@ V(g)$name <- letters[1:vcount(g)] E(g)$weight <- runif(ecount(g)) g2 <- contract(g, rep(1:5, each = 2), - vertex_attr_comb = toString + vertex_attr_combine = toString ) ## graph and edge attributes are kept, vertex attributes are diff --git a/man/disjoint_union.Rd b/man/disjoint_union.Rd index 9d9f7ab4937..f72701ae8ad 100644 --- a/man/disjoint_union.Rd +++ b/man/disjoint_union.Rd @@ -5,15 +5,15 @@ \alias{\%du\%} \title{Disjoint union of graphs} \usage{ -disjoint_union(..., graph_attr_comb = igraph_opt("graph_attr_comb")) +disjoint_union(..., graph_attr_combine = igraph_opt("graph_attr_combine")) x \%du\% y } \arguments{ \item{\dots}{Graph objects or lists of graph objects.} -\item{graph_attr_comb}{Specification for combining shared graph attributes. -Defaults to the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed +\item{graph_attr_combine}{Specification for combining shared graph attributes. +Defaults to the \code{graph_attr_combine} igraph option (\code{"rename"} unless changed via \code{\link[=igraph_options]{igraph_options()}}), which preserves the historical behaviour of appending \verb{_1}, \verb{_2}, ... suffixes to clashing attribute names. See \link{igraph-attribute-combination} for the available combiners.} @@ -38,7 +38,7 @@ particular, it merges vertex and edge attributes using the \code{\link[vctrs:vec function. For graphs that lack some vertex/edge attribute, the corresponding values in the new graph are set to a missing value (\code{NA} for scalar attributes, \code{NULL} for list attributes). Graph attributes are combined according to -\code{graph_attr_comb}; by default any name clash is resolved by adding +\code{graph_attr_combine}; by default any name clash is resolved by adding suffixes (\verb{_1}, \verb{_2}, ...). See \link{igraph-attribute-combination} for the available combiners. diff --git a/man/igraph-attribute-combination.Rd b/man/igraph-attribute-combination.Rd index 20354f01636..681a4481e3c 100644 --- a/man/igraph-attribute-combination.Rd +++ b/man/igraph-attribute-combination.Rd @@ -13,7 +13,7 @@ vertex/edge attributes in these cases. } \details{ The functions that support the combination of attributes have one or two -extra arguments called \code{vertex.attr.comb} and/or \code{edge.attr.comb} +extra arguments called \code{vertex_attr_combine} and/or \code{edge_attr_combine} that specify how to perform the mapping of the attributes. E.g. \code{\link[=contract]{contract()}} contracts many vertices into a single one, the attributes of the vertices can be combined and stores as the vertex @@ -116,22 +116,22 @@ igraph_options(print.vertex.attributes = TRUE) igraph_options(print.edge.attributes = TRUE) ## new attribute is the sum of the old ones -simplify(g, edge.attr.comb = "sum") +simplify(g, edge_attr_combine = "sum") ## collect attributes into a string -simplify(g, edge.attr.comb = toString) +simplify(g, edge_attr_combine = toString) ## concatenate them into a vector, this creates a complex ## attribute -simplify(g, edge.attr.comb = "concat") +simplify(g, edge_attr_combine = "concat") E(g)$name <- letters[seq_len(ecount(g))] ## both attributes are collected into strings -simplify(g, edge.attr.comb = toString) +simplify(g, edge_attr_combine = toString) ## harmonic average of weights, names are dropped -simplify(g, edge.attr.comb = list( +simplify(g, edge_attr_combine = list( weight = function(x) length(x) / sum(1 / x), name = "ignore" )) diff --git a/man/igraph_options.Rd b/man/igraph_options.Rd index e69b713d10d..23faeeab1f3 100644 --- a/man/igraph_options.Rd +++ b/man/igraph_options.Rd @@ -65,13 +65,13 @@ The plotting function to use when plotting community structure dendrograms via \ Possible values are \sQuote{auto} (the default), \sQuote{phylo}, \sQuote{hclust} and \sQuote{dendrogram}. See \code{\link[=plot_dendrogram]{plot_dendrogram()}} for details. } -\item{edge_attr_comb}{ +\item{edge_attr_combine}{ Specifies what to do with the edge attributes if the graph is modified. The default value is \code{list(weight="sum", name="concat", "ignore")}. See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. The former dotted name \code{edge.attr.comb} still works but is soft-deprecated. } -\item{graph_attr_comb}{ +\item{graph_attr_combine}{ Specifies what to do with the graph attributes when graphs are combined, e.g. via \code{\link[=union]{union()}}, \code{\link[=intersection]{intersection()}}, \code{\link[=disjoint_union]{disjoint_union()}} or \code{\link[=compose]{compose()}}. The default value is \code{"rename"}, which resolves any @@ -115,7 +115,7 @@ It is recommended, if the user works with larger graphs. Logical constant, whether igraph functions should talk more than minimal. E.g. if \code{TRUE} then some functions will use progress bars while computing. Defaults to \code{FALSE}. } -\item{vertex_attr_comb}{ +\item{vertex_attr_combine}{ Specifies what to do with the vertex attributes if the graph is modified. The default value is \code{list(name="concat", "ignore")}. See \code{\link[=attribute.combination]{attribute.combination()}} for details on this. The former dotted diff --git a/man/intersection.igraph.Rd b/man/intersection.igraph.Rd index bb9d3324ce9..2c313890870 100644 --- a/man/intersection.igraph.Rd +++ b/man/intersection.igraph.Rd @@ -9,9 +9,9 @@ ..., byname = "auto", keep.all.vertices = TRUE, - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) } \arguments{ @@ -26,10 +26,10 @@ graphs are named.} \item{keep.all.vertices}{Logical scalar, whether to keep vertices that only appear in a subset of the input graphs.} -\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} -and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to -the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\item{graph_attr_combine, vertex_attr_combine, edge_attr_combine}{Specification for +combining clashing graph, vertex and edge attributes. \code{vertex_attr_combine} +and \code{edge_attr_combine} default to \code{"rename"}; \code{graph_attr_combine} defaults to +the \code{graph_attr_combine} igraph option (\code{"rename"} unless changed via \code{\link[=igraph_options]{igraph_options()}}). See \link{igraph-attribute-combination} for the available combiners.} } @@ -53,7 +53,7 @@ of the internal numeric vertex IDs. vertex and edge attributes are copied to the result. By default, if an attribute is present in multiple graphs and would result in a name clash, that attribute is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass -\code{graph_attr_comb}, \code{vertex_attr_comb} or \code{edge_attr_comb} to combine +\code{graph_attr_combine}, \code{vertex_attr_combine} or \code{edge_attr_combine} to combine clashing attributes instead; see \link{igraph-attribute-combination} for the available combiners. diff --git a/man/simplify.Rd b/man/simplify.Rd index 6432891eed8..fc4b7c6c4dc 100644 --- a/man/simplify.Rd +++ b/man/simplify.Rd @@ -11,7 +11,7 @@ simplify( remove.multiple = TRUE, remove.loops = TRUE, ..., - edge_attr_comb = igraph_opt("edge_attr_comb") + edge_attr_combine = igraph_opt("edge_attr_combine") ) is_simple(graph) @@ -28,7 +28,7 @@ removed.} \item{...}{These dots are for future extensions and must be empty.} -\item{edge_attr_comb}{Specifies what to do with edge attributes, if +\item{edge_attr_combine}{Specifies what to do with edge attributes, if \code{remove.multiple=TRUE}. In this case many edges might be mapped to a single one in the new graph, and their attributes are combined. Please see \code{\link[=attribute.combination]{attribute.combination()}} for details on this.} diff --git a/man/union.igraph.Rd b/man/union.igraph.Rd index 032d009d425..60a0357a8f3 100644 --- a/man/union.igraph.Rd +++ b/man/union.igraph.Rd @@ -8,9 +8,9 @@ \method{union}{igraph}( ..., byname = "auto", - graph_attr_comb = igraph_opt("graph_attr_comb"), - vertex_attr_comb = "rename", - edge_attr_comb = "rename" + graph_attr_combine = igraph_opt("graph_attr_combine"), + vertex_attr_combine = "rename", + edge_attr_combine = "rename" ) } \arguments{ @@ -22,10 +22,10 @@ to perform the operation based on symbolic vertex names. If it is otherwise. A warning is generated if \code{auto} and some (but not all) graphs are named.} -\item{graph_attr_comb, vertex_attr_comb, edge_attr_comb}{Specification for -combining clashing graph, vertex and edge attributes. \code{vertex_attr_comb} -and \code{edge_attr_comb} default to \code{"rename"}; \code{graph_attr_comb} defaults to -the \code{graph_attr_comb} igraph option (\code{"rename"} unless changed via +\item{graph_attr_combine, vertex_attr_combine, edge_attr_combine}{Specification for +combining clashing graph, vertex and edge attributes. \code{vertex_attr_combine} +and \code{edge_attr_combine} default to \code{"rename"}; \code{graph_attr_combine} defaults to +the \code{graph_attr_combine} igraph option (\code{"rename"} unless changed via \code{\link[=igraph_options]{igraph_options()}}). \code{"rename"} preserves the historical behaviour of appending \verb{_1}, \verb{_2}, ... suffixes. See \link{igraph-attribute-combination} for the available combiners.} @@ -49,8 +49,8 @@ of the internal numeric vertex IDs. \code{union()} keeps the attributes of all graphs. All graph, vertex and edge attributes are copied to the result. By default, if an attribute is present in multiple graphs and would result in a name clash, that attribute -is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass \code{graph_attr_comb}, -\code{vertex_attr_comb} or \code{edge_attr_comb} to combine clashing attributes +is renamed by adding suffixes: \verb{_1}, \verb{_2}, etc. Pass \code{graph_attr_combine}, +\code{vertex_attr_combine} or \code{edge_attr_combine} to combine clashing attributes instead, e.g. by summing or by taking the first non-\code{NA} value. See \link{igraph-attribute-combination} for the available combiners. diff --git a/man/which_multiple.Rd b/man/which_multiple.Rd index ac17d8dfe4f..ab6d858f424 100644 --- a/man/which_multiple.Rd +++ b/man/which_multiple.Rd @@ -88,7 +88,7 @@ which_multiple(make_graph(c(1, 2, 2, 1), dir = FALSE)) # Remove multiple edges but keep multiplicity g <- sample_pa(10, m = 3, algorithm = "bag") E(g)$weight <- count_multiple(g) -g <- simplify(g, edge.attr.comb = list(weight = "min")) +g <- simplify(g, edge_attr_combine = list(weight = "min")) any(which_multiple(g)) E(g)$weight From 96e21e2601ae0cd0d06f16878199bbaade2f6121 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 25 Jun 2026 08:24:16 +0200 Subject: [PATCH 11/16] Sort known_names alphabetically and reorder known_codes accordingly in igraph.i.attribute.combination --- R/attributes.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/R/attributes.R b/R/attributes.R index 480a5a04377..8cfba586359 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1311,19 +1311,19 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { cli::cli_warn("Some attributes are duplicated") } known_names <- c( - "ignore", - "sum", - "prod", - "min", - "max", - "random", + "concat", "first", + "ignore", "last", + "max", "mean", "median", - "concat" + "min", + "prod", + "random", + "sum" ) - known_codes <- c(0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + known_codes <- c(12, 8, 0, 9, 6, 10, 11, 5, 4, 7, 3) if (allow_rename) { known_names <- c(known_names, "rename") known_codes <- c(known_codes, NA_integer_) From 874f8eb1e9f260b526a7761bdfa70fb3c9569320 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 25 Jun 2026 08:27:17 +0200 Subject: [PATCH 12/16] Add documentation for attribute combination codes and use integer literals --- R/attributes.R | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/R/attributes.R b/R/attributes.R index 8cfba586359..8c764670da6 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1310,6 +1310,11 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { if (anyDuplicated(names(comb)) > 0) { cli::cli_warn("Some attributes are duplicated") } + # `known_codes` are the numeric values of the `igraph_attribute_combination_type_t` + # enum in the C library (see src/vendor/cigraph/include/igraph_attributes.h). + # Each code must stay aligned with its name in `known_names`. The DEFAULT (1) and + # FUNCTION (2) enum values are intentionally absent: FUNCTION is handled by the + # `!is.character(x)` branch below, and DEFAULT is not selectable by name. known_names <- c( "concat", "first", @@ -1323,7 +1328,7 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { "random", "sum" ) - known_codes <- c(12, 8, 0, 9, 6, 10, 11, 5, 4, 7, 3) + known_codes <- c(12L, 8L, 0L, 9L, 6L, 10L, 11L, 5L, 4L, 7L, 3L) if (allow_rename) { known_names <- c(known_names, "rename") known_codes <- c(known_codes, NA_integer_) From eb8937274f1bc0f9df09daf03f9f46b1419ef554 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 25 Jun 2026 08:30:55 +0200 Subject: [PATCH 13/16] Reorder condition check in attribute combination to test for "rename" before checking allow_rename flag --- R/attributes.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/attributes.R b/R/attributes.R index 8c764670da6..92d9f319107 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1339,7 +1339,7 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { } else { idx <- pmatch(tolower(x), known_names) if (is.na(idx)) { - if (!allow_rename && identical(tolower(x), "rename")) { + if (identical(tolower(x), "rename") && !allow_rename) { cli::cli_abort( "{.val rename} is only supported by graph operators ({.fn union}, {.fn intersection}, {.fn compose}, {.fn disjoint_union}), not by this function." ) From fde3bed1d835315a8fe05f741a8e9703e8cc20a4 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 25 Jun 2026 08:43:36 +0200 Subject: [PATCH 14/16] add example to rename --- R/attributes.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/R/attributes.R b/R/attributes.R index 92d9f319107..57be26b72a8 100644 --- a/R/attributes.R +++ b/R/attributes.R @@ -1448,7 +1448,10 @@ igraph.i.attribute.combination <- function(comb, allow_rename = FALSE) { #' } #' \item{"rename"}{ #' Keep clashing attributes side-by-side under disambiguated names by -#' appending `_1`, `_2`, ... suffixes. This is the default for the +#' appending `_1`, `_2`, ... suffixes. For example, if two graphs each +#' have an attribute called `group`, the resulting graph will have +#' attributes `group_1` and `group_2`, corresponding to the first and +#' second input graph, respectively. This is the default for the #' graph operators [union()], [intersection()], [compose()] and #' [disjoint_union()] and preserves their historical behaviour. #' Only those operators accept `"rename"`; [simplify()] and From fbf2bcc424b51106558b3157e8b5f29de4362f2a Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 25 Jun 2026 08:51:49 +0200 Subject: [PATCH 15/16] Update testthat minimum version to >= 3.3.0 and use expect_all_true --- DESCRIPTION | 2 +- tests/testthat/test-operators.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index d092b85a615..1801cb14e41 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -60,7 +60,7 @@ Suggests: scales, stats4, tcltk, - testthat, + testthat (>= 3.3.0), vdiffr, withr Enhances: diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index ed2d3872e63..142af4f49f6 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -1412,7 +1412,7 @@ test_that("graph_attr_combine defaults to the graph_attr_combine igraph option", # Default option ("rename") preserves the historical suffixing behaviour. u <- union(g1, g2) - expect_true(all(c("label_1", "label_2") %in% graph_attr_names(u))) + expect_all_true(c("label_1", "label_2") %in% graph_attr_names(u)) # Setting the option changes the default for the graph operators. local_igraph_options(graph_attr_combine = "ignore") From 409b31033e9ac28e578d3bb167eed90e77459911 Mon Sep 17 00:00:00 2001 From: schochastics Date: Thu, 25 Jun 2026 07:02:14 +0000 Subject: [PATCH 16/16] chore: Auto-update from GitHub Actions Run: https://github.com/igraph/rigraph/actions/runs/28152398180 --- man/igraph-attribute-combination.Rd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/man/igraph-attribute-combination.Rd b/man/igraph-attribute-combination.Rd index 681a4481e3c..b36ff376c89 100644 --- a/man/igraph-attribute-combination.Rd +++ b/man/igraph-attribute-combination.Rd @@ -95,7 +95,10 @@ This results almost always a complex attribute. } \item{"rename"}{ Keep clashing attributes side-by-side under disambiguated names by -appending \verb{_1}, \verb{_2}, ... suffixes. This is the default for the +appending \verb{_1}, \verb{_2}, ... suffixes. For example, if two graphs each +have an attribute called \code{group}, the resulting graph will have +attributes \code{group_1} and \code{group_2}, corresponding to the first and +second input graph, respectively. This is the default for the graph operators \code{\link[=union]{union()}}, \code{\link[=intersection]{intersection()}}, \code{\link[=compose]{compose()}} and \code{\link[=disjoint_union]{disjoint_union()}} and preserves their historical behaviour. Only those operators accept \code{"rename"}; \code{\link[=simplify]{simplify()}} and