Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion R/operators.R
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,7 @@ difference.igraph <- function(big, small, byname = "auto", ...) {
#' attributes are lost.
#'
#' @param graph The input graph, can be directed or undirected.
#' @inheritParams rlang::args_dots_empty
#' @param loops Logical, whether to generate loop edges.
#' @return A new graph object.
#' @author Gabor Csardi \email{csardi.gabor@@gmail.com}
Expand All @@ -896,7 +897,33 @@ difference.igraph <- function(big, small, byname = "auto", ...) {
#' gu
#' isomorphic(gu, make_full_graph(vcount(g)))
#'
complementer <- function(graph, loops = FALSE) {
complementer <- function(
graph,
...,
loops = FALSE
) {
# BEGIN GENERATED ARG_HANDLE: complementer, do not edit, see tools/generate-migrations.R
if (...length() > 0L) {
.arg_handle <- migrate_recover_args(
list(...),
current = list(loops = loops),
recover_new = c("loops"),
recover_old = c("loops"),
match_names = c("loops"),
match_to = c("loops"),
defaults = list(loops = FALSE),
head_args = c("graph"),
fn_name = "complementer"
)
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)

complementer_impl(graph = graph, loops = as.logical(loops))
Expand Down Expand Up @@ -948,6 +975,7 @@ complementer <- function(graph, loops = FALSE) {
#' @aliases %c%
#' @param g1 The first input graph.
#' @param g2 The second input graph.
#' @inheritParams rlang::args_dots_empty
#' @param byname A Logical, or the character scalar `auto`. Whether
#' to perform the operation based on symbolic vertex names. If it is
#' `auto`, that means `TRUE` if both graphs are named and
Expand Down Expand Up @@ -975,11 +1003,64 @@ complementer <- function(graph, loops = FALSE) {
compose <- function(
g1,
g2,
...,
byname = "auto",
graph.attr.comb = igraph_opt("graph.attr.comb"),
vertex.attr.comb = "rename",
edge.attr.comb = "rename"
) {
# BEGIN GENERATED ARG_HANDLE: compose, do not edit, see tools/generate-migrations.R
if (...length() > 0L) {
.arg_handle <- migrate_recover_args(
list(...),
current = list(
byname = byname,
graph.attr.comb = graph.attr.comb,
vertex.attr.comb = vertex.attr.comb,
edge.attr.comb = edge.attr.comb
),
recover_new = c(
"byname",
"graph.attr.comb",
"vertex.attr.comb",
"edge.attr.comb"
),
recover_old = c(
"byname",
"graph.attr.comb",
"vertex.attr.comb",
"edge.attr.comb"
),
match_names = c(
"byname",
"graph.attr.comb",
"vertex.attr.comb",
"edge.attr.comb"
),
match_to = c(
"byname",
"graph.attr.comb",
"vertex.attr.comb",
"edge.attr.comb"
),
defaults = list(
byname = "auto",
graph.attr.comb = igraph_opt("graph.attr.comb"),
vertex.attr.comb = "rename",
edge.attr.comb = "rename"
),
head_args = c("g1", "g2"),
fn_name = "compose"
)
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(g1)
ensure_igraph(g2)

Expand Down
28 changes: 28 additions & 0 deletions R/rewire.R
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ rewire_keeping_degseq <- function(graph, loops, niter) {
#' Note that this method might create graphs with multiple and/or loop edges.
#'
#' @param prob The rewiring probability, a real number between zero and one.
#' @inheritParams rlang::args_dots_empty
#' @param loops Logical, whether loop edges are allowed in the rewired
#' graph.
#' @param multiple Logical, whether multiple edges are allowed in the
Expand Down Expand Up @@ -130,10 +131,37 @@ rewire_keeping_degseq <- function(graph, loops, niter) {
#' degree(g, mode = "in") == degree(g2, mode = "in")
each_edge <- function(
prob,
...,
loops = FALSE,
multiple = FALSE,
mode = c("all", "out", "in", "total")
) {
# BEGIN GENERATED ARG_HANDLE: each_edge, do not edit, see tools/generate-migrations.R
if (...length() > 0L) {
.arg_handle <- migrate_recover_args(
list(...),
current = list(loops = loops, multiple = multiple, mode = mode),
recover_new = c("loops", "multiple", "mode"),
recover_old = c("loops", "multiple", "mode"),
match_names = c("loops", "multiple", "mode"),
match_to = c("loops", "multiple", "mode"),
defaults = list(
loops = FALSE,
multiple = FALSE,
mode = c("all", "out", "in", "total")
),
head_args = c("prob"),
fn_name = "each_edge"
)
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)
multiple <- as.logical(multiple)
if (mode != "all" && mode != "total") {
Expand Down
4 changes: 3 additions & 1 deletion man/complementer.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions man/compose.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions man/each_edge.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions tests/testthat/test-operators.R
Original file line number Diff line number Diff line change
Expand Up @@ -1413,3 +1413,60 @@ test_that("simplify() rejects 'rename' combiner", {
"rename"
)
})

# ---- ellipsis migration: argument coverage ----------------------------------

test_that("complementer() takes `loops` by name and recovers it positionally", {
g <- make_ring(4)

# The complement of C4 has the two missing cross edges plus one loop per vertex.
gc <- complementer(g, loops = TRUE)
expect_ecount(gc, 6)
expect_equal(sum(which_loop(gc)), 4)

rlang::local_options(lifecycle_verbosity = "warning")
lifecycle::expect_deprecated(
res <- complementer(g, TRUE)
)
expect_identical_graphs(res, complementer(g, loops = TRUE))
})

test_that("compose() takes all tail arguments by name", {
g1 <- graph_from_literal(A -+ B, B -+ C)
g2 <- graph_from_literal(D -+ E, E -+ F)
g1$kind <- "one"
g2$kind <- "two"
V(g1)$score <- 1:3
V(g2)$score <- c(10, 20, 30)
E(g1)$w <- c(1, 2)
E(g2)$w <- c(10, 20)

res <- compose(
g1,
g2,
byname = FALSE,
graph.attr.comb = "first",
vertex.attr.comb = "first",
edge.attr.comb = "concat"
)

# By vertex ID the graphs overlap; by name they are disjoint (6 vertices, no edge).
expect_vcount(res, 3)
expect_ecount(res, 1)
expect_equal(as_edgelist(res), cbind("A", "C"))
# "first" keeps the first graph's attribute, "concat" concatenates both edges'.
expect_equal(res$kind, "one")
expect_equal(V(res)$score, 1:3)
expect_equal(E(res)$w, list(c(1, 20)))
})

test_that("compose() recovers legacy positional arguments", {
g1 <- graph_from_literal(A -+ B, B -+ C)
g2 <- graph_from_literal(D -+ E, E -+ F)

rlang::local_options(lifecycle_verbosity = "warning")
lifecycle::expect_deprecated(
res <- compose(g1, g2, FALSE)
)
expect_identical_graphs(res, compose(g1, g2, byname = FALSE))
})
30 changes: 30 additions & 0 deletions tests/testthat/test-rewire.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,33 @@ test_that("rewire() with zero probability does not do anything", {
g2 <- g %>% rewire(each_edge(prob = 0))
expect_identical_graphs(g, g2)
})

# ---- ellipsis migration: argument coverage ----------------------------------

test_that("each_edge() takes all tail arguments by name", {
igraph_local_seed(42)
g <- sample_pa(100)

g2 <- g %>%
rewire(each_edge(prob = 1, loops = TRUE, multiple = TRUE, mode = "out"))
# Rewiring only the heads keeps the out-degree sequence and the edge count.
expect_equal(degree(g2, mode = "out"), degree(g, mode = "out"))
expect_ecount(g2, ecount(g))
# With this seed the rewiring uses the freedom that `loops = TRUE` grants.
expect_true(any(which_loop(g2)))

# On a single-vertex graph the rewired edge can only stay a loop.
g_loop <- make_graph(c(1, 1))
expect_identical_graphs(
rewire(g_loop, each_edge(prob = 1, loops = TRUE, multiple = TRUE)),
g_loop
)
})

test_that("each_edge() recovers legacy positional arguments", {
rlang::local_options(lifecycle_verbosity = "warning")
lifecycle::expect_deprecated(
res <- each_edge(0.3, TRUE)
)
expect_identical(res, each_edge(0.3, loops = TRUE))
})
48 changes: 48 additions & 0 deletions tools/migrations/operators.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Argument-signature migrations: operators
# Schema: see tools/migrations/README.md. Regenerate with:
# Rscript tools/generate-migrations.R

migrations <- list(
complementer = list(
old = function(graph, loops) {},
new = function(
graph,
...,
loops = FALSE
) {},
when = "3.0.0"
),

compose = list(
old = function(
g1,
g2,
byname,
graph.attr.comb,
vertex.attr.comb,
edge.attr.comb
) {},
new = function(
g1,
g2,
...,
byname = "auto",
graph.attr.comb = igraph_opt("graph.attr.comb"),
vertex.attr.comb = "rename",
edge.attr.comb = "rename"
) {},
when = "3.0.0"
),

each_edge = list(
old = function(prob, loops, multiple, mode) {},
new = function(
prob,
...,
loops = FALSE,
multiple = FALSE,
mode = c("all", "out", "in", "total")
) {},
when = "3.0.0"
)
)
Loading