library(ape)
## Warning: package 'ape' was built under R version 2.15.1
lcx <- 1  ## contour lab cex
dumpfn <- "acedump.RData"
if (file.exists(dumpfn)) load(dumpfn)
mytree <- read.nexus("monkey_tree.nex")
copy_num <- c(35, 24, 6)
names(copy_num) <- c("human", "chimp", "rhesus")
source("ace.R")  ## hacked versions of curve3d, apply2d, and ace
## Loading required package: plyr
max_copy_num = 80

Set up the transition matrix:

copy_num <- factor(copy_num, levels = 1:max_copy_num)
transitions <- matrix(0, max_copy_num, max_copy_num)
transitions[(row(transitions) - col(transitions)) == -1] <- 1
transitions[(row(transitions) - col(transitions)) == 1] <- 2

Regular ace fails:

out <- ace(copy_num, mytree, type = "discrete", model = transitions)
## Error: non-finite value supplied by 'nlm'

Now we'll use the hacked version to return just the deviance function so we can play with it:

info <- ace2(copy_num, mytree, type = "discrete", model = transitions, devInfoOnly = TRUE)

See what's in there:

names(info)
## [1] "np" "ip" "fn"
info$np
## [1] 2
info$ip
## [1] 0.1

Generate deviance surface (by brute force):

if (!exists("cc")) {
    cc <- curve3d(info$fn(c(x, y)), xlim = c(0.01, 100), ylim = c(0.01, 100), 
        log = "xy", sys3d = "none")
}

Picture:

with(cc, image(x, y, z, log = "xy"))
with(cc, contour(x, y, z, log = "xy", add = TRUE))

plot of chunk surf1pic

Things are so horrid that the poor thing can't build a contour plot effectively.

Just for comparison we'll try persp — since it can't handle infinite values
we'll replace them:

maxz <- with(cc, ifelse(z == Inf, max(is.finite(z)), z))
with(cc, persp(x, y, maxz))

plot of chunk surf1persp

Still pretty ugly!
… but there are some reasonable values in the results:

hist(na.omit(c(cc$z)), col = "gray", main = "")

plot of chunk surf1hist

Try again, over a narrower range:

if (!exists("cc2")) {
    cc2 <- curve3d(info$fn(c(x, y)), xlim = c(0.1, 10), ylim = c(0.1, 10), log = "xy", 
        sys3d = "none")
}

Better, but still ugly:

with(cc2, image(x, y, z, log = "xy"))
with(cc2, contour(x, y, z, log = "xy", labcex = lcx, add = TRUE))

plot of chunk surf2pic1

If we try Nelder-Mead (a more robust algorithm), and start within
the OK region, we can actually get a reasonable answer:

(opt1 <- optim(fn = info$fn, par = c(1, 1), method = "Nelder-Mead"))
## $par
## [1] 68.83 74.97
## 
## $value
## [1] 15.23
## 
## $counts
## function gradient 
##       83       NA 
## 
## $convergence
## [1] 0
## 
## $message
## NULL

Now let's check the vicinity of those results:

if (!exists("cc3")) {
    cc3 <- curve3d(info$fn(c(x, y)), xlim = c(50, 80), ylim = c(50, 80), sys3d = "none")
}
with(cc3, image(x, y, z))
with(cc3, contour(x, y, z, add = TRUE, labcex = lcx))
with(cc3, contour(x, y, z, add = TRUE, levels = 15:20, lty = 2, labcex = lcx))
points(opt1$par[1], opt1$par[2], pch = 16)

plot of chunk surf3pic1

If all we care about is the MLE, then we're done. I thought
we might want to know something about the confidence intervals, though \ldots
We can calculate the local curvature (Hessian) and from
that get the correlation matrix of the parameters:

library(numDeriv)
h1 <- hessian(info$fn, opt1$par)
v <- solve(h1)
cov2cor(v)
##       [,1]  [,2]
## [1,] 1.000 0.998
## [2,] 0.998 1.000

\( x \) and \( y \) are almost perfectly correlated.
We would be better off reparameterizing this in terms of
(either) \( x+y \) and \( x-y \) (I also considered \( xy \) and \( x/y \),
but the former seems to work OK).

fn2 <- function(sumxy, diffxy) {
    x <- (sumxy + diffxy)/2
    y <- sumxy - x
    info$fn(c(x, y))
}

Checking:

info$fn(c(1, 1))
## [1] 56.97
fn2(2, 0)
## [1] 56.97
info$fn(c(1, 0.7))
## [1] 61.52
fn2(1.7, 0.3)
## [1] 61.52

I drew a few surfaces, tweaking the limits outward each time:

if (!exists("cc4")) {
    cc4 <- curve3d(fn2(x, y), xlim = c(20, 400), ylim = c(-20, 5), sys3d = "none")
}

In order to fit the function on this scale we need to make a little wrapper:

fn3 <- function(p) {
    fn2(p[1], p[2])
}
opt3 <- optim(fn = fn3, par = c(150, -5), method = "Nelder-Mead")

A picture:

with(cc4, image(x, y, z, xlab = "x+y", ylab = "x-y", zlim = c(15, 25)))
with(cc4, contour(x, y, z, add = TRUE, labcex = lcx, levels = 14:25))
with(cc4, contour(x, y, z, add = TRUE, labcex = lcx, levels = seq(15, 16, by = 0.1), 
    lty = 2))
points(opt3$par[1], opt3$par[2], pch = 16)

plot of chunk surf4pic1

Expand the range a bit more: to get 95% bivariate confidence intervals
we have to get up to (min+6) \( \approx \) 21.3 …

if (!exists("cc5")) {
    cc5 <- curve3d(fn2(x, y), xlim = c(20, 600), ylim = c(-25, 5), sys3d = "none")
}
with(cc5, image(x, y, z, xlab = "x+y", ylab = "x-y", zlim = c(15, 25)))
with(cc5, contour(x, y, z, add = TRUE, labcex = lcx, levels = 14:25))
with(cc5, contour(x, y, z, add = TRUE, labcex = lcx, levels = seq(15, 16, by = 0.1), 
    lty = 2))

plot of chunk surf5pic1

We might try reparameterizing this as \( \{1/(x+y),x-y\} \), so that we can try the boundary as \( x+y \to \infty \)?

fn2(1000, -10)  ## still not above 16!
## [1] 15.92

Compute cross-sections of the deviance surface for a couple of large values of \( x+y \):

xydiffvec <- seq(-200, 5, by = 1)
if (!exists("bigslice")) bigslice <- sapply(xydiffvec, function(d) fn2(5000, 
    d))
plot(xydiffvec, bigslice, type = "l")

plot of chunk slice1

The midpoint of the surface (i.e. the best value of \( x-y \) for a given value
of \( x+y \)) keeps decreasing as we go out:

xydiffvec2 <- seq(-500, 0, by = 2)
if (!exists("bigslice2")) bigslice2 <- sapply(xydiffvec2, function(d) fn2(10000, 
    d))
plot(xydiffvec2, bigslice2, type = "l")

plot of chunk slice2

Save lengthy computation results:

if (!file.exists(dumpfn)) save(list = ls(pattern = "^(cc|big)"), file = dumpfn)