Skip to content

Commit 80ddc9f

Browse files
authored
as.Splits edge cases (#4)
Catch some bugs with certain methods
1 parent 9678094 commit 80ddc9f

3 files changed

Lines changed: 72 additions & 55 deletions

File tree

R/Splits.R

Lines changed: 58 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -37,43 +37,6 @@
3737
#' @export
3838
as.Splits <- function (x, tipLabels = NULL, ...) UseMethod('as.Splits')
3939

40-
#' @keywords internal
41-
#' @export
42-
.TipLabels <- function (x) UseMethod('.TipLabels')
43-
44-
#' @keywords internal
45-
#' @export
46-
.TipLabels.phylo <- function (x) x$tip.label
47-
48-
#' @keywords internal
49-
#' @export
50-
.TipLabels.list <- function (x) {
51-
.TipLabels(x[[1]])
52-
}
53-
54-
#' @keywords internal
55-
#' @export
56-
.TipLabels.matrix <- function (x) colnames(x)
57-
58-
#' @keywords internal
59-
#' @export
60-
.TipLabels.multiPhylo <- function (x) {
61-
.TipLabels(x[[1]])
62-
}
63-
64-
#' @keywords internal
65-
#' @export
66-
.TipLabels.Splits <- function (x) attr(x, 'tip.label')
67-
68-
#' @keywords internal
69-
#' @export
70-
.TipLabels.default <- function (x) x
71-
72-
#' @keywords internal
73-
#' @export
74-
.TipLabels.numeric <- function (x) NextMethod('.TipLabels', as.character(x))
75-
76-
7740
#' @describeIn as.Splits Convert object of class `phylo` to `Splits`.
7841
#' @param asSplits Logical specifying whether to return a `Splits` object,
7942
#' or an unannotated two-dimensional array (useful where performance is
@@ -83,7 +46,7 @@ as.Splits.phylo <- function (x, tipLabels = NULL, asSplits = TRUE, ...) {
8346
if (!is.null(tipLabels)) {
8447
x <- RenumberTips(x, .TipLabels(tipLabels))
8548
}
86-
x <- Cladewise(x)
49+
x <- Preorder(x)
8750
splits <- cpp_edge_to_splits(x$edge)
8851
nSplits <- dim(splits)[1]
8952
# Return:
@@ -336,18 +299,20 @@ names.Splits <- function (x) rownames(x)
336299
c.Splits <- function (...) {
337300
splits <- list(...)
338301
nTip <- unique(vapply(splits, attr, 1, 'nTip'))
339-
if (length(nTip) > 1) {
302+
if (length(nTip) > 1L) {
340303
stop("Splits must relate to identical tips.")
341304
}
342-
tips <- vapply(splits, attr, character(nTip), 'tip.label')
343-
if (dim(unique(tips, MARGIN = 2))[2] != 1) {
344-
stop("Order of tip labels must be identical.")
305+
tips <- lapply(splits, attr, 'tip.label')
306+
if (length(unique(lapply(tips, sort))) > 1L) {
307+
stop("All splits must bear identical tips")
345308
}
309+
tipLabels <- tips[[1]]
310+
splits <- c(splits[1], lapply(splits[seq_along(splits)[-1]], as.Splits,
311+
tipLabels = tipLabels))
346312

347-
x <- rbind(...)
348-
structure(x,
313+
structure(do.call(rbind, splits),
349314
nTip = nTip,
350-
tip.label = tips[, 1],
315+
tip.label = tipLabels,
351316
class='Splits')
352317
}
353318

@@ -480,3 +445,51 @@ in.Splits <- function (x, table, incomparables = NULL) {
480445
duplicated(c(x, table), fromLast = TRUE,
481446
incomparables = incomparables)[seq_along(x), ]
482447
}
448+
449+
450+
#' @keywords internal
451+
#' @export
452+
.TipLabels <- function (x) UseMethod('.TipLabels')
453+
454+
#' @keywords internal
455+
#' @export
456+
.TipLabels.default <- function (x) {
457+
if (is.null(names(x))) {
458+
if (any(duplicated(x))) {
459+
NULL
460+
} else {
461+
x
462+
}
463+
} else {
464+
names(x)
465+
}
466+
}
467+
468+
#' @keywords internal
469+
#' @export
470+
.TipLabels.phylo <- function (x) x$tip.label
471+
472+
#' @keywords internal
473+
#' @export
474+
.TipLabels.list <- function (x) {
475+
.TipLabels(x[[1]])
476+
}
477+
478+
#' @keywords internal
479+
#' @export
480+
.TipLabels.matrix <- function (x) colnames(x)
481+
482+
#' @keywords internal
483+
#' @export
484+
.TipLabels.multiPhylo <- function (x) {
485+
.TipLabels(x[[1]])
486+
}
487+
488+
#' @keywords internal
489+
#' @export
490+
.TipLabels.Splits <- function (x) attr(x, 'tip.label')
491+
492+
493+
#' @keywords internal
494+
#' @export
495+
.TipLabels.numeric <- function (x) NextMethod('.TipLabels', as.character(x))

src/splits.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const uint32_t powers_of_two[32] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512,
88
2097152, 4194304, 8388608, 16777216,
99
33554432, 67108864, 134217728, 268435456,
1010
536870912U, 1073741824U, 2147483648U};
11+
const int BIN_SIZE = 32;
1112

1213
// [[Rcpp::export]]
1314
NumericMatrix cpp_edge_to_splits(NumericMatrix edge) {
@@ -18,26 +19,26 @@ NumericMatrix cpp_edge_to_splits(NumericMatrix edge) {
1819
const int n_edge = edge.rows(),
1920
n_node = n_edge + 1,
2021
n_tip = edge(0, 0) - 1,
21-
n_bin = n_tip / 32 + 1;
22-
22+
n_bin = (n_tip / BIN_SIZE) + 1;
23+
2324
if (n_edge == n_tip) { /* No internal nodes resolved */
2425
return NumericMatrix (0, n_bin);
2526
}
2627

2728
uint32_t** splits = new uint32_t*[n_node];
28-
for (int i = 0; i < n_node; i++) {
29+
for (int i = 0; i != n_node; i++) {
2930
splits[i] = new uint32_t[n_bin];
30-
for (int j = 0; j < n_bin; j++) {
31+
for (int j = 0; j != n_bin; j++) {
3132
splits[i][j] = 0;
3233
}
3334
}
3435

35-
for (int i = 0; i < n_tip; i++) {
36+
for (int i = 0; i != n_tip; i++) {
3637
splits[i][(int) i / 32] = powers_of_two[i % 32];
3738
}
3839

39-
for (int i = n_edge - 1; i > 0; i--) { /* edge 0 is second root edge */
40-
for (int j = 0; j < n_bin; j++) {
40+
for (int i = n_edge - 1; i != 0; i--) { /* edge 0 is second root edge */
41+
for (int j = 0; j != n_bin; j++) {
4142
splits[(int) edge(i, 0) - 1][j] |= splits[(int) edge(i, 1) - 1][j];
4243
}
4344
}

tests/testthat/test-Splits.R

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ test_that("as.Split", {
3030
expect_equal("0 bipartition splits dividing 5 tips.",
3131
capture_output(print(as.Splits(polytomy))))
3232

33-
tree1 <- PectinateTree(1:8)
34-
tree2 <- BalancedTree(8:1)
33+
notPreOrder <- structure(list(edge = structure(c(6L, 9L, 8L, 7L, 7L, 8L, 9L,
34+
6L, 9L, 8L, 7L, 2L, 3L, 5L, 4L, 1L),
35+
.Dim = c(8L, 2L)), Nnode = 4L,
36+
tip.label = 1:5), class = "phylo", order = "cladewise")
37+
expect_equal(c(n8 = 22, n9 = 6), as.Splits(notPreOrder)[, 1])
3538

3639
})
3740

@@ -103,7 +106,7 @@ test_that("Split combination", {
103106

104107
expect_equal(4L, length(splits12))
105108
expect_equal(c(FALSE, FALSE, TRUE, TRUE), as.logical(duplicated(splits12)))
106-
expect_error(c(splits1, as.Splits(tree3)))
109+
expect_equal(2L, length(unique(c(splits1, as.Splits(tree3)))))
107110
expect_error(c(splits1, as.Splits(tree4)))
108111
expect_error(c(splits1, as.Splits(tree5)))
109112
expect_equal(c(28L, 24L, 28L, 24L),

0 commit comments

Comments
 (0)