diff --git a/R/bipartite.R b/R/bipartite.R index 5174b6ce07b..07b1e6364b2 100644 --- a/R/bipartite.R +++ b/R/bipartite.R @@ -117,6 +117,7 @@ bipartite.mapping <- function(graph) { #' @param types An optional vertex type vector to use instead of the #' \sQuote{`type`} vertex attribute. You must supply this argument if the #' graph has no \sQuote{`type`} vertex attribute. +#' @inheritParams rlang::args_dots_empty #' @param multiplicity If `TRUE`, then igraph keeps the multiplicity of #' the edges as an edge attribute called \sQuote{weight}. #' E.g. if there is an A-C-B and also an A-D-B @@ -162,11 +163,44 @@ bipartite.mapping <- function(graph) { bipartite_projection <- function( graph, types = NULL, + ..., multiplicity = TRUE, probe1 = NULL, which = c("both", "true", "false"), remove.type = TRUE ) { + # BEGIN GENERATED ARG_HANDLE: bipartite_projection, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list( + multiplicity = multiplicity, + probe1 = probe1, + which = which, + remove.type = remove.type + ), + recover_new = c("multiplicity", "probe1", "which", "remove.type"), + recover_old = c("multiplicity", "probe1", "which", "remove.type"), + match_names = c("multiplicity", "probe1", "which", "remove.type"), + match_to = c("multiplicity", "probe1", "which", "remove.type"), + defaults = list( + multiplicity = TRUE, + probe1 = NULL, + which = c("both", "true", "false"), + remove.type = TRUE + ), + head_args = c("graph", "types"), + fn_name = "bipartite_projection" + ) + 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) types <- handle_vertex_type_arg(types, graph) diff --git a/R/cycles.R b/R/cycles.R index c3d65b5cae0..25356203038 100644 --- a/R/cycles.R +++ b/R/cycles.R @@ -31,6 +31,7 @@ #' a specific cycle. #' #' @param graph The input graph. +#' @inheritParams rlang::args_dots_empty #' @param mode Character constant specifying how to handle directed graphs. #' `out` follows edge directions, `in` follows edges in the reverse direction, #' and `all` ignores edge directions. Ignored in undirected graphs. diff --git a/R/degseq.R b/R/degseq.R index 918239d17dd..7be35794701 100644 --- a/R/degseq.R +++ b/R/degseq.R @@ -117,6 +117,7 @@ is_degseq <- function(out.deg, in.deg = NULL) { #' the out-degree sequence for directed graphs. #' @param in.deg `NULL` or an integer vector. For undirected graphs, it #' should be `NULL`. For directed graphs it specifies the in-degrees. +#' @inheritParams rlang::args_dots_empty #' @param allowed.edge.types The allowed edge types in the graph. \sQuote{simple} #' means that neither loop nor multiple edges are allowed (i.e. the graph must be #' simple). \sQuote{loops} means that loop edges are allowed but mutiple edges @@ -142,8 +143,33 @@ is_degseq <- function(out.deg, in.deg = NULL) { is_graphical <- function( out.deg, in.deg = NULL, + ..., allowed.edge.types = c("simple", "loops", "multi", "all") ) { + # BEGIN GENERATED ARG_HANDLE: is_graphical, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(allowed.edge.types = allowed.edge.types), + recover_new = c("allowed.edge.types"), + recover_old = c("allowed.edge.types"), + match_names = c("allowed.edge.types"), + match_to = c("allowed.edge.types"), + defaults = list( + allowed.edge.types = c("simple", "loops", "multi", "all") + ), + head_args = c("out.deg", "in.deg"), + fn_name = "is_graphical" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + is_graphical_impl( out_deg = out.deg, in_deg = in.deg, diff --git a/man/bipartite_projection.Rd b/man/bipartite_projection.Rd index 961e0a4b812..5d15d940311 100644 --- a/man/bipartite_projection.Rd +++ b/man/bipartite_projection.Rd @@ -8,6 +8,7 @@ bipartite_projection( graph, types = NULL, + ..., multiplicity = TRUE, probe1 = NULL, which = c("both", "true", "false"), @@ -24,6 +25,8 @@ ignored during the computation.} \sQuote{\code{type}} vertex attribute. You must supply this argument if the graph has no \sQuote{\code{type}} vertex attribute.} +\item{...}{These dots are for future extensions and must be empty.} + \item{multiplicity}{If \code{TRUE}, then igraph keeps the multiplicity of the edges as an edge attribute called \sQuote{weight}. E.g. if there is an A-C-B and also an A-D-B diff --git a/man/is_graphical.Rd b/man/is_graphical.Rd index b3cf11866c1..b9050392166 100644 --- a/man/is_graphical.Rd +++ b/man/is_graphical.Rd @@ -7,6 +7,7 @@ is_graphical( out.deg, in.deg = NULL, + ..., allowed.edge.types = c("simple", "loops", "multi", "all") ) } @@ -17,6 +18,8 @@ the out-degree sequence for directed graphs.} \item{in.deg}{\code{NULL} or an integer vector. For undirected graphs, it should be \code{NULL}. For directed graphs it specifies the in-degrees.} +\item{...}{These dots are for future extensions and must be empty.} + \item{allowed.edge.types}{The allowed edge types in the graph. \sQuote{simple} means that neither loop nor multiple edges are allowed (i.e. the graph must be simple). \sQuote{loops} means that loop edges are allowed but mutiple edges diff --git a/tests/testthat/test-bipartite.R b/tests/testthat/test-bipartite.R index c3020f40698..a57d7222cd5 100644 --- a/tests/testthat/test-bipartite.R +++ b/tests/testthat/test-bipartite.R @@ -97,3 +97,45 @@ test_that("bipartite_projection prints a warning if the type attribute is non-lo expect_warning(bipartite_projection(g), "logical") expect_warning(bipartite_projection_size(g), "logical") }) + +# ---- ellipsis migration: argument coverage ---------------------------------- + +test_that("bipartite_projection() takes all tail arguments by name", { + g <- make_full_bipartite_graph(3, 2) + + # A single projection is returned, without multiplicity weights, + # and it keeps the `type` vertex attribute. + proj <- bipartite_projection( + g, + multiplicity = FALSE, + which = "false", + remove.type = FALSE + ) + expect_true(is_igraph(proj)) + expect_vcount(proj, 3) + expect_false("weight" %in% edge_attr_names(proj)) + expect_true("type" %in% vertex_attr_names(proj)) + + # `probe1` puts the projection containing that vertex first, + # reversing the default order (type `FALSE` first). + # Current behaviour warns even though both projections are requested. + expect_warning( + proj_probe <- bipartite_projection(g, probe1 = 4), + "probe1" + ) + expect_vcount(proj_probe[[1]], 2) + expect_vcount(proj_probe[[2]], 3) +}) + +test_that("bipartite_projection() recovers legacy positional arguments", { + g <- make_full_bipartite_graph(3, 2) + + rlang::local_options(lifecycle_verbosity = "warning") + lifecycle::expect_deprecated( + res <- bipartite_projection(g, NULL, FALSE) + ) + ref <- bipartite_projection(g, multiplicity = FALSE) + expect_identical_graphs(res[[1]], ref[[1]]) + expect_identical_graphs(res[[2]], ref[[2]]) + expect_false("weight" %in% edge_attr_names(res[[1]])) +}) diff --git a/tests/testthat/test-degseq.R b/tests/testthat/test-degseq.R index 0089d1db628..1fe573a1608 100644 --- a/tests/testthat/test-degseq.R +++ b/tests/testthat/test-degseq.R @@ -40,3 +40,22 @@ test_that("is_degseq works", { expect_true(is_degseq(degree(g))) expect_true(is_graphical(degree(g))) }) + +# ---- ellipsis migration: argument coverage ---------------------------------- + +test_that("is_graphical() takes `allowed.edge.types` by name", { + # Two vertices of degree 3 need multi-edges, so the verdict flips. + expect_false(is_graphical(c(3, 3))) + expect_true(is_graphical(c(3, 3), allowed.edge.types = "multi")) + # Directed sequences pass the in-degrees as the second head argument. + expect_true(is_graphical(c(2, 0, 1), c(1, 1, 1))) +}) + +test_that("is_graphical() recovers legacy positional arguments", { + rlang::local_options(lifecycle_verbosity = "warning") + lifecycle::expect_deprecated( + res <- is_graphical(c(3, 3), NULL, "multi") + ) + expect_identical(res, is_graphical(c(3, 3), allowed.edge.types = "multi")) + expect_true(res) +}) diff --git a/tools/migrations/bipartite-degseq.R b/tools/migrations/bipartite-degseq.R new file mode 100644 index 00000000000..d58f2fa1d22 --- /dev/null +++ b/tools/migrations/bipartite-degseq.R @@ -0,0 +1,30 @@ +# Argument-signature migrations: bipartite-degseq +# Schema: see tools/migrations/README.md. Regenerate with: +# Rscript tools/generate-migrations.R + +migrations <- list( + bipartite_projection = list( + old = function(graph, types, multiplicity, probe1, which, remove.type) {}, + new = function( + graph, + types = NULL, + ..., + multiplicity = TRUE, + probe1 = NULL, + which = c("both", "true", "false"), + remove.type = TRUE + ) {}, + when = "3.0.0" + ), + + is_graphical = list( + old = function(out.deg, in.deg, allowed.edge.types) {}, + new = function( + out.deg, + in.deg = NULL, + ..., + allowed.edge.types = c("simple", "loops", "multi", "all") + ) {}, + when = "3.0.0" + ) +)