diff --git a/R/adjacency.R b/R/adjacency.R index 5f3985a97ba..ccfa2c4823f 100644 --- a/R/adjacency.R +++ b/R/adjacency.R @@ -136,6 +136,7 @@ graph.adjacency <- function( #' #' @param adjmatrix A square adjacency matrix. From igraph version 0.5.1 this #' can be a sparse matrix created with the `Matrix` package. +#' @inheritParams rlang::args_dots_empty #' @param mode Character scalar, specifies how igraph should interpret the #' supplied matrix. See also the `weighted` argument, the interpretation #' depends on that too. Possible values are: `directed`, @@ -275,6 +276,7 @@ graph.adjacency <- function( #' @export graph_from_adjacency_matrix <- function( adjmatrix, + ..., mode = c( "directed", "undirected", @@ -289,6 +291,58 @@ graph_from_adjacency_matrix <- function( add.colnames = NULL, add.rownames = NA ) { + # BEGIN GENERATED ARG_HANDLE: graph_from_adjacency_matrix, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list( + mode = mode, + weighted = weighted, + diag = diag, + add.colnames = add.colnames, + add.rownames = add.rownames + ), + recover_new = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + recover_old = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + match_names = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + match_to = c("mode", "weighted", "diag", "add.colnames", "add.rownames"), + defaults = list( + mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), + weighted = NULL, + diag = TRUE, + add.colnames = NULL, + add.rownames = NA + ), + head_args = c("adjmatrix"), + fn_name = "graph_from_adjacency_matrix" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + mode <- igraph_match_arg(mode) ensure_no_na(adjmatrix, "adjacency matrix", mode) @@ -388,16 +442,70 @@ is_symmetric <- function(x) { } #' @rdname graph_from_adjacency_matrix +#' @inheritParams rlang::args_dots_empty #' @family adjacency #' @export from_adjacency <- function( adjmatrix, + ..., mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted = NULL, diag = TRUE, add.colnames = NULL, add.rownames = NA ) { + # BEGIN GENERATED ARG_HANDLE: from_adjacency, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list( + mode = mode, + weighted = weighted, + diag = diag, + add.colnames = add.colnames, + add.rownames = add.rownames + ), + recover_new = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + recover_old = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + match_names = c( + "mode", + "weighted", + "diag", + "add.colnames", + "add.rownames" + ), + match_to = c("mode", "weighted", "diag", "add.colnames", "add.rownames"), + defaults = list( + mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), + weighted = NULL, + diag = TRUE, + add.colnames = NULL, + add.rownames = NA + ), + head_args = c("adjmatrix"), + fn_name = "from_adjacency" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + constructor_spec( graph_from_adjacency_matrix, adjmatrix = adjmatrix, diff --git a/R/conversion.R b/R/conversion.R index a1ccb77f8a1..9f44f193617 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -546,6 +546,7 @@ as_adj <- function( #' `as_edgelist()` returns the list of edges in a graph. #' #' @param graph The graph to convert. +#' @inheritParams rlang::args_dots_empty #' @param names Whether to return a character matrix containing vertex #' names (i.e. the `name` vertex attribute) if they exist or numeric #' vertex IDs. @@ -562,7 +563,33 @@ as_adj <- function( #' #' @family conversion #' @export -as_edgelist <- function(graph, names = TRUE) { +as_edgelist <- function( + graph, + ..., + names = TRUE +) { + # BEGIN GENERATED ARG_HANDLE: as_edgelist, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(names = names), + recover_new = c("names"), + recover_old = c("names"), + match_names = c("names"), + match_to = c("names"), + defaults = list(names = TRUE), + head_args = c("graph"), + fn_name = "as_edgelist" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) res <- matrix(get_edgelist_impl(graph = graph, bycol = TRUE), ncol = 2) res <- res + 1 @@ -660,6 +687,7 @@ as_edgelist <- function(graph, names = TRUE) { #' plot(g3, layout = layout_in_circle, edge.label = E(g3)$weight) #' plot(ug3, layout = layout_in_circle, edge.label = E(ug3)$weight) +#' @inheritParams rlang::args_dots_empty #' @examples #' #' g4 <- make_graph(c( @@ -676,8 +704,31 @@ as_edgelist <- function(graph, names = TRUE) { #' as_directed <- function( graph, + ..., mode = c("mutual", "arbitrary", "random", "acyclic") ) { + # BEGIN GENERATED ARG_HANDLE: as_directed, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode), + recover_new = c("mode"), + recover_old = c("mode"), + match_names = c("mode"), + match_to = c("mode"), + defaults = list(mode = c("mutual", "arbitrary", "random", "acyclic")), + head_args = c("graph"), + fn_name = "as_directed" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + to_directed_impl( graph = graph, mode = mode @@ -725,6 +776,7 @@ as_undirected <- function( #' vertices. #' #' @param graph The input graph. +#' @inheritParams rlang::args_dots_empty #' @param mode Character scalar, it gives what kind of adjacent edges/vertices #' to include in the lists. \sQuote{`out`} is for outgoing edges/vertices, #' \sQuote{`in`} is for incoming edges/vertices, \sQuote{`all`} is @@ -753,10 +805,37 @@ as_undirected <- function( #' as_adj_list <- function( graph, + ..., mode = c("all", "out", "in", "total"), loops = c("twice", "once", "ignore"), multiple = TRUE ) { + # BEGIN GENERATED ARG_HANDLE: as_adj_list, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode, loops = loops, multiple = multiple), + recover_new = c("mode", "loops", "multiple"), + recover_old = c("mode", "loops", "multiple"), + match_names = c("mode", "loops", "multiple"), + match_to = c("mode", "loops", "multiple"), + defaults = list( + mode = c("all", "out", "in", "total"), + loops = c("twice", "once", "ignore"), + multiple = TRUE + ), + head_args = c("graph"), + fn_name = "as_adj_list" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) mode <- igraph_match_arg(mode) @@ -782,12 +861,39 @@ as_adj_list <- function( } #' @rdname as_adj_list +#' @inheritParams rlang::args_dots_empty #' @export as_adj_edge_list <- function( graph, + ..., mode = c("all", "out", "in", "total"), loops = c("twice", "once", "ignore") ) { + # BEGIN GENERATED ARG_HANDLE: as_adj_edge_list, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode, loops = loops), + recover_new = c("mode", "loops"), + recover_old = c("mode", "loops"), + match_names = c("mode", "loops"), + match_to = c("mode", "loops"), + defaults = list( + mode = c("all", "out", "in", "total"), + loops = c("twice", "once", "ignore") + ), + head_args = c("graph"), + fn_name = "as_adj_edge_list" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + ensure_igraph(graph) mode <- igraph_match_arg(mode) @@ -823,6 +929,7 @@ as_adj_edge_list <- function( #' attributes of the first of the multiple edges. #' #' @param graphNEL The graphNEL graph. +#' @inheritParams rlang::args_dots_empty #' @param name Logical, whether to add graphNEL vertex names as an #' igraph vertex attribute called \sQuote{`name`}. #' @param weight Logical, whether to add graphNEL edge weights as an @@ -855,10 +962,33 @@ as_adj_edge_list <- function( #' @export graph_from_graphnel <- function( graphNEL, + ..., name = TRUE, weight = TRUE, unlist.attrs = TRUE ) { + # BEGIN GENERATED ARG_HANDLE: graph_from_graphnel, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(name = name, weight = weight, unlist.attrs = unlist.attrs), + recover_new = c("name", "weight", "unlist.attrs"), + recover_old = c("name", "weight", "unlist.attrs"), + match_names = c("name", "weight", "unlist.attrs"), + match_to = c("name", "weight", "unlist.attrs"), + defaults = list(name = TRUE, weight = TRUE, unlist.attrs = TRUE), + head_args = c("graphNEL"), + fn_name = "graph_from_graphnel" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + if (!inherits(graphNEL, "graphNEL")) { cli::cli_abort( "{.arg graphNEL} is {.obj_type_friendly {graphNEL}} and not a graphNEL graph" @@ -965,7 +1095,10 @@ as_graphnel <- function(graph) { edgemode <- if (is_directed(graph)) "directed" else "undirected" if ("weight" %in% edge_attr_names(graph) && is.numeric(E(graph)$weight)) { - al <- lapply(as_adj_edge_list(graph, "out", loops = "once"), as.vector) + al <- lapply( + as_adj_edge_list(graph, mode = "out", loops = "once"), + as.vector + ) for (i in seq(along.with = al)) { edges <- ends(graph, al[[i]], names = FALSE) edges <- ifelse(edges[, 2] == i, edges[, 1], edges[, 2]) @@ -973,7 +1106,7 @@ as_graphnel <- function(graph) { al[[i]] <- list(edges = edges, weights = weights) } } else { - al <- as_adj_list(graph, "out", loops = "once") + al <- as_adj_list(graph, mode = "out", loops = "once") al <- lapply(al, function(x) list(edges = as.vector(x))) } @@ -1323,6 +1456,7 @@ as_data_frame <- function(x, what = c("edges", "vertices", "both")) { #' @param adjlist The adjacency list. It should be consistent, i.e. the maximum #' throughout all vectors in the list must be less than the number of vectors #' (=the number of vertices in the graph). +#' @inheritParams rlang::args_dots_empty #' @param mode Character scalar, it specifies whether the graph to create is #' undirected (\sQuote{all} or \sQuote{total}) or directed; and in the latter #' case, whether it contains the outgoing (\sQuote{out}) or the incoming @@ -1359,9 +1493,32 @@ as_data_frame <- function(x, what = c("edges", "vertices", "both")) { #' @export graph_from_adj_list <- function( adjlist, + ..., mode = c("out", "in", "all", "total"), duplicate = TRUE ) { + # BEGIN GENERATED ARG_HANDLE: graph_from_adj_list, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(mode = mode, duplicate = duplicate), + recover_new = c("mode", "duplicate"), + recover_old = c("mode", "duplicate"), + match_names = c("mode", "duplicate"), + match_to = c("mode", "duplicate"), + defaults = list(mode = c("out", "in", "all", "total"), duplicate = TRUE), + head_args = c("adjlist"), + fn_name = "graph_from_adj_list" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + adjlist_impl( adjlist = adjlist, mode = mode, @@ -1629,6 +1786,7 @@ graph.data.frame <- function(d, directed = TRUE, vertices = NULL) { #' version 0.7 this argument is coerced to a data frame with #' `as.data.frame`. #' @param directed Logical, whether or not to create a directed graph. +#' @inheritParams rlang::args_dots_empty #' @param vertices A data frame with vertex metadata, or `NULL`. See #' details below. Since version 0.7 this argument is coerced to a data frame #' with `as.data.frame`, if not `NULL`. @@ -1678,7 +1836,34 @@ graph.data.frame <- function(d, directed = TRUE, vertices = NULL) { #' as_data_frame(g, what = "edges") #' #' @export -graph_from_data_frame <- function(d, directed = TRUE, vertices = NULL) { +graph_from_data_frame <- function( + d, + directed = TRUE, + ..., + vertices = NULL +) { + # BEGIN GENERATED ARG_HANDLE: graph_from_data_frame, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(vertices = vertices), + recover_new = c("vertices"), + recover_old = c("vertices"), + match_names = c("vertices"), + match_to = c("vertices"), + defaults = list(vertices = NULL), + head_args = c("d", "directed"), + fn_name = "graph_from_data_frame" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + d <- as.data.frame(d) if (!is.null(vertices)) { vertices <- as.data.frame(vertices) @@ -1770,6 +1955,7 @@ from_data_frame <- function(...) constructor_spec(graph_from_data_frame, ...) #' #' @concept Edge list #' @param el The edge list, a two column matrix, character or numeric. +#' @inheritParams rlang::args_dots_empty #' @param directed Whether to create a directed graph. #' @return An igraph graph. #' @@ -1781,7 +1967,33 @@ from_data_frame <- function(...) constructor_spec(graph_from_data_frame, ...) #' #' # Create a ring by hand #' graph_from_edgelist(cbind(1:10, c(2:10, 1))) -graph_from_edgelist <- function(el, directed = TRUE) { +graph_from_edgelist <- function( + el, + ..., + directed = TRUE +) { + # BEGIN GENERATED ARG_HANDLE: graph_from_edgelist, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list(directed = directed), + recover_new = c("directed"), + recover_old = c("directed"), + match_names = c("directed"), + match_to = c("directed"), + defaults = list(directed = TRUE), + head_args = c("el"), + fn_name = "graph_from_edgelist" + ) + list2env(.arg_handle$values, environment()) + lifecycle::deprecate_soft( + "3.0.0", + what = I(.arg_handle$what), + details = .arg_handle$details + ) + } + # END GENERATED ARG_HANDLE + if (!is.matrix(el) || ncol(el) != 2) { cli::cli_abort("graph_from_edgelist expects a matrix with two columns.") } 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/incidence.R b/R/incidence.R index 77e301d6ac1..7ca08b1b9d7 100644 --- a/R/incidence.R +++ b/R/incidence.R @@ -140,6 +140,7 @@ graph_incidence_build <- function( #' #' @param incidence The input bipartite adjacency matrix. It can also be a sparse matrix #' from the `Matrix` package. +#' @inheritParams rlang::args_dots_empty #' @param directed Logical, whether to create a directed graph. #' @param mode A character constant, defines the direction of the edges in #' directed graphs, ignored for undirected graphs. If \sQuote{`out`}, then @@ -186,12 +187,47 @@ graph_incidence_build <- function( #' @export graph_from_biadjacency_matrix <- function( incidence, + ..., directed = FALSE, mode = c("all", "out", "in", "total"), multiple = FALSE, weighted = NULL, add.names = NULL ) { + # BEGIN GENERATED ARG_HANDLE: graph_from_biadjacency_matrix, do not edit, see tools/generate-migrations.R + if (...length() > 0L) { + .arg_handle <- migrate_recover_args( + list(...), + current = list( + directed = directed, + mode = mode, + multiple = multiple, + weighted = weighted, + add.names = add.names + ), + recover_new = c("directed", "mode", "multiple", "weighted", "add.names"), + recover_old = c("directed", "mode", "multiple", "weighted", "add.names"), + match_names = c("directed", "mode", "multiple", "weighted", "add.names"), + match_to = c("directed", "mode", "multiple", "weighted", "add.names"), + defaults = list( + directed = FALSE, + mode = c("all", "out", "in", "total"), + multiple = FALSE, + weighted = NULL, + add.names = NULL + ), + head_args = c("incidence"), + fn_name = "graph_from_biadjacency_matrix" + ) + 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_no_na(incidence, "biadjacency matrix") directed <- as.logical(directed) diff --git a/man/as_adj_list.Rd b/man/as_adj_list.Rd index 64d1a31f03a..4f5f7515f95 100644 --- a/man/as_adj_list.Rd +++ b/man/as_adj_list.Rd @@ -7,6 +7,7 @@ \usage{ as_adj_list( graph, + ..., mode = c("all", "out", "in", "total"), loops = c("twice", "once", "ignore"), multiple = TRUE @@ -14,6 +15,7 @@ as_adj_list( as_adj_edge_list( graph, + ..., mode = c("all", "out", "in", "total"), loops = c("twice", "once", "ignore") ) @@ -21,6 +23,8 @@ as_adj_edge_list( \arguments{ \item{graph}{The input graph.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Character scalar, it gives what kind of adjacent edges/vertices to include in the lists. \sQuote{\code{out}} is for outgoing edges/vertices, \sQuote{\verb{in}} is for incoming edges/vertices, \sQuote{\code{all}} is diff --git a/man/as_directed.Rd b/man/as_directed.Rd index e496f6022b0..f4032e7956d 100644 --- a/man/as_directed.Rd +++ b/man/as_directed.Rd @@ -5,7 +5,7 @@ \alias{as_undirected} \title{Convert between directed and undirected graphs} \usage{ -as_directed(graph, mode = c("mutual", "arbitrary", "random", "acyclic")) +as_directed(graph, ..., mode = c("mutual", "arbitrary", "random", "acyclic")) as_undirected( graph, @@ -16,6 +16,8 @@ as_undirected( \arguments{ \item{graph}{The graph to convert.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Character constant, defines the conversion algorithm. For \code{as_directed()} it can be \code{mutual} or \code{arbitrary}. For \code{as_undirected()} it can be \code{each}, \code{collapse} or diff --git a/man/as_edgelist.Rd b/man/as_edgelist.Rd index c9541e46712..218bcecca7e 100644 --- a/man/as_edgelist.Rd +++ b/man/as_edgelist.Rd @@ -4,11 +4,13 @@ \alias{as_edgelist} \title{Convert a graph to an edge list} \usage{ -as_edgelist(graph, names = TRUE) +as_edgelist(graph, ..., names = TRUE) } \arguments{ \item{graph}{The graph to convert.} +\item{...}{These dots are for future extensions and must be empty.} + \item{names}{Whether to return a character matrix containing vertex names (i.e. the \code{name} vertex attribute) if they exist or numeric vertex IDs.} diff --git a/man/from_incidence_matrix.Rd b/man/from_incidence_matrix.Rd index 725efe0bb5f..7825135c191 100644 --- a/man/from_incidence_matrix.Rd +++ b/man/from_incidence_matrix.Rd @@ -6,6 +6,9 @@ \usage{ from_incidence_matrix(...) } +\arguments{ +\item{...}{These dots are for future extensions and must be empty.} +} \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/graph_from_adj_list.Rd b/man/graph_from_adj_list.Rd index 7ebdbe8d139..0104a57779f 100644 --- a/man/graph_from_adj_list.Rd +++ b/man/graph_from_adj_list.Rd @@ -6,6 +6,7 @@ \usage{ graph_from_adj_list( adjlist, + ..., mode = c("out", "in", "all", "total"), duplicate = TRUE ) @@ -15,6 +16,8 @@ graph_from_adj_list( throughout all vectors in the list must be less than the number of vectors (=the number of vertices in the graph).} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Character scalar, it specifies whether the graph to create is undirected (\sQuote{all} or \sQuote{total}) or directed; and in the latter case, whether it contains the outgoing (\sQuote{out}) or the incoming diff --git a/man/graph_from_adjacency_matrix.Rd b/man/graph_from_adjacency_matrix.Rd index 3503fdb97be..bf92df54a42 100644 --- a/man/graph_from_adjacency_matrix.Rd +++ b/man/graph_from_adjacency_matrix.Rd @@ -7,6 +7,7 @@ \usage{ graph_from_adjacency_matrix( adjmatrix, + ..., mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted = NULL, diag = TRUE, @@ -16,6 +17,7 @@ graph_from_adjacency_matrix( from_adjacency( adjmatrix, + ..., mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), weighted = NULL, diag = TRUE, @@ -27,6 +29,8 @@ from_adjacency( \item{adjmatrix}{A square adjacency matrix. From igraph version 0.5.1 this can be a sparse matrix created with the \code{Matrix} package.} +\item{...}{These dots are for future extensions and must be empty.} + \item{mode}{Character scalar, specifies how igraph should interpret the supplied matrix. See also the \code{weighted} argument, the interpretation depends on that too. Possible values are: \code{directed}, diff --git a/man/graph_from_biadjacency_matrix.Rd b/man/graph_from_biadjacency_matrix.Rd index e8bc2de2fd3..8cf04abf03e 100644 --- a/man/graph_from_biadjacency_matrix.Rd +++ b/man/graph_from_biadjacency_matrix.Rd @@ -6,6 +6,7 @@ \usage{ graph_from_biadjacency_matrix( incidence, + ..., directed = FALSE, mode = c("all", "out", "in", "total"), multiple = FALSE, @@ -17,6 +18,8 @@ graph_from_biadjacency_matrix( \item{incidence}{The input bipartite adjacency matrix. It can also be a sparse matrix from the \code{Matrix} package.} +\item{...}{These dots are for future extensions and must be empty.} + \item{directed}{Logical, whether to create a directed graph.} \item{mode}{A character constant, defines the direction of the edges in diff --git a/man/graph_from_data_frame.Rd b/man/graph_from_data_frame.Rd index 051542d9420..f435b5f650d 100644 --- a/man/graph_from_data_frame.Rd +++ b/man/graph_from_data_frame.Rd @@ -8,7 +8,7 @@ \usage{ as_data_frame(x, what = c("edges", "vertices", "both")) -graph_from_data_frame(d, directed = TRUE, vertices = NULL) +graph_from_data_frame(d, directed = TRUE, ..., vertices = NULL) from_data_frame(...) } @@ -25,11 +25,11 @@ version 0.7 this argument is coerced to a data frame with \item{directed}{Logical, whether or not to create a directed graph.} +\item{...}{Passed to \code{graph_from_data_frame()}.} + \item{vertices}{A data frame with vertex metadata, or \code{NULL}. See details below. Since version 0.7 this argument is coerced to a data frame with \code{as.data.frame}, if not \code{NULL}.} - -\item{...}{Passed to \code{graph_from_data_frame()}.} } \value{ An igraph graph object for \code{graph_from_data_frame()}, and either a diff --git a/man/graph_from_edgelist.Rd b/man/graph_from_edgelist.Rd index c5e15c71603..7d24a705763 100644 --- a/man/graph_from_edgelist.Rd +++ b/man/graph_from_edgelist.Rd @@ -5,16 +5,16 @@ \alias{from_edgelist} \title{Create a graph from an edge list matrix} \usage{ -graph_from_edgelist(el, directed = TRUE) +graph_from_edgelist(el, ..., directed = TRUE) from_edgelist(...) } \arguments{ \item{el}{The edge list, a two column matrix, character or numeric.} -\item{directed}{Whether to create a directed graph.} - \item{...}{Passed to \code{graph_from_edgelist()}.} + +\item{directed}{Whether to create a directed graph.} } \value{ An igraph graph. diff --git a/man/graph_from_graphnel.Rd b/man/graph_from_graphnel.Rd index 7741d81d942..d1bdc8853d5 100644 --- a/man/graph_from_graphnel.Rd +++ b/man/graph_from_graphnel.Rd @@ -4,11 +4,19 @@ \alias{graph_from_graphnel} \title{Convert graphNEL objects from the graph package to igraph} \usage{ -graph_from_graphnel(graphNEL, name = TRUE, weight = TRUE, unlist.attrs = TRUE) +graph_from_graphnel( + graphNEL, + ..., + name = TRUE, + weight = TRUE, + unlist.attrs = TRUE +) } \arguments{ \item{graphNEL}{The graphNEL graph.} +\item{...}{These dots are for future extensions and must be empty.} + \item{name}{Logical, whether to add graphNEL vertex names as an igraph vertex attribute called \sQuote{\code{name}}.} diff --git a/man/graph_from_incidence_matrix.Rd b/man/graph_from_incidence_matrix.Rd index fd84444aa97..3bc46309c90 100644 --- a/man/graph_from_incidence_matrix.Rd +++ b/man/graph_from_incidence_matrix.Rd @@ -6,6 +6,9 @@ \usage{ graph_from_incidence_matrix(...) } +\arguments{ +\item{...}{These dots are for future extensions and must be empty.} +} \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/tests/testthat/_snaps/incidence.md b/tests/testthat/_snaps/incidence.md index 9e0c14767e2..f27772f8cee 100644 --- a/tests/testthat/_snaps/incidence.md +++ b/tests/testthat/_snaps/incidence.md @@ -61,7 +61,7 @@ # graph_from_biadjacency_matrix() errors well Code - (graph_from_biadjacency_matrix(inc, weight = FALSE)) + (graph_from_biadjacency_matrix(inc, weighted = FALSE)) Condition Error in `graph_from_biadjacency_matrix()`: ! `weighted` can't be `FALSE`. @@ -70,7 +70,7 @@ --- Code - (graph_from_biadjacency_matrix(inc, weight = 42)) + (graph_from_biadjacency_matrix(inc, weighted = 42)) Condition Error in `graph_from_biadjacency_matrix()`: ! `weighted` can't be a number. diff --git a/tests/testthat/test-adjacency.R b/tests/testthat/test-adjacency.R index 4486e9c1c60..f87846a342f 100644 --- a/tests/testthat/test-adjacency.R +++ b/tests/testthat/test-adjacency.R @@ -948,3 +948,57 @@ test_that("graph_from_adjacency NA check for upper/lower with sparse matrices", graph_from_adjacency_matrix(sp2, mode = "upper", weighted = TRUE) ) }) + +# ---- ellipsis migration: argument coverage ---------------------------- + +test_that("graph_from_adjacency_matrix() recovers positional mode with a deprecation", { + # All tail arguments are already exercised by name elsewhere in this file, + # so only the legacy positional path is covered here. + M <- rbind(c(0, 1, 2), c(1, 3, 0), c(2, 0, 0)) + + lifecycle::expect_deprecated( + res <- graph_from_adjacency_matrix(M, "undirected") + ) + expect_identical_graphs( + res, + graph_from_adjacency_matrix(M, mode = "undirected") + ) + expect_false(is_directed(res)) +}) + +test_that("from_adjacency() covers tail args by name and recovers positional calls", { + M <- rbind(c(0, 1, 2), c(1, 3, 0), c(2, 0, 0)) + rownames(M) <- c("r1", "r2", "r3") + colnames(M) <- c("c1", "c2", "c3") + + g_spec <- make_(from_adjacency( + M, + mode = "undirected", + weighted = TRUE, + diag = FALSE, + add.colnames = "col_code", + add.rownames = "row_code" + )) + g_direct <- graph_from_adjacency_matrix( + M, + mode = "undirected", + weighted = TRUE, + diag = FALSE, + add.colnames = "col_code", + add.rownames = "row_code" + ) + expect_identical_graphs(g_spec, g_direct) + # diag = FALSE drops the loop on vertex 2. + expect_ecount(g_spec, 2) + expect_identical(sort(E(g_spec)$weight), c(1, 2)) + expect_identical(V(g_spec)$col_code, c("c1", "c2", "c3")) + expect_identical(V(g_spec)$row_code, c("r1", "r2", "r3")) + + lifecycle::expect_deprecated( + spec_legacy <- from_adjacency(M, "undirected") + ) + expect_identical_graphs( + make_(spec_legacy), + make_(from_adjacency(M, mode = "undirected")) + ) +}) diff --git a/tests/testthat/test-conversion.R b/tests/testthat/test-conversion.R index c98b678fd5a..9bf0395232f 100644 --- a/tests/testthat/test-conversion.R +++ b/tests/testthat/test-conversion.R @@ -30,14 +30,14 @@ test_that("as_directed keeps attributes", { expect_equal(V(g_arbitrary)$name, V(g)$name) E(g)$weight <- seq_len(ecount(g)) - g_mutual <- as_directed(g, "mutual") + g_mutual <- as_directed(g, mode = "mutual") df_mutual <- as_data_frame(g_mutual) expect_equal( df_mutual[order(df_mutual[, 1], df_mutual[, 2]), ]$weight, c(1, 3, 1, 2, 2, 3) ) - g_arbitrary <- as_directed(g, "arbitrary") + g_arbitrary <- as_directed(g, mode = "arbitrary") df_arbitrary <- as_data_frame(g_arbitrary) expect_equal( df_arbitrary[order(df_arbitrary[, 1], df_arbitrary[, 2]), ]$weight, @@ -1037,3 +1037,164 @@ test_that("as_adjacency_matrix() comprehensive snapshot tests", { expect_snapshot(as_adjacency_matrix(g_named, sparse = TRUE)) expect_snapshot(as_adjacency_matrix(g_named, sparse = FALSE)) }) + +# ---- ellipsis migration: argument coverage ---------------------------- + +test_that("as_adj_list() covers tail args by name and recovers positional calls", { + # Parallel 1-2 edges plus a loop on vertex 3 make loops and multiple visible. + g <- make_graph(c(1, 2, 1, 2, 2, 3, 3, 3), directed = FALSE) + + al_full <- as_adj_list(g, mode = "all", loops = "twice", multiple = TRUE) + expect_identical( + lapply(al_full, as.numeric), + list(c(2, 2), c(1, 1, 3), c(2, 3, 3)) + ) + + al_slim <- as_adj_list(g, mode = "all", loops = "ignore", multiple = FALSE) + expect_identical( + lapply(al_slim, as.numeric), + list(2, c(1, 3), 2) + ) + + # The positional mode is visible on a directed path. + g_dir <- make_graph(c(1, 2, 2, 3)) + lifecycle::expect_deprecated( + res <- as_adj_list(g_dir, "out") + ) + expect_identical( + lapply(res, as.numeric), + lapply(as_adj_list(g_dir, mode = "out"), as.numeric) + ) +}) + +test_that("as_adj_edge_list() covers tail args by name and recovers positional calls", { + # A triangle with a loop on vertex 1 makes the loops argument visible. + g <- make_graph(c(1, 2, 2, 3, 3, 1, 1, 1), directed = FALSE) + + # The loop edge is listed twice by default and dropped with "ignore". + ael_twice <- as_adj_edge_list(g, mode = "all", loops = "twice") + ael_ignore <- as_adj_edge_list(g, mode = "all", loops = "ignore") + expect_identical(as.numeric(ael_twice[[1]]), c(4, 4, 1, 3)) + expect_identical(as.numeric(ael_ignore[[1]]), c(1, 3)) + + g_dir <- make_graph(c(1, 2, 2, 3)) + lifecycle::expect_deprecated( + res <- as_adj_edge_list(g_dir, "out") + ) + expect_identical( + lapply(res, as.numeric), + lapply(as_adj_edge_list(g_dir, mode = "out"), as.numeric) + ) +}) + +test_that("as_directed() recovers positional mode with a deprecation", { + g <- make_ring(4) + + lifecycle::expect_deprecated( + res <- as_directed(g, "arbitrary") + ) + expect_identical_graphs(res, as_directed(g, mode = "arbitrary")) + # "arbitrary" keeps one directed edge per undirected edge. + expect_true(is_directed(res)) + expect_ecount(res, 4) +}) + +test_that("as_edgelist() covers names by name and recovers positional calls", { + g <- make_ring(3) + V(g)$name <- c("a", "b", "c") + + el_names <- as_edgelist(g, names = TRUE) + el_ids <- as_edgelist(g, names = FALSE) + expect_identical(el_names, rbind(c("a", "b"), c("b", "c"), c("a", "c"))) + expect_identical(el_ids, rbind(c(1, 2), c(2, 3), c(1, 3))) + + lifecycle::expect_deprecated( + res <- as_edgelist(g, FALSE) + ) + expect_identical(res, as_edgelist(g, names = FALSE)) +}) + +test_that("graph_from_adj_list() covers duplicate by name and recovers positional calls", { + g <- make_ring(6) + al <- as_adj_list(g, mode = "all") + + # Every ring edge appears twice in the adjacency list, + # so duplicate = FALSE keeps both copies as parallel edges. + g_dup <- graph_from_adj_list(al, mode = "all", duplicate = TRUE) + g_nodup <- graph_from_adj_list(al, mode = "all", duplicate = FALSE) + expect_ecount(g_dup, 6) + expect_ecount(g_nodup, 12) + expect_true(any(which_multiple(g_nodup))) + expect_isomorphic(g, g_dup) + + lifecycle::expect_deprecated( + res <- graph_from_adj_list(al, "all") + ) + expect_identical_graphs(res, graph_from_adj_list(al, mode = "all")) +}) + +test_that("graph_from_data_frame() recovers positional vertices with a deprecation", { + edges <- data.frame(from = c("a", "b"), to = c("b", "c")) + verts <- data.frame(name = c("a", "b", "c"), size = 1:3) + + lifecycle::expect_deprecated( + res <- graph_from_data_frame(edges, TRUE, verts) + ) + expect_identical_graphs( + res, + graph_from_data_frame(edges, directed = TRUE, vertices = verts) + ) + # The vertex metadata from the recovered argument is present. + expect_identical(V(res)$size, 1:3) +}) + +test_that("graph_from_edgelist() recovers positional directed with a deprecation", { + el <- cbind(1:4, c(2:4, 1)) + + lifecycle::expect_deprecated( + res <- graph_from_edgelist(el, FALSE) + ) + expect_identical_graphs(res, graph_from_edgelist(el, directed = FALSE)) + expect_false(is_directed(res)) + expect_ecount(res, 4) +}) + +test_that("graph_from_graphnel() covers tail args by name and recovers positional calls", { + skip_if_not_installed("graph") + + # Round-trip fixture with vertex names, an extra vertex attribute, + # and edge weights. + g <- make_ring(4) + V(g)$name <- letters[1:4] + V(g)$score <- 1:4 + E(g)$weight <- c(1, 2, 3, 4) + gnel <- as_graphnel(g) + + g_full <- graph_from_graphnel( + gnel, + name = TRUE, + weight = TRUE, + unlist.attrs = TRUE + ) + expect_isomorphic(g_full, g) + expect_identical(V(g_full)$name, letters[1:4]) + expect_true(is_weighted(g_full)) + expect_equal(sort(E(g_full)$weight), c(1, 2, 3, 4)) + expect_equal(V(g_full)$score, 1:4, ignore_attr = TRUE) + + g_min <- graph_from_graphnel( + gnel, + name = FALSE, + weight = FALSE, + unlist.attrs = FALSE + ) + expect_false("name" %in% vertex_attr_names(g_min)) + expect_false(is_weighted(g_min)) + # With unlist.attrs = FALSE the attribute values stay wrapped in a list. + expect_true(is.list(V(g_min)$score)) + + lifecycle::expect_deprecated( + res <- graph_from_graphnel(gnel, FALSE) + ) + expect_identical_graphs(res, graph_from_graphnel(gnel, name = FALSE)) +}) diff --git a/tests/testthat/test-incidence.R b/tests/testthat/test-incidence.R index d960b12b6ab..90646d23f5d 100644 --- a/tests/testthat/test-incidence.R +++ b/tests/testthat/test-incidence.R @@ -210,10 +210,10 @@ test_that("graph_from_biadjacency_matrix() errors well", { rownames(inc) <- LETTERS[1:3] expect_snapshot_igraph_error({ - (graph_from_biadjacency_matrix(inc, weight = FALSE)) + (graph_from_biadjacency_matrix(inc, weighted = FALSE)) }) expect_snapshot_igraph_error({ - (graph_from_biadjacency_matrix(inc, weight = 42)) + (graph_from_biadjacency_matrix(inc, weighted = 42)) }) expect_snapshot_igraph_error({ (graph_from_biadjacency_matrix(inc, multiple = TRUE, weighted = TRUE)) @@ -224,3 +224,30 @@ test_that("graph_from_biadjacency_matrix errors for NAs", { A <- matrix(c(1, 1, NA, 1), 2, 2) expect_snapshot_igraph_error(graph_from_biadjacency_matrix(A)) }) + +# ---- ellipsis migration: argument coverage ---------------------------- + +test_that("graph_from_biadjacency_matrix() covers add.names by name and recovers positional calls", { + # The other tail arguments are already exercised by name elsewhere + # in this file, so only add.names and the legacy positional path + # are covered here. + inc <- matrix(c(1, 0, 0, 1, 1, 1), nrow = 2) + rownames(inc) <- c("r1", "r2") + colnames(inc) <- c("c1", "c2", "c3") + + g_label <- graph_from_biadjacency_matrix(inc, add.names = "label") + expect_identical(V(g_label)$label, c("r1", "r2", "c1", "c2", "c3")) + expect_false("name" %in% vertex_attr_names(g_label)) + + g_anon <- graph_from_biadjacency_matrix(inc, add.names = NA) + expect_identical(vertex_attr_names(g_anon), "type") + + lifecycle::expect_deprecated( + res <- graph_from_biadjacency_matrix(inc, TRUE) + ) + expect_identical_graphs( + res, + graph_from_biadjacency_matrix(inc, directed = TRUE) + ) + expect_true(is_directed(res)) +}) diff --git a/tests/testthat/test-indexing.R b/tests/testthat/test-indexing.R index 9dc75f54d28..c11da9ec52d 100644 --- a/tests/testthat/test-indexing.R +++ b/tests/testthat/test-indexing.R @@ -380,7 +380,7 @@ test_that("[ handles all combinations of i and/or j", { nrow = 10L, ncol = 10L ) - g <- graph_from_adjacency_matrix(A, "directed") + g <- graph_from_adjacency_matrix(A, mode = "directed") expect_equal(as_unnamed_dense_matrix(g[1:3, ]), A[1:3, ]) expect_equal(as_unnamed_dense_matrix(g[, 4:7]), A[, 4:7]) expect_equal(as_unnamed_dense_matrix(g[1:3, 4:7]), A[1:3, 4:7]) @@ -398,7 +398,7 @@ test_that("[ handles duplicated i/j well", { nrow = 10L, ncol = 10L ) - g <- graph_from_adjacency_matrix(A, "directed") + g <- graph_from_adjacency_matrix(A, mode = "directed") expect_equal(as_unnamed_dense_matrix(g[c(1, 2, 2), ]), A[c(1, 2, 2), ]) expect_equal(as_unnamed_dense_matrix(g[, c(3, 3, 4, 4)]), A[, c(3, 3, 4, 4)]) expect_equal( diff --git a/tools/migrations/conversion.R b/tools/migrations/conversion.R index 1aaffbe4d9a..f70729ddca7 100644 --- a/tools/migrations/conversion.R +++ b/tools/migrations/conversion.R @@ -35,5 +35,156 @@ migrations <- list( attr = deprecated() ) {}, when = "3.0.0" + ), + + from_adjacency = list( + old = function( + adjmatrix, + mode, + weighted, + diag, + add.colnames, + add.rownames + ) {}, + new = function( + adjmatrix, + ..., + mode = c("directed", "undirected", "max", "min", "upper", "lower", "plus"), + weighted = NULL, + diag = TRUE, + add.colnames = NULL, + add.rownames = NA + ) {}, + when = "3.0.0" + ), + + graph_from_adjacency_matrix = list( + old = function( + adjmatrix, + mode, + weighted, + diag, + add.colnames, + add.rownames + ) {}, + new = function( + adjmatrix, + ..., + mode = c( + "directed", + "undirected", + "max", + "min", + "upper", + "lower", + "plus" + ), + weighted = NULL, + diag = TRUE, + add.colnames = NULL, + add.rownames = NA + ) {}, + when = "3.0.0" + ), + + as_adj_edge_list = list( + old = function(graph, mode, loops) {}, + new = function( + graph, + ..., + mode = c("all", "out", "in", "total"), + loops = c("twice", "once", "ignore") + ) {}, + when = "3.0.0" + ), + + as_adj_list = list( + old = function(graph, mode, loops, multiple) {}, + new = function( + graph, + ..., + mode = c("all", "out", "in", "total"), + loops = c("twice", "once", "ignore"), + multiple = TRUE + ) {}, + when = "3.0.0" + ), + + as_directed = list( + old = function(graph, mode) {}, + new = function( + graph, + ..., + mode = c("mutual", "arbitrary", "random", "acyclic") + ) {}, + when = "3.0.0" + ), + + as_edgelist = list( + old = function(graph, names) {}, + new = function( + graph, + ..., + names = TRUE + ) {}, + when = "3.0.0" + ), + + graph_from_adj_list = list( + old = function(adjlist, mode, duplicate) {}, + new = function( + adjlist, + ..., + mode = c("out", "in", "all", "total"), + duplicate = TRUE + ) {}, + when = "3.0.0" + ), + + graph_from_data_frame = list( + old = function(d, directed, vertices) {}, + new = function( + d, + directed = TRUE, + ..., + vertices = NULL + ) {}, + when = "3.0.0" + ), + + graph_from_edgelist = list( + old = function(el, directed) {}, + new = function( + el, + ..., + directed = TRUE + ) {}, + when = "3.0.0" + ), + + graph_from_graphnel = list( + old = function(graphNEL, name, weight, unlist.attrs) {}, + new = function( + graphNEL, + ..., + name = TRUE, + weight = TRUE, + unlist.attrs = TRUE + ) {}, + when = "3.0.0" + ), + + graph_from_biadjacency_matrix = list( + old = function(incidence, directed, mode, multiple, weighted, add.names) {}, + new = function( + incidence, + ..., + directed = FALSE, + mode = c("all", "out", "in", "total"), + multiple = FALSE, + weighted = NULL, + add.names = NULL + ) {}, + when = "3.0.0" ) )