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
29 changes: 28 additions & 1 deletion R/cycles.R
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -49,7 +50,33 @@
#' @family cycles
#' @export

find_cycle <- function(graph, mode = c("out", "in", "all", "total")) {
find_cycle <- function(
graph,
...,
mode = c("out", "in", "all", "total")
) {
# BEGIN GENERATED ARG_HANDLE: find_cycle, 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("out", "in", "all", "total")),
head_args = c("graph"),
fn_name = "find_cycle"
)
list2env(.arg_handle$values, environment())
lifecycle::deprecate_soft(
"3.0.0",
what = I(.arg_handle$what),
details = .arg_handle$details
)
}
# END GENERATED ARG_HANDLE

find_cycle_impl(
graph = graph,
mode = mode
Expand Down
56 changes: 56 additions & 0 deletions R/random_walk.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#' @param graph The input graph, might be undirected or directed.
#' @param start The start vertex.
#' @param steps The number of steps to make.
#' @inheritParams rlang::args_dots_empty
#' @param weights The edge weights. Larger edge weights increase the
#' probability that an edge is selected by the random walker. In other
#' words, larger edge weights correspond to stronger connections. The
Expand Down Expand Up @@ -51,10 +52,37 @@ random_walk <- function(
graph,
start,
steps,
...,
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
) {
# BEGIN GENERATED ARG_HANDLE: random_walk, do not edit, see tools/generate-migrations.R
if (...length() > 0L) {
.arg_handle <- migrate_recover_args(
list(...),
current = list(weights = weights, mode = mode, stuck = stuck),
recover_new = c("weights", "mode", "stuck"),
recover_old = c("weights", "mode", "stuck"),
match_names = c("weights", "mode", "stuck"),
match_to = c("weights", "mode", "stuck"),
defaults = list(
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
),
head_args = c("graph", "start", "steps"),
fn_name = "random_walk"
)
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 <- match.arg(mode)
stuck <- match.arg(stuck)
out <- random_walk_impl(
Expand All @@ -71,15 +99,43 @@ random_walk <- function(
}

#' @rdname random_walk
#' @inheritParams rlang::args_dots_empty
#' @export
random_edge_walk <- function(
graph,
start,
steps,
...,
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
) {
# BEGIN GENERATED ARG_HANDLE: random_edge_walk, do not edit, see tools/generate-migrations.R
if (...length() > 0L) {
.arg_handle <- migrate_recover_args(
list(...),
current = list(weights = weights, mode = mode, stuck = stuck),
recover_new = c("weights", "mode", "stuck"),
recover_old = c("weights", "mode", "stuck"),
match_names = c("weights", "mode", "stuck"),
match_to = c("weights", "mode", "stuck"),
defaults = list(
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
),
head_args = c("graph", "start", "steps"),
fn_name = "random_edge_walk"
)
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 <- match.arg(mode)
stuck <- match.arg(stuck)
out <- random_walk_impl(
Expand Down
4 changes: 3 additions & 1 deletion man/find_cycle.Rd

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

4 changes: 4 additions & 0 deletions man/random_walk.Rd

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

21 changes: 21 additions & 0 deletions tests/testthat/test-cycles.R
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,24 @@ test_that("simple_cycles_callback handles errors in callback", {
"Error in R callback function"
)
})

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

test_that("find_cycle mode variants distinguish edge orientation", {
# A directed acyclic triangle is only cyclic when directions are ignored.
g <- make_graph(c(1, 2, 1, 3, 2, 3), directed = TRUE)

expect_length(find_cycle(g)$vertices, 0)
expect_length(find_cycle(g, mode = "in")$vertices, 0)

cyc <- find_cycle(g, mode = "all")
expect_length(cyc$vertices, 3)
expect_length(cyc$edges, 3)
})

test_that("find_cycle recovers legacy positional arguments", {
g <- make_graph(c(1, 2, 1, 3, 2, 3), directed = TRUE)
lifecycle::expect_deprecated(res <- find_cycle(g, "all"))
expect_equal(res, find_cycle(g, mode = "all"))
expect_length(res$vertices, 3)
})
49 changes: 49 additions & 0 deletions tests/testthat/test-random_walk.R
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,52 @@ test_that("directed random_edge_walk can return wtih an error when stuck", {
"Random walk got stuck"
)
})

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

test_that("random_walk follows edge weights and returns partial walks when stuck", {
igraph_local_seed(42)

# The heavy edge to vertex 4 forces the walk to bounce between 1 and 4.
g <- make_star(5, mode = "undirected")
w <- random_walk(g, start = 1, steps = 6, weights = c(1, 1, 1e12, 1))
expect_equal(as_ids(w), rep_len(c(1, 4), 7))

# A walk starting on a sink vertex stops immediately with stuck = "return".
g2 <- make_star(11, mode = "out")
w2 <- random_walk(g2, start = 7, steps = 10, stuck = "return")
expect_equal(as_ids(w2), 7)
})

test_that("random_walk recovers legacy positional arguments", {
g <- make_star(5, mode = "undirected")
lifecycle::expect_deprecated(
res <- igraph_with_seed(42, random_walk(g, 1, 6, c(1, 1, 1e12, 1)))
)
expect_equal(
res,
igraph_with_seed(42, random_walk(g, 1, 6, weights = c(1, 1, 1e12, 1)))
)
expect_equal(as_ids(res), rep_len(c(1, 4), 7))
})

test_that("random_edge_walk follows edge weights", {
igraph_local_seed(42)
# A two-cycle with one heavy edge keeps the walker crossing that edge.
g <- make_ring(2)
w <- random_edge_walk(g, start = 1, steps = 8, weights = c(1e12, 1))
expect_length(w, 8)
expect_equal(as.integer(w), rep(1L, 8))
})

test_that("random_edge_walk recovers legacy positional arguments", {
g <- make_ring(2)
lifecycle::expect_deprecated(
res <- igraph_with_seed(42, random_edge_walk(g, 1, 8, c(1e12, 1)))
)
expect_equal(
res,
igraph_with_seed(42, random_edge_walk(g, 1, 8, weights = c(1e12, 1)))
)
expect_equal(as.integer(res), rep(1L, 8))
})
43 changes: 43 additions & 0 deletions tools/migrations/walks-cycles.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Argument-signature migrations: walks-cycles
# Schema: see tools/migrations/README.md. Regenerate with:
# Rscript tools/generate-migrations.R

migrations <- list(
find_cycle = list(
old = function(graph, mode) {},
new = function(
graph,
...,
mode = c("out", "in", "all", "total")
) {},
when = "3.0.0"
),

random_edge_walk = list(
old = function(graph, start, steps, weights, mode, stuck) {},
new = function(
graph,
start,
steps,
...,
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
) {},
when = "3.0.0"
),

random_walk = list(
old = function(graph, start, steps, weights, mode, stuck) {},
new = function(
graph,
start,
steps,
...,
weights = NULL,
mode = c("out", "in", "all", "total"),
stuck = c("return", "error")
) {},
when = "3.0.0"
)
)
Loading