# DAGs -------------------------------------------------------------------- test_that("no self-loops or cycles", { expect_error(dag("X -> X")) expect_error(dag("X -> Z -> X")) expect_error(dag("X -> Z -> Y -> X")) }) test_that("extra delimiter invariance", { expect_identical(dag("X -> Y -> Z"), dag("X -> Y; Y -> Z")) expect_identical(dag("X -> Y -> Z"), dag("X -> Y\n Y -> Z")) expect_identical(dag("{X, Y, Z}"), dag("X Y Z")) expect_identical(dag("{{X, Y, Z}}"), dag("X Y Z")) expect_identical(dag("{{X, Y} Z}"), dag("X Y Z")) }) test_that("zero edges is allowed", { expect_length(attr(dag("X Y Z"), "labels"), 3) expect_length(attr(dag("{X Y Z}"), "labels"), 3) }) test_that("duplicate edge invariance", { expect_identical(dag("X -> Y <- X"), dag("X -> Y")) expect_identical(dag("X <-> Y <-> X"), dag("X <-> Y")) }) test_that("singleton bidirected", { expect_error(dag("X <-> X")) }) test_that("syntax errors fail", { expect_error(dag("->")) expect_error(dag("<-")) expect_error(dag("<->")) expect_error(dag("X ->")) expect_error(dag("<- X")) expect_error(dag("<- <- X")) expect_error(dag("X -> ->")) expect_error(dag("X -> -> Y")) expect_error(dag("X -> {}")) expect_error(dag("{} -> X")) expect_error(dag("{Y -> -> X} -> X")) expect_error(dag("<-> X")) expect_error(dag("X <->")) expect_error(dag("X -> {")) expect_error(dag("X -> }")) expect_error(dag("{")) expect_error(dag("}")) }) test_that("allowed structures", { expect_error(dag("{X -> Y}"), NA) expect_error(dag("{X <-> Y} -> {Z -> W}"), NA) expect_error(dag("X -> {Y Z} <- W <-> G"), NA) expect_error(dag("{X Z} -> {Y W}"), NA) expect_error(dag("X -> Z -> Y; X <-> Y"), NA) expect_error(dag("{X, Y, Z} -> W"), NA) expect_error(dag("Z -> W\nX -> Y"), NA) expect_error(dag("{X -> Z} -> {Y <-> W}"), NA) expect_error(dag("{X -> {Z -> {Y <-> W}}}"), NA) }) test_that("latent_projection", { expect_identical(dag("X -> Z -> Y", u = "Z"), dag("X -> Y")) expect_identical(dag("X <- Z -> Y", u = "Z"), dag("X <-> Y")) expect_identical(dag("Z <-> X -> Z -> Y", u = "Z"), dag("X <-> Y; X -> Y")) expect_identical( dag(" X -> Z -> Y <- W -> X", u = c("Z", "W")), dag("X <-> Y; X -> Y") ) expect_identical( dag("X -> {Z <-> {A B} -> H} -> Y", u = c("Z", "A", "B", "H")), dag("X -> Y") ) }) g1 <- dag("X -> W -> Y <- Z <- D X <-> Y") v1 <- cf("Y", 0, c(X = 0)) v2 <- cf("X", 1) v3 <- cf("Z", 0, c(D = 0)) v4 <- cf("D", 0) gamma1 <- conj(v4, v1, v2, v3) p1 <- cfid:::pwg(g1, gamma1) test_that("generated worlds", { expect_identical(sum(p1$latent), 4L) expect_identical(p1$n_obs, 15L) expect_identical(p1$n_unobs, 4L) expect_identical(p1$n_worlds, 3L) }) # Graph operations -------------------------------------------------------- A <- matrix( c( 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ), 5, 5, byrow = TRUE ) test_that("vertex ordering", { expect_identical(topological_order(A), c(1L, 2L, 4L, 3L, 5L)) }) test_that("children", { expect_setequal(children(1, A), c(2, 4)) expect_setequal(children(c(2, 4), A), 3) }) test_that("parents", { expect_setequal(parents(c(2, 4), A), 1) expect_setequal(parents(5, A), 3) }) test_that("d-separation", { expect_true(dsep(A, 1, 5, c(2, 4))) expect_true(dsep(A, 1, 5, 3)) expect_true(dsep(A, 2, 4, 1)) expect_false(dsep(A, 1, 5)) expect_false(dsep(A, 2, 4, 5)) expect_false(dsep(A, 2, 4)) }) test_that("print", { expect_error( print.dag(1L), "Argument `x` must be a `dag` object" ) expect_output( print(dag("X -> Z -> Y")), "X -> Z; Z -> Y" ) expect_output( print(dag("X -> {Z, W} -> Y")), "X -> \\{Z, W\\}; Z -> Y; W -> Y" ) expect_output( print(dag("X -> Z; X -> Y; Z -> Y; Z -> W; W -> Y")), "X -> \\{Z, Y\\}; Z -> \\{Y, W\\}; W -> Y" ) }) # Imported Graphs --------------------------------------------------------- test_that("dagitty dag supported", { dgty <- "dag {\nx\ny\nx -> y\n}\n" class(dgty) <- "dagitty" expect_identical(import_graph(dgty), dag("x -> y")) }) test_that("dagitty mag not supported", { dgty <- "mag {\nx\ny\nx -- y\n}\n" class(dgty) <- "dagitty" expect_error(import_graph(dgty)) }) test_that("causaleffect syntax with igraph supported", { ig <- igraph::graph.formula(X -+ Z -+ Y, Y -+ X, X -+ Y) ig <- igraph::set.edge.attribute(ig, "description", c(2, 4), "U") expect_identical(import_graph(ig), dag("X -> Z -> Y X <-> Y")) }) test_that("generic igraph not supported", { ig <- igraph::graph.formula(X -- Y) expect_error(import_graph(ig)) }) test_that("fail when no igraph", { ig <- igraph::graph.formula(X -+ Y) mockery::stub(import_graph, "requireNamespace", FALSE) expect_error(import_graph(ig)) }) test_that("dosearch syntax", { g <- "X -> Y\n X -> Z" expect_identical(import_graph(g), dag(g)) }) test_that("unsupported format", { g <- data.frame() expect_error(import_graph(g)) }) # Exported Graphs --------------------------------------------------------- test_that("dags only", { g <- data.frame() expect_error(export_graph(g)) }) test_that("dagitty sanity", { # dagitty will always enforce alphabetical order it seems g <- dag("X <-> Y X -> Z -> Y") e <- export_graph(g, "dagitty") expect_identical(import_graph(e), g) }) test_that("fail when no dagitty", { g <- dag("X -> Z -> Y <-> X") mockery::stub(export_graph, "requireNamespace", FALSE) expect_error(export_graph(g, "dagitty")) }) test_that("causaleffect sanity", { g <- dag("X -> Z -> Y X <-> Y ") e <- export_graph(g, "causaleffect") expect_identical(import_graph(e), g) }) test_that("fail when no igraph", { g <- dag("X -> Z -> Y <-> X") mockery::stub(export_graph, "requireNamespace", FALSE) expect_error(export_graph(g, "causaleffect")) }) test_that("dosearch sanity", { g <- dag("X -> Z -> Y <-> X") e <- export_graph(g, "dosearch") expect_identical(import_graph(e), g) }) test_that("dosearch explicit latent variable", { g <- dag("X <-> Y") expect_identical( export_graph(g, "dosearch", use_bidirected = FALSE), "U[X,Y] -> X\nU[X,Y] -> Y" ) })