diff --git a/NAMESPACE b/NAMESPACE index 6735ed9b748..dbfeb04b9c0 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -577,6 +577,7 @@ export(is_max_matching) export(is_min_separator) export(is_named) export(is_printer_callback) +export(is_same_graph) export(is_separator) export(is_simple) export(is_subgraph_isomorphic_to) diff --git a/R/iterators.R b/R/iterators.R index 15e1875e434..ae7da6d1cd3 100644 --- a/R/iterators.R +++ b/R/iterators.R @@ -78,6 +78,8 @@ get_es_graph_id <- get_vs_graph_id <- function(seq) { #' @inheritParams rlang::args_dots_empty #' @param attrs Whether to compare the attributes of the graphs #' @return Logical scalar +#' @seealso [is_same_graph()] to ignore the order of vertices and edges, +#' [isomorphic()] to ignore vertex labels altogether. #' @export identical_graphs <- function( g1, @@ -124,6 +126,151 @@ identical_graphs <- function( .Call(Rx_igraph_identical_graphs, g1, g2, as.logical(attrs)) } +#' Decide if two graphs are the same as labelled graphs +#' +#' @description +#' Two graphs are the same as labelled graphs if they have the same +#' directedness, the same vertices and the same edges. +#' Unlike [identical_graphs()], the order in which vertices and edges are +#' stored does not matter. +#' Unlike [isomorphic()], vertices are not relabelled. +#' +#' @details +#' Vertices can be identified by their index or by their name, +#' and `use_names` controls which one is used. +#' If `use_names` is `TRUE` and both graphs have a `name` vertex attribute, +#' a vertex of `g1` corresponds to the vertex with the same name in `g2`, +#' wherever it is stored. +#' Vertex names must be unique in both graphs in this case. +#' Otherwise, the \eqn{i}-th vertex of `g1` corresponds to the +#' \eqn{i}-th vertex of `g2`. +#' +#' Two graphs have the same edges if every edge occurs equally often in both. +#' The order of the edges does not matter, +#' and neither does the order of the two endpoints of an undirected edge. +#' +#' Vertex attributes listed in `vertex_attrs` must also be identical, +#' after matching up the vertices. +#' They are compared exactly, with [identical()], +#' so for example the integer `1L` and the double `1` are different values. +#' Graph and edge attributes are never compared, +#' and neither are vertex attributes other than those in `vertex_attrs`. +#' +#' The three ways of comparing graphs answer different questions: +#' - [identical_graphs()]: do the two objects store exactly the same data, +#' in the same order? +#' This is mostly useful in tests, +#' or before transferring edge attributes from one graph to the other by +#' position. +#' - `is_same_graph()`: do the same vertices have the same connections? +#' - [isomorphic()]: is there any way to relabel the vertices of one graph +#' so that it becomes the same as the other? +#' +#' Each check is stricter than the next one: +#' identical graphs are always the same, +#' and graphs that are the same are always isomorphic, +#' but not the other way round. +#' +#' @param g1,g2 The two graphs. +#' @inheritParams rlang::args_dots_empty +#' @param use_names Logical scalar, whether to identify vertices by their +#' names if both graphs have a `name` vertex attribute. +#' @param vertex_attrs Character vector of vertex attribute names to compare, +#' or `NULL` to compare none. +#' Every attribute must exist in both graphs. +#' Including `"name"` is only meaningful with `use_names = FALSE`, +#' otherwise names match by construction. +#' @return A logical scalar, `TRUE` if the two graphs are the same. +#' @seealso [identical_graphs()] for comparing the internal representation, +#' [isomorphic()] for comparing graphs up to relabelling of the vertices. +#' @export +#' @examples +#' # 1-2, 3-2 and 2-3, 1-2 only differ in the order of the edges and of +#' # their endpoints, so they are the same graph. +#' g1 <- make_graph(c(1, 2, 3, 2), directed = FALSE) +#' g2 <- make_graph(c(2, 3, 1, 2), directed = FALSE) +#' identical_graphs(g1, g2) +#' is_same_graph(g1, g2) +#' +#' # 1-3, 2-3 is isomorphic to them, but not the same: +#' # it contains the edge 1-3, which g1 does not. +#' g3 <- make_graph(c(1, 3, 2, 3), directed = FALSE) +#' is_same_graph(g1, g3) +#' isomorphic(g1, g3) +#' +#' # After bringing both graphs into canonical form, +#' # is_same_graph() tests for isomorphism. +#' # For named graphs, use `use_names = FALSE`: +#' # matching vertices by name would undo the reordering. +#' canon <- function(g) permute(g, canonical_permutation(g)$labeling) +#' is_same_graph(canon(g1), canon(g3)) +#' +#' # Named vertices stored in a different order +#' edges <- data.frame(from = c("a", "b"), to = c("b", "c")) +#' g4 <- graph_from_data_frame(edges, vertices = c("a", "b", "c")) +#' g5 <- graph_from_data_frame(edges, vertices = c("c", "b", "a")) +#' identical_graphs(g4, g5) +#' is_same_graph(g4, g5) +#' is_same_graph(g4, g5, use_names = FALSE) +#' +#' # Vertex-coloured graphs: same edges, different colours +#' star <- make_star(4, mode = "undirected") +#' c1 <- set_vertex_attr(star, "color", value = c(1, 2, 2, 2)) +#' c2 <- set_vertex_attr(star, "color", value = c(1, 3, 3, 3)) +#' is_same_graph(c1, c2) +#' is_same_graph(c1, c2, vertex_attrs = "color") +is_same_graph <- function(g1, g2, ..., use_names = TRUE, vertex_attrs = NULL) { + check_dots_empty() + ensure_igraph(g1) + ensure_igraph(g2) + check_bool(use_names) + check_character(vertex_attrs, allow_na = FALSE, allow_null = TRUE) + + missing1 <- setdiff(vertex_attrs, vertex_attr_names(g1)) + missing2 <- setdiff(vertex_attrs, vertex_attr_names(g2)) + if (length(missing1) > 0 || length(missing2) > 0) { + cli::cli_abort(c( + "All {.arg vertex_attrs} must exist in both graphs.", + x = if (length(missing1) > 0) "Missing in {.arg g1}: {.val {missing1}}.", + x = if (length(missing2) > 0) "Missing in {.arg g2}: {.val {missing2}}." + )) + } + + if (use_names && xor(is_named(g1), is_named(g2))) { + cli::cli_warn(c( + "Only one of the graphs has vertex names, matching vertices by their IDs.", + i = "Use {.code use_names = FALSE} to silence this warning." + )) + } + + if (use_names && is_named(g1) && is_named(g2)) { + names1 <- V(g1)$name + names2 <- V(g2)$name + if (anyDuplicated(names1) || anyDuplicated(names2)) { + cli::cli_abort(c( + "Vertex names must be unique to match vertices by name.", + i = "Use {.code use_names = FALSE} to match vertices by their IDs." + )) + } + if (length(names1) != length(names2) || !setequal(names1, names2)) { + return(FALSE) + } + g2 <- permute(g2, match(names2, names1)) + } + + if (!is_same_graph_impl(graph1 = g1, graph2 = g2)) { + return(FALSE) + } + + for (attr in vertex_attrs) { + if (!identical(vertex_attr(g1, attr), vertex_attr(g2, attr))) { + return(FALSE) + } + } + + TRUE +} + add_vses_graph_ref <- function(vses, graph) { ref <- get_vs_ref(graph) if (!is.null(ref)) { diff --git a/_pkgdown.yml b/_pkgdown.yml index 22fbbfcc819..807e42dadd1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -93,6 +93,7 @@ reference: - contents: - graph_id - identical_graphs + - is_same_graph - is_igraph - is_named - is_weighted diff --git a/man/identical_graphs.Rd b/man/identical_graphs.Rd index ed60154f8c6..3de63d6d4fc 100644 --- a/man/identical_graphs.Rd +++ b/man/identical_graphs.Rd @@ -32,3 +32,7 @@ different even if the two graphs are identical. Attribute comparison can be turned off with the \code{attrs} parameter if the attributes of the two graphs are allowed to be different. } +\seealso{ +\code{\link[=is_same_graph]{is_same_graph()}} to ignore the order of vertices and edges, +\code{\link[=isomorphic]{isomorphic()}} to ignore vertex labels altogether. +} diff --git a/man/is_same_graph.Rd b/man/is_same_graph.Rd new file mode 100644 index 00000000000..d249fa77516 --- /dev/null +++ b/man/is_same_graph.Rd @@ -0,0 +1,114 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/iterators.R +\name{is_same_graph} +\alias{is_same_graph} +\title{Decide if two graphs are the same as labelled graphs} +\usage{ +is_same_graph(g1, g2, ..., use_names = TRUE, vertex_attrs = NULL) +} +\arguments{ +\item{g1, g2}{The two graphs.} + +\item{...}{These dots are for future extensions and must be empty.} + +\item{use_names}{Logical scalar, whether to identify vertices by their +names if both graphs have a \code{name} vertex attribute.} + +\item{vertex_attrs}{Character vector of vertex attribute names to compare, +or \code{NULL} to compare none. +Every attribute must exist in both graphs. +Including \code{"name"} is only meaningful with \code{use_names = FALSE}, +otherwise names match by construction.} +} +\value{ +A logical scalar, \code{TRUE} if the two graphs are the same. +} +\description{ +Two graphs are the same as labelled graphs if they have the same +directedness, the same vertices and the same edges. +Unlike \code{\link[=identical_graphs]{identical_graphs()}}, the order in which vertices and edges are +stored does not matter. +Unlike \code{\link[=isomorphic]{isomorphic()}}, vertices are not relabelled. +} +\details{ +Vertices can be identified by their index or by their name, +and \code{use_names} controls which one is used. +If \code{use_names} is \code{TRUE} and both graphs have a \code{name} vertex attribute, +a vertex of \code{g1} corresponds to the vertex with the same name in \code{g2}, +wherever it is stored. +Vertex names must be unique in both graphs in this case. +Otherwise, the \eqn{i}-th vertex of \code{g1} corresponds to the +\eqn{i}-th vertex of \code{g2}. + +Two graphs have the same edges if every edge occurs equally often in both. +The order of the edges does not matter, +and neither does the order of the two endpoints of an undirected edge. + +Vertex attributes listed in \code{vertex_attrs} must also be identical, +after matching up the vertices. +They are compared exactly, with \code{\link[=identical]{identical()}}, +so for example the integer \code{1L} and the double \code{1} are different values. +Graph and edge attributes are never compared, +and neither are vertex attributes other than those in \code{vertex_attrs}. + +The three ways of comparing graphs answer different questions: +\itemize{ +\item \code{\link[=identical_graphs]{identical_graphs()}}: do the two objects store exactly the same data, +in the same order? +This is mostly useful in tests, +or before transferring edge attributes from one graph to the other by +position. +\item \code{is_same_graph()}: do the same vertices have the same connections? +\item \code{\link[=isomorphic]{isomorphic()}}: is there any way to relabel the vertices of one graph +so that it becomes the same as the other? +} + +Each check is stricter than the next one: +identical graphs are always the same, +and graphs that are the same are always isomorphic, +but not the other way round. +} +\section{Related documentation in the C library}{ +\href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_is_same_graph}{\code{is_same_graph()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Isomorphism.html#igraph_permute_vertices}{\code{permute_vertices()}}, \href{https://igraph.org/c/html/0.10.17/igraph-Basic.html#igraph_vcount}{\code{vcount()}} +} + +\examples{ +# 1-2, 3-2 and 2-3, 1-2 only differ in the order of the edges and of +# their endpoints, so they are the same graph. +g1 <- make_graph(c(1, 2, 3, 2), directed = FALSE) +g2 <- make_graph(c(2, 3, 1, 2), directed = FALSE) +identical_graphs(g1, g2) +is_same_graph(g1, g2) + +# 1-3, 2-3 is isomorphic to them, but not the same: +# it contains the edge 1-3, which g1 does not. +g3 <- make_graph(c(1, 3, 2, 3), directed = FALSE) +is_same_graph(g1, g3) +isomorphic(g1, g3) + +# After bringing both graphs into canonical form, +# is_same_graph() tests for isomorphism. +# For named graphs, use `use_names = FALSE`: +# matching vertices by name would undo the reordering. +canon <- function(g) permute(g, canonical_permutation(g)$labeling) +is_same_graph(canon(g1), canon(g3)) + +# Named vertices stored in a different order +edges <- data.frame(from = c("a", "b"), to = c("b", "c")) +g4 <- graph_from_data_frame(edges, vertices = c("a", "b", "c")) +g5 <- graph_from_data_frame(edges, vertices = c("c", "b", "a")) +identical_graphs(g4, g5) +is_same_graph(g4, g5) +is_same_graph(g4, g5, use_names = FALSE) + +# Vertex-coloured graphs: same edges, different colours +star <- make_star(4, mode = "undirected") +c1 <- set_vertex_attr(star, "color", value = c(1, 2, 2, 2)) +c2 <- set_vertex_attr(star, "color", value = c(1, 3, 3, 3)) +is_same_graph(c1, c2) +is_same_graph(c1, c2, vertex_attrs = "color") +} +\seealso{ +\code{\link[=identical_graphs]{identical_graphs()}} for comparing the internal representation, +\code{\link[=isomorphic]{isomorphic()}} for comparing graphs up to relabelling of the vertices. +} diff --git a/tests/testthat/_snaps/aaa-auto.md b/tests/testthat/_snaps/aaa-auto.md index b8315efdb62..0e7e285a432 100644 --- a/tests/testthat/_snaps/aaa-auto.md +++ b/tests/testthat/_snaps/aaa-auto.md @@ -56,6 +56,21 @@ Error in `ensure_igraph()`: ! Must provide a graph object (provided `NULL`). +# is_same_graph_impl basic + + Code + is_same_graph_impl(graph1 = g1, graph2 = g2) + Output + [1] TRUE + +# is_same_graph_impl errors + + Code + is_same_graph_impl(graph1 = NULL, graph2 = make_ring(3)) + Condition + Error in `ensure_igraph()`: + ! Must provide a graph object (provided `NULL`). + # delete_vertices_idx_impl basic Code diff --git a/tests/testthat/_snaps/iterators.md b/tests/testthat/_snaps/iterators.md index 14bfdb75dfe..dda12b9b1df 100644 --- a/tests/testthat/_snaps/iterators.md +++ b/tests/testthat/_snaps/iterators.md @@ -1,3 +1,62 @@ +# is_same_graph() uses IDs unless both graphs are named + + Code + res <- is_same_graph(g1, g2) + Condition + Warning: + Only one of the graphs has vertex names, matching vertices by their IDs. + i Use `use_names = FALSE` to silence this warning. + +# is_same_graph() errors + + Code + is_same_graph(dup, dup) + Condition + Error in `is_same_graph()`: + ! Vertex names must be unique to match vertices by name. + i Use `use_names = FALSE` to match vertices by their IDs. + Code + is_same_graph(g, g, TRUE) + Condition + Error in `is_same_graph()`: + ! `...` must be empty. + x Problematic argument: + * ..1 = TRUE + i Did you forget to name an argument? + Code + is_same_graph(g, g, use_names = NA) + Condition + Error in `is_same_graph()`: + ! `use_names` must be `TRUE` or `FALSE`, not `NA`. + Code + is_same_graph(g, NULL) + Condition + Error in `ensure_igraph()`: + ! Must provide a graph object (provided `NULL`). + Code + is_same_graph(g, g, vertex_attrs = 1) + Condition + Error in `is_same_graph()`: + ! `vertex_attrs` must be a character vector or `NULL`, not the number 1. + Code + is_same_graph(g, g, vertex_attrs = NA_character_) + Condition + Error in `is_same_graph()`: + ! `vertex_attrs` can't contain NA values. + Code + is_same_graph(g, set_vertex_attr(g, "color", value = 1:3), vertex_attrs = "color") + Condition + Error in `is_same_graph()`: + ! All `vertex_attrs` must exist in both graphs. + x Missing in `g1`: "color". + Code + is_same_graph(g, g, vertex_attrs = c("color", "size")) + Condition + Error in `is_same_graph()`: + ! All `vertex_attrs` must exist in both graphs. + x Missing in `g1`: "color" and "size". + x Missing in `g2`: "color" and "size". + # printing connected vs/es works Code diff --git a/tests/testthat/test-aaa-auto.R b/tests/testthat/test-aaa-auto.R index f55d16dea98..476c33788e7 100644 --- a/tests/testthat/test-aaa-auto.R +++ b/tests/testthat/test-aaa-auto.R @@ -104,6 +104,31 @@ test_that("copy_impl errors", { )) }) +# 3a. is_same_graph_impl + +test_that("is_same_graph_impl basic", { + igraph_local_seed(20250909) + g1 <- make_graph(c(1, 2, 2, 3), directed = FALSE) + g2 <- make_graph(c(3, 2, 2, 1), directed = FALSE) + + expect_snapshot(is_same_graph_impl( + graph1 = g1, + graph2 = g2 + )) + + # Structured tests + expect_true(is_same_graph_impl(graph1 = g1, graph2 = g2)) + expect_false(is_same_graph_impl(graph1 = g1, graph2 = make_ring(3))) +}) + +test_that("is_same_graph_impl errors", { + igraph_local_seed(20250909) + expect_snapshot_igraph_error(is_same_graph_impl( + graph1 = NULL, + graph2 = make_ring(3) + )) +}) + # 4. delete_vertices_idx_impl test_that("delete_vertices_idx_impl basic", { diff --git a/tests/testthat/test-iterators.R b/tests/testthat/test-iterators.R index 5e41c086170..50e8cb5a1d4 100644 --- a/tests/testthat/test-iterators.R +++ b/tests/testthat/test-iterators.R @@ -199,6 +199,146 @@ test_that("identical_graphs considers attributes", { expect_identical_graphs(g, g2, attrs = FALSE) }) +test_that("is_same_graph() ignores edge order", { + g <- make_ring(10) + g_edit <- g + edge(7, 9) + edge(8, 10) - edge("7|8") - edge("9|10") + + adj <- as_adjacency_matrix(g, sparse = FALSE) + adj[7, 8] <- adj[8, 7] <- adj[9, 10] <- adj[10, 9] <- 0 + adj[7, 9] <- adj[9, 7] <- adj[8, 10] <- adj[10, 8] <- 1 + g_adj <- graph_from_adjacency_matrix(adj, mode = "undirected") + + expect_not_identical_graphs(g_edit, g_adj) + expect_true(is_same_graph(g_edit, g_adj)) +}) + +test_that("is_same_graph() ignores endpoint order in undirected graphs only", { + g1 <- make_graph(c(1, 2, 2, 3), directed = FALSE) + g2 <- make_graph(c(3, 2, 2, 1), directed = FALSE) + expect_true(is_same_graph(g1, g2)) + + d1 <- make_graph(c(1, 2, 2, 3), directed = TRUE) + d2 <- make_graph(c(3, 2, 2, 1), directed = TRUE) + expect_false(is_same_graph(d1, d2)) + expect_false(is_same_graph(g1, d1)) +}) + +test_that("is_same_graph() respects edge multiplicities", { + g1 <- make_graph(c(1, 2, 1, 2, 2, 3), directed = FALSE) + g2 <- make_graph(c(2, 3, 2, 1, 1, 2), directed = FALSE) + g3 <- make_graph(c(1, 2, 2, 3, 2, 3), directed = FALSE) + expect_true(is_same_graph(g1, g2)) + expect_false(is_same_graph(g1, g3)) +}) + +test_that("is_same_graph() distinguishes isomorphic graphs", { + g1 <- make_star(4, mode = "undirected") + g2 <- permute(g1, c(2, 1, 3, 4)) + expect_true(isomorphic(g1, g2)) + expect_false(is_same_graph(g1, g2)) + expect_false(is_same_graph(make_ring(5), make_ring(6))) +}) + +test_that("is_same_graph() matches vertices by name", { + igraph_local_seed(50) + edges <- unique(cbind( + paste0("v", sample(100:200, 10)), + paste0("v", sample(100:200, 10)) + )) + vertices <- unique(as.vector(edges)) + + g1 <- graph_from_data_frame(edges, directed = TRUE) + g2 <- graph_from_data_frame(edges, directed = TRUE, vertices = sort(vertices)) + + expect_not_identical_graphs(g1, g2) + expect_true(is_same_graph(g1, g2)) + expect_false(is_same_graph(g1, g2, use_names = FALSE)) +}) + +test_that("is_same_graph() compares vertex name sets", { + g1 <- make_graph(~ a - b, b - c) + g2 <- make_graph(~ a - b, b - d) + g3 <- make_graph(~ a - b, b - c, d) + expect_false(is_same_graph(g1, g2)) + expect_false(is_same_graph(g1, g3)) +}) + +test_that("is_same_graph() uses IDs unless both graphs are named", { + g1 <- make_ring(3) + g2 <- set_vertex_attr(make_ring(3), "name", value = c("c", "b", "a")) + + expect_snapshot(res <- is_same_graph(g1, g2)) + expect_true(res) + expect_true(is_same_graph(g1, g2, use_names = FALSE)) + expect_no_warning(is_same_graph(g1, g1)) +}) + +test_that("is_same_graph() ignores attributes other than names", { + g1 <- set_edge_attr(make_ring(3), "weight", value = 1:3) + g2 <- set_graph_attr(make_ring(3), "name", "ring") + expect_true(is_same_graph(g1, g2)) +}) + +test_that("is_same_graph() compares selected vertex attributes", { + star <- make_star(4, mode = "undirected") + g1 <- set_vertex_attr(star, "color", value = c(1, 2, 2, 2)) + g1 <- set_vertex_attr(g1, "size", value = 1:4) + g2 <- set_vertex_attr(star, "color", value = c(1, 3, 3, 3)) + g2 <- set_vertex_attr(g2, "size", value = 1:4) + + expect_true(is_same_graph(g1, g2)) + expect_true(is_same_graph(g1, g2, vertex_attrs = "size")) + expect_false(is_same_graph(g1, g2, vertex_attrs = "color")) + expect_false(is_same_graph(g1, g2, vertex_attrs = c("size", "color"))) + expect_true(is_same_graph(g1, g1, vertex_attrs = c("size", "color"))) + expect_true(is_same_graph(g1, g2, vertex_attrs = character())) +}) + +test_that("is_same_graph() compares vertex attributes after matching names", { + edges <- data.frame(from = c("a", "b"), to = c("b", "c")) + vertices1 <- data.frame(name = c("a", "b", "c"), color = c(1, 2, 3)) + vertices2 <- data.frame(name = c("c", "b", "a"), color = c(3, 2, 1)) + g1 <- graph_from_data_frame(edges, directed = FALSE, vertices = vertices1) + g2 <- graph_from_data_frame(edges, directed = FALSE, vertices = vertices2) + + expect_true(is_same_graph(g1, g2, vertex_attrs = "color")) +}) + +test_that("is_same_graph() compares vertex attributes exactly", { + g1 <- set_vertex_attr(make_ring(3), "color", value = c(1, 2, 3)) + g2 <- set_vertex_attr(make_ring(3), "color", value = 1:3) + expect_false(is_same_graph(g1, g2, vertex_attrs = "color")) +}) + +test_that("is_same_graph() compares names as attributes when not matching by them", { + g1 <- set_vertex_attr(make_ring(3), "name", value = c("a", "b", "c")) + g2 <- set_vertex_attr(make_ring(3), "name", value = c("c", "b", "a")) + + expect_true(is_same_graph(g1, g2, use_names = FALSE)) + expect_false(is_same_graph(g1, g2, use_names = FALSE, vertex_attrs = "name")) +}) + +test_that("is_same_graph() errors", { + g <- make_ring(3) + dup <- set_vertex_attr(g, "name", value = c("a", "a", "b")) + + expect_snapshot(error = TRUE, { + is_same_graph(dup, dup) + is_same_graph(g, g, TRUE) + is_same_graph(g, g, use_names = NA) + is_same_graph(g, NULL) + is_same_graph(g, g, vertex_attrs = 1) + is_same_graph(g, g, vertex_attrs = NA_character_) + is_same_graph( + g, + set_vertex_attr(g, "color", value = 1:3), + vertex_attrs = "color" + ) + is_same_graph(g, g, vertex_attrs = c("color", "size")) + }) + expect_true(is_same_graph(dup, dup, use_names = FALSE)) +}) + test_that("we can create vertex/edge seqs", { g <- make_ring(10) V(g) %&&% expect_true(TRUE) diff --git a/tools/stimulus/functions-R.yaml b/tools/stimulus/functions-R.yaml index f5e95746274..efb46b71a3b 100644 --- a/tools/stimulus/functions-R.yaml +++ b/tools/stimulus/functions-R.yaml @@ -24,8 +24,8 @@ igraph_get_all_eids_between: DEPS: from ON graph, to ON graph, eids ON graph igraph_is_same_graph: - # Not needed in R; we can simply compare things in the R layer without - # calling into C + # Used by is_same_graph(), which matches vertices by name before calling + # into C ####################################### # Constructors, deterministic