From 939e8ddd70844f8c0539e1d297ec6aa3b43ae702 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 08:49:49 +0000 Subject: [PATCH 1/2] Initial plan From 19f69ed770f5dc196f0c8e2bf5572ab360056bc9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 10 Jul 2025 13:39:19 +0000 Subject: [PATCH 2/2] Fix disjoint_union to handle mixed named/unnamed graphs - Add logic to detect when some graphs have vertex names and others don't - Generate generic vertex names (V1, V2, etc.) for unnamed graphs - Respect existing V-pattern names by continuing the sequence - Add comprehensive tests for the new functionality - Fixes issue #1946 Co-authored-by: krlmlr <1741643+krlmlr@users.noreply.github.com> --- R/operators.R | 24 ++++++++++++++++++++ tests/testthat/test-operators.R | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/R/operators.R b/R/operators.R index 8722413f9a1..d1fcca95136 100644 --- a/R/operators.R +++ b/R/operators.R @@ -228,6 +228,30 @@ disjoint_union <- function(...) { ) lapply(graphs, ensure_igraph) + ## Handle mixed named/unnamed graphs by generating generic names + have_names <- sapply(graphs, is_named) + if (sum(have_names) > 0 && sum(have_names) < length(graphs)) { + # Some graphs have names, others don't - generate names for unnamed graphs + existing_names <- unlist(lapply(graphs[have_names], function(g) V(g)$name)) + + # Create a counter for generating new names (check if named graph already has generic names) + prefix <- "V" + name_counter <- if (any(grepl(paste0("^", prefix, "[0-9]+$"), existing_names))) { + max(as.integer(gsub(paste0("^", prefix, "([0-9]+)$"), "\\1", existing_names)), na.rm = TRUE) + 1 + } else { + 1 + } + + for (i in seq_along(graphs)) { + if (!have_names[i]) { + n <- vcount(graphs[[i]]) + num_id <- seq(name_counter, name_counter + n - 1) + V(graphs[[i]])$name <- paste0(prefix, num_id) + name_counter <- name_counter + n + } + } + } + on.exit(.Call(R_igraph_finalizer)) res <- .Call(R_igraph_disjoint_union, graphs) diff --git a/tests/testthat/test-operators.R b/tests/testthat/test-operators.R index afa804d5f16..5a1499ed435 100644 --- a/tests/testthat/test-operators.R +++ b/tests/testthat/test-operators.R @@ -345,6 +345,46 @@ test_that("disjoint union gives warning for non-unique vertex names", { ) }) +test_that("disjoint union handles mixed named/unnamed graphs", { + # Test named + unnamed graphs + g1 <- make_ring(4) + g2 <- make_ring(3) + V(g1)$name <- c("A", "B", "C", "D") + + result <- disjoint_union(g1, g2) + + expect_equal(V(result)$name, c("A", "B", "C", "D", "V1", "V2", "V3")) + expect_equal(vcount(result), 7) + expect_equal(ecount(result), 7) + + # Test with existing V-pattern names + g3 <- make_ring(3) + g4 <- make_ring(2) + V(g3)$name <- c("V1", "V2", "V3") + + result2 <- disjoint_union(g3, g4) + expect_equal(V(result2)$name, c("V1", "V2", "V3", "V4", "V5")) + + # Test unnamed + named graphs (should work the same) + result3 <- disjoint_union(g2, g1) + expect_equal(V(result3)$name, c("V1", "V2", "V3", "A", "B", "C", "D")) + + # Test all unnamed graphs (should not add names) + g5 <- make_ring(2) + g6 <- make_ring(2) + result4 <- disjoint_union(g5, g6) + expect_true(is.null(V(result4)$name) || all(is.na(V(result4)$name))) + + # Test all named graphs (should work as before) + g7 <- make_ring(2) + g8 <- make_ring(2) + V(g7)$name <- c("X", "Y") + V(g8)$name <- c("P", "Q") + + result5 <- disjoint_union(g7, g8) + expect_equal(V(result5)$name, c("X", "Y", "P", "Q")) +}) + test_that("union of unnamed graphs works", { g1 <- make_ring(10)