Skip to content
uyedaj edited this page Dec 11, 2014 · 9 revisions

Customized models in bayou

The development version of bayou enables you to add in customized models that can make use of bayou's reversible-jump machinary on phylogenetic trees. This document will walk through the process of adding in new models into bayou. This functionality is in development and somewhat limited right now, but should become increasingly powerful and robust over time.

An example: OUwie's OUMV model

I haven't yet implemented OU models with varying sigma or alpha. Let's try putting OUwie's likelihood function for doing this into bayou. First, let's load the package and simulate the data.

require(devtools)
require(OUwie)
# Install the development version of bayou
#install_github("uyedaj/bayou", ref="dev")
require(bayou)
load_all("~/repos/bayou/bayou_1.0")
## Loading bayou
# Set parameters for simulation:
nn <- 30 # number of taxa
alpha <- 5
sig2 <- log(c(0.1, 2, 5)) # vector of log-transformed sigma^2 values
theta <- c(0, 1.5, -1) # vector of intercepts for each regime
# Simulate the tree
tt <- sim.bdtree(b=0.1, d=0, stop="taxa", n=nn, seed=1984)
tt <- reorder(tt, "postorder")
# Rescale the tree to unit height
tt$edge.length <- tt$edge.length/max(branching.times(tt))
# Assign clades to regimes:
parsT <- identifyBranches(tt, length(sig2)-1, fixed.loc=FALSE) # Select some clades, pick some meaty ones
parsT$ntheta <- 3; parsT$k <- 2; parsT$t2 <- 2:3; parsT$loc <- c(0,0) # Specify necessary parameters
parsT$sig2 <- sig2; parsT$theta <- theta; parsT$alpha <- alpha # Input known parameters
regs <- bayou:::.tipregime(parsT, tt) # Determine which regime each tip is in
tr <- bayou2OUwie(parsT, tt, setNames(rnorm(nn), tt$tip.label))
d1 <- OUwie.sim(tr$tree, tr$dat[,1:2], alpha=rep(parsT$alpha^2, parsT$ntheta), sigma.sq = exp(parsT$sig2), 
           theta0 = parsT$theta[1], theta = parsT$theta)
dd <- setNames(d1[,3], d1[,1])
phenogram(pars2simmap(parsT, tt)$tree, dd)

plot of chunk unnamed-chunk-4

Create a likelihood function (the hard part):

Now that the data are simulated, we can input the appropriate model. First, we need to define a likelihood. I assume that the user will provide the likelihood function. The only requirement is that the likelihood function take in a bayou formatted parameter list and a bayou cache object and return a list with the element '$loglik'.

Let's wrap OUwie's OUMV model into bayou. We're going to use OUwie's likelihood function 'OUwie.fixed'. I'm sure the OUwie developers never intended this to be used in an MCMC, so this is going to be slow and not optimized for bayou.

Note: the cache object is simply a list with a bunch of pre-calculated information about the tree and data. These are useful to calculate once so that subsequent calculations during the MCMC are faster. bayou generates the cache object during the initialization step of the MCMC by calling '.prepare.ou.univariate'.

cache <- bayou:::.prepare.ou.univariate(tt, dd, SE=0)
require(OUwie)
ouwie.lik <- function(pars, cache, X, model="custom"){
  ouwie.pars <- bayou2OUwie(pars, cache$phy, dat=X)
  # Here I use capture.output to suppress OUwie's printing to screen
  capture.output(ouwie.out <<- OUwie.fixed(ouwie.pars$tree, ouwie.pars$dat, model="OUMV", root.station = FALSE, alpha = rep(pars$alpha, pars$ntheta), 
                           sigma.sq = exp(pars$sig2), theta=c(pars$theta[1], pars$theta))) #Note that we set the root at the optimum...
  out <- list(loglik=ouwie.out$loglik[1,1])
  return(out)
} 

Check and see if it works:

pars <- list(alpha=1, sig2=log(c(1,1,1)), k=2, ntheta=3, theta=c(0,4,4), sb=sample(1:nrow(tt$edge), 2, replace=FALSE), loc=c(0,0), t2=2:3)
ouwie.lik(pars, cache, dd)
## $loglik
## [1] -29.69
ouwie.lik(parsT, cache, dd)
## $loglik
## [1] -1.426

Now we need to define a model object for bayou so it knows how to treat the model. It is specified by providing a list of the following items:

  • moves - A list of the names of the move functions to use. These may be custom functions as well.
  • control.weights - A list giving the relative frequency of each move type
  • D - A list giving the tuning weights supplied to each move. NOTE: for the tuning of K, you need to provide a tuning parameter for each of the rjpars!
  • parorder - A vector giving the order in which the parameters should be returned (arbitrary preference)
  • rjpars - A vector of the parameter names which will be split at each point in the phylogeny
  • shiftpars - A vector of the parameters governing the shift locations (this probably should not ever change as of now)
  • monitor.fn - A printing function to summarize the pars vector (optional)
  • lik.fn - The likelihood function

Defining a monitor function is a little tricky right now, we'll get to it later, for now, we'll just make sure the MCMC doesn't print anything.

model.OUMV <- list(moves = list(alpha=".multiplierProposal", sig2=".vectorSlidingWindow", k=".splitmergebd", theta=".adjustTheta", slide=".slide2"),
                      control.weights = list(alpha=2, sig2=3, theta=3, slide=2, k=10),
                      D = list(alpha=2, sig2=5, k = c(1, 1), theta=2, slide=1),
                      parorder = c("alpha", "sig2", "k", "ntheta", "theta"),
                      rjpars = c("sig2", "theta"),
                      shiftpars = c("sb", "loc", "t2"),
                      lik.fn = ouwie.lik)

Now we define the prior:

prior <- make.prior(tt, plot.prior = TRUE, dists=list(dalpha="dhalfcauchy", dsig2="dnorm", dsb="dsb", 
                                                       dk="cdpois", dtheta="dnorm", dloc="dunif"), 
                        param=list(dalpha=list(scale=1), dsig2=list(mean=-1, sd=2), dk=list(lambda=10, kmax=20), 
                                   dsb=list(bmax=1,prob=1), dtheta=list(mean=0, sd=1), dloc=list(min=0, max=1)), 
                            model="custom")

plot of chunk unnamed-chunk-8

prior(pars, cache) # Test the prior function.
## [1] -38.61

We are now ready to run our model. We will need to define our starting parameters since bayou doesn't yet know how to simulate for arbitrary models. We'll just use the values we've been using so far.

testmcmc <- bayou.makeMCMC(tt, dd, model=model.OUMV, prior=prior, startpar=pars, new.dir=TRUE, outname="oumvtest", plot.freq=NULL, ticker.freq=10000, samp = 20)

Let's run for 1000 generations just to test it out.

testmcmc$run(1000)
chain <- testmcmc$load()
summary(chain)
## bayou MCMC chain: 1000 generations
## 204 samples, first 1 samples discarded as burnin
## 
## 
## Summary statistics for parameters:
##                 Mean     SD Naive SE Time-series SE Effective Size
## lnL        -24.07667 1.4426  0.10100        0.14327         101.39
## prior      -20.96256 4.8479  0.33942        0.35666         184.75
## alpha        0.27725 0.2344  0.01641        0.02520          86.55
## k            1.35294 0.7381  0.05168        0.06025         150.09
## ntheta       2.35294 0.7381  0.05168        0.06025         150.09
## root.sig2    0.07954 0.1699  0.01189        0.02530          45.08
## root.theta   0.26603 0.3708  0.02596        0.01824         413.15
## all sig2    -0.06109 0.5951       NA             NA             NA
## all theta    0.32522 1.1643       NA             NA             NA
## 
## 
## Branches with posterior probabilities higher than 0.1:
##        pp magnitude.of.theta2 naive.SE.of.theta2 rel.location
## 20 0.5686            -0.11069            0.14054      0.50911
## 10 0.2549             0.09093            0.09353      0.44625
## 30 0.2157             0.29788            0.12552      0.07225
## 22 0.1078             3.05568            0.19801      0.07045

Now, we've only run for 1000 generations and we don't have a monitor function, so it will break the MCMC when we get to a point where it prints to the screen according to 'ticker.freq'. Let's make a monitor function and redo the analysis. The inputs must ALWAYS be as below (i= generation, lik= likelihood, pr= prior, pars= parameter list, accept= whether accepted, accept.type= proposal type, j= a counter to reprint header names)

model.OUMV$monitor.fn <- function(i, lik, pr, pars, accept, accept.type, j){
  names <- c("gen", "lnL", "prior", "alpha","sig2", "theta","k") #The parameters we want to print
  string <- "%-8i%-8.2f%-8.2f%-8.2f%-8.2f%-8.2f%-8i" #The types of each parameter
  acceptratios <- tapply(accept, accept.type, mean)
  names <- c(names, names(acceptratios))
  if(j==0){
    cat(sprintf("%-7.7s", names), "\n", sep=" ")                           
  }
  cat(sprintf(string, i, lik, pr, pars$alpha, pars$sig2[1], pars$theta[1], pars$k), sprintf("%-8.2f", acceptratios),"\n", sep="")
}

Let's remake and see if it works.

mymcmc <- bayou.makeMCMC(tt, dd, SE=0, model=model.OUMV, prior=prior, startpar=pars, new.dir=TRUE, outname="oumvtest", plot.freq=NULL, ticker.freq=100, samp = 20)

Run it again.

mymcmc$run(10000)
## gen     lnL     prior   alpha   sig2    theta   k       .alpha  birth.k death.k .sig2   .theta  U0.slid 
## 100     -24.12  -13.72  0.82    0.14    0.34    0       0.70    0.00    1.00    0.33    0.61    1.00    
## 200     -24.21  -13.34  0.32    0.45    0.02    0       0.67    0.00    1.00    0.21    0.56    1.00    
## 300     -25.52  -13.56  0.46    0.73    0.17    0       0.62    0.00    1.00    0.19    0.52    1.00    
## 400     -23.31  -21.22  0.09    0.04    0.37    1       0.68    0.01    1.00    0.24    0.51    1.00    
## 500     -10.85  -19.86  0.16    0.07    -0.38   1       0.68    0.01    0.67    0.27    0.55    1.00
## Error: non-conformable arguments
chain <- mymcmc$load()
summary(chain)
## bayou MCMC chain: 520 generations
## 54 samples, first 1 samples discarded as burnin
## 
## 
## Summary statistics for parameters:
##                Mean     SD Naive SE Time-series SE Effective Size
## lnL        -22.0518 5.7015  0.77587        2.13481          7.133
## prior      -18.3615 6.2484  0.85030        1.75345         12.698
## alpha        0.5104 0.3643  0.04958        0.09702         14.101
## k            0.6667 0.7770  0.10574        0.27381          8.053
## ntheta       1.6667 0.7770  0.10574        0.27381          8.053
## root.sig2    0.2379 0.2829  0.03850        0.04588         38.021
## root.theta   0.1820 0.2881  0.03921        0.03921         54.000
## all sig2    -1.0888 2.3781       NA             NA             NA
## all theta    0.3433 1.0844       NA             NA             NA
## 
## 
## Branches with posterior probabilities higher than 0.1:
##        pp magnitude.of.theta2 naive.SE.of.theta2 rel.location
## 7  0.2963            -0.26807             0.1492       0.1941
## 55 0.2222             0.07086             0.1633       0.5264
plotSimmap.mcmc(tr$tree, chain, burnin=0)

plot of chunk unnamed-chunk-13

Doesn't look very good so far...whether or not it ever gets the right answer and converges is beyond the scope here, One problem is that OUwie.fixed is pretty slow, so speeding up this function is probably necessary to really give this a good test. Let's try it fixing the location of the shifts to at least make sure we get the right variance estimates:

model.OUMVFixed <- list(moves = list(alpha=".multiplierProposal", sig2=".vectorSlidingWindow", theta=".adjustTheta"),
                   control.weights = list(alpha=1, sig2=1, theta=1, k=0),
                   D = list(alpha=2, sig2=5, theta=2),
                   parorder = c("alpha", "sig2", "k", "ntheta", "theta"),
                   rjpars = c("sig2", "theta"),
                   shiftpars = c("sb", "loc", "t2"),
                   monitor.fn = model.OUMV$monitor.fn,
                   lik.fn = ouwie.lik)
startpar.fixed <- parsT
fixed.hypothesis <- parsT[c("sb", "t2", "loc")]
startpar.fixed$alpha <- 1; startpar.fixed$sig2 <- c(1,1,1); startpar.fixed$theta=c(2,2,2)

prior.fixed <- make.prior(tt, plot.prior = TRUE, dists=list(dalpha="dhalfcauchy", dsig2="dnorm", dsb="fixed", 
                                                            dk="fixed", dtheta="dnorm", dloc="dunif"), 
                          param=list(dalpha=list(scale=1), dsig2=list(mean=-1, sd=2), dtheta=list(mean=0, sd=1), dloc=list(min=0, max=1)), 
                          fixed=fixed.hypothesis)

plot of chunk unnamed-chunk-14

mymcmc.fixed <- bayou.makeMCMC(tt, dd, SE=0, model=model.OUMVFixed, prior=prior.fixed, startpar=startpar.fixed, 
                               new.dir=TRUE,  outname="oumvfixed", plot.freq=NULL, ticker.freq=100, samp = 20)


mymcmc.fixed$run(10000)
## gen     lnL     prior   alpha   sig2    theta   k       .alpha  .sig2   .theta  
## 100     -19.34  -11.45  0.93    -1.98   0.26    2       0.63    0.51    0.67    
## 200     -4.69   -10.91  0.65    -4.02   0.08    2       0.59    0.40    0.69    
## 300     2.14    -15.80  1.92    -3.55   0.05    2       0.56    0.33    0.62    
## 400     6.61    -13.70  3.50    -3.69   -0.00   2       0.50    0.30    0.58    
## 500     6.98    -15.95  3.12    -4.63   -0.00   2       0.45    0.28    0.51    
## 600     5.61    -14.35  5.51    -3.34   0.02    2       0.45    0.31    0.49    
## 700     4.24    -15.06  6.16    -2.96   0.02    2       0.42    0.33    0.46    
## 800     4.20    -13.88  1.85    -3.90   0.02    2       0.41    0.31    0.43    
## 900     4.57    -13.82  1.85    -4.65   0.02    2       0.41    0.30    0.40    
## 1000    4.94    -13.58  4.14    -3.65   0.02    2       0.41    0.30    0.38    
## 1100    6.82    -15.60  7.90    -4.41   0.02    2       0.40    0.28    0.37    
## 1200    4.62    -15.93  6.90    -3.25   0.02    2       0.40    0.29    0.35    
## 1300    4.83    -15.99  1.86    -5.23   0.02    2       0.41    0.29    0.35    
## 1400    5.61    -14.73  2.33    -4.06   0.02    2       0.39    0.29    0.33    
## 1500    4.68    -15.44  2.86    -5.47   0.02    2       0.38    0.28    0.32    
## 1600    4.86    -14.28  5.08    -3.60   0.02    2       0.38    0.28    0.31    
## 1700    2.29    -13.77  3.17    -4.73   0.02    2       0.38    0.29    0.30    
## 1800    4.16    -14.63  2.82    -3.96   0.01    2       0.37    0.29    0.31    
## 1900    5.45    -14.75  2.91    -3.70   0.01    2       0.37    0.30    0.31    
## 2000    7.30    -15.90  2.97    -5.09   0.01    2       0.38    0.29    0.31    
## 2100    2.39    -14.60  4.60    -3.14   0.01    2       0.38    0.30    0.32    
## 2200    3.37    -14.33  7.44    -3.29   0.01    2       0.38    0.30    0.31    
## 2300    5.59    -14.47  2.24    -5.23   0.01    2       0.38    0.30    0.31    
## 2400    8.06    -15.01  4.84    -4.49   0.01    2       0.38    0.29    0.30    
## 2500    6.96    -14.80  8.70    -2.83   0.01    2       0.37    0.29    0.29    
## 2600    -2.46   -11.88  0.68    -5.10   0.01    2       0.38    0.29    0.29    
## 2700    1.36    -13.87  1.39    -5.06   0.00    2       0.39    0.29    0.30    
## 2800    7.29    -15.26  7.06    -3.67   0.00    2       0.39    0.29    0.29    
## 2900    5.71    -14.29  2.08    -4.54   0.00    2       0.39    0.29    0.29    
## 3000    5.75    -13.62  2.99    -3.92   0.01    2       0.39    0.29    0.29    
## 3100    8.13    -14.72  5.55    -4.03   0.01    2       0.39    0.29    0.28    
## 3200    5.87    -13.72  3.51    -4.09   0.01    2       0.38    0.29    0.29    
## 3300    2.66    -15.52  5.07    -5.24   0.01    2       0.38    0.30    0.29    
## 3400    5.37    -15.33  6.69    -3.06   0.01    2       0.38    0.30    0.29    
## 3500    4.76    -14.40  4.95    -3.62   0.01    2       0.38    0.30    0.28    
## 3600    6.48    -13.85  2.96    -3.83   0.01    2       0.38    0.30    0.28    
## 3700    0.13    -14.90  1.33    -4.57   -0.04   2       0.38    0.30    0.28    
## 3800    5.24    -13.94  2.87    -3.52   -0.01   2       0.38    0.30    0.29    
## 3900    -0.09   -14.19  1.83    -3.67   -0.10   2       0.38    0.30    0.29    
## 4000    -3.25   -13.34  1.45    -2.54   -0.10   2       0.38    0.30    0.29    
## 4100    -6.40   -13.15  1.31    -2.54   -0.10   2       0.38    0.29    0.29    
## 4200    6.66    -14.57  3.46    -4.53   0.02    2       0.38    0.29    0.29    
## 4300    5.69    -13.74  3.97    -3.13   0.02    2       0.38    0.30    0.29    
## 4400    4.40    -13.89  4.96    -4.03   0.02    2       0.38    0.30    0.29    
## 4500    6.77    -14.88  6.26    -3.41   0.02    2       0.37    0.30    0.29    
## 4600    5.68    -14.40  5.79    -3.61   0.02    2       0.37    0.30    0.28    
## 4700    3.12    -13.91  3.97    -4.29   0.02    2       0.37    0.30    0.28    
## 4800    7.05    -14.62  3.56    -3.79   0.02    2       0.37    0.30    0.28    
## 4900    2.87    -13.54  4.44    -3.06   0.01    2       0.37    0.30    0.28    
## 5000    6.32    -14.62  5.08    -3.67   0.01    2       0.37    0.30    0.28    
## 5100    8.32    -15.05  6.51    -3.82   0.02    2       0.36    0.30    0.27    
## 5200    7.42    -15.21  7.59    -3.44   0.02    2       0.36    0.30    0.27    
## 5300    5.09    -14.55  2.96    -3.81   0.05    2       0.36    0.30    0.27    
## 5400    5.50    -14.65  2.42    -4.30   0.05    2       0.36    0.30    0.28    
## 5500    2.90    -14.39  2.59    -3.16   -0.03   2       0.36    0.30    0.28    
## 5600    -2.96   -16.01  1.19    -2.91   0.09    2       0.36    0.30    0.28    
## 5700    3.40    -15.33  1.89    -4.36   0.01    2       0.36    0.29    0.28    
## 5800    6.29    -16.66  1.87    -4.56   0.01    2       0.37    0.30    0.28    
## 5900    4.00    -14.86  3.27    -5.22   -0.02   2       0.36    0.30    0.28    
## 6000    5.65    -14.36  3.26    -4.23   -0.02   2       0.37    0.30    0.28    
## 6100    6.75    -14.92  6.14    -3.57   0.03    2       0.37    0.30    0.28    
## 6200    5.08    -14.85  5.06    -3.50   0.03    2       0.37    0.30    0.28    
## 6300    4.98    -14.62  4.57    -4.34   0.03    2       0.37    0.30    0.28    
## 6400    2.67    -16.15  1.73    -3.54   0.03    2       0.37    0.30    0.28    
## 6500    6.27    -15.24  2.62    -4.46   0.03    2       0.37    0.30    0.28    
## 6600    5.48    -17.11  1.90    -5.19   0.03    2       0.37    0.30    0.29    
## 6700    7.25    -15.61  2.65    -4.89   0.02    2       0.37    0.30    0.29    
## 6800    6.79    -13.87  3.52    -4.23   0.02    2       0.37    0.30    0.28    
## 6900    8.25    -14.68  4.41    -4.12   0.02    2       0.37    0.30    0.28    
## 7000    6.22    -14.89  5.02    -4.34   0.02    2       0.36    0.29    0.28    
## 7100    3.58    -13.51  5.06    -2.72   0.02    2       0.36    0.29    0.28    
## 7200    5.21    -14.77  6.37    -3.89   0.02    2       0.36    0.30    0.28    
## 7300    3.56    -14.81  7.96    -3.09   0.02    2       0.36    0.30    0.28    
## 7400    2.95    -13.53  2.73    -3.91   0.02    2       0.36    0.30    0.28    
## 7500    3.93    -14.63  3.10    -4.87   0.01    2       0.36    0.30    0.28    
## 7600    3.70    -14.33  4.44    -3.46   0.01    2       0.36    0.30    0.28    
## 7700    6.01    -15.28  3.72    -4.48   0.01    2       0.36    0.30    0.28    
## 7800    6.21    -13.94  4.50    -4.05   0.02    2       0.36    0.30    0.28    
## 7900    6.71    -14.44  5.02    -4.42   0.02    2       0.36    0.30    0.28    
## 8000    7.11    -14.77  3.57    -4.64   0.02    2       0.36    0.30    0.28    
## 8100    7.19    -15.21  4.76    -4.50   -0.01   2       0.36    0.30    0.28    
## 8200    6.85    -15.10  4.76    -4.22   -0.01   2       0.36    0.30    0.28    
## 8300    7.45    -14.85  3.53    -4.22   -0.01   2       0.36    0.29    0.28    
## 8400    4.20    -13.71  2.38    -4.22   -0.01   2       0.36    0.29    0.28    
## 8500    7.28    -14.59  5.26    -4.18   -0.01   2       0.36    0.29    0.28    
## 8600    3.60    -13.86  2.53    -4.02   -0.01   2       0.36    0.29    0.28    
## 8700    7.11    -15.08  2.85    -4.22   0.02    2       0.36    0.29    0.28    
## 8800    1.04    -14.55  3.84    -2.57   0.02    2       0.35    0.29    0.28    
## 8900    5.55    -13.92  3.67    -4.47   0.02    2       0.35    0.29    0.28    
## 9000    7.65    -14.61  4.16    -4.70   0.02    2       0.35    0.29    0.27    
## 9100    7.43    -14.66  4.46    -4.02   0.02    2       0.35    0.29    0.27    
## 9200    4.97    -14.87  2.33    -5.10   0.02    2       0.36    0.29    0.28    
## 9300    5.75    -14.20  4.54    -4.01   0.02    2       0.36    0.29    0.28    
## 9400    4.72    -13.89  3.25    -3.53   -0.01   2       0.36    0.29    0.28    
## 9500    5.29    -15.62  1.65    -4.53   -0.01   2       0.36    0.29    0.28    
## 9600    3.53    -16.26  6.59    -4.32   -0.01   2       0.36    0.29    0.28    
## 9700    4.94    -14.52  3.46    -3.29   -0.01   2       0.36    0.29    0.28    
## 9800    4.79    -15.76  5.13    -4.55   -0.01   2       0.35    0.29    0.28    
## 9900    5.08    -14.78  2.13    -4.71   -0.01   2       0.35    0.29    0.28    
## 10000   1.73    -13.45  3.76    -3.24   -0.01   2       0.35    0.29    0.28
chain <- mymcmc.fixed$load()

# Plot to see if sigma^2 is estimated ok:
sig2post <- do.call(rbind, chain$sig2)
plot(c(0, nrow(sig2post)),c(min(sig2post)-1, max(sig2post)+1), type="n", xlab="mcmc sample", ylab="log sigma^2")
lapply(1:ncol(sig2post), function(x) lines(1:nrow(sig2post), sig2post[,x], col=x+1))
abline(h=parsT$sig2, col=1+(1:ncol(sig2post)))

plot of chunk unnamed-chunk-14

This seems pretty reasonable, given that the trees are pretty small.

That's all, any feedback is greatly appreciated!!