├── .github └── workflows │ └── main.yml ├── .gitignore ├── COPYING ├── Dockerfile ├── README.md ├── XML ├── 4_time_samples_muinf.xml ├── 4_time_samples_muinf_fixed_alignment.xml ├── ALLtest.xml ├── BFTest.xml ├── BSSVS │ ├── 4deme.xml │ ├── 4deme_pole.xml │ ├── 4deme_static.xml │ ├── CVsampler.py │ ├── CVwithPole.xml │ └── pole_plots.R ├── EwingTest.xml ├── EwingTest2.xml ├── NSRtest1.xml ├── NSRtest2.xml ├── STXRtest.xml ├── STXtest.xml ├── Sanity.xml ├── TWBRtest.xml ├── TWBtest.xml ├── analysis │ ├── CWB_SC_3taxa.pdf │ ├── SC_3taxa.pdf │ ├── SC_3taxa_combined.pdf │ ├── fit_height_distribs.R │ ├── fits0.1.pdf │ ├── fits0.2.pdf │ ├── plot_3taxa.R │ └── plot_CWB_3taxa.R ├── badness_eradication │ ├── 2deme_0.5samples.xml │ ├── conditionedPathGen.R │ ├── countSampling.R │ ├── hybrid.xml │ ├── plot_countProb.R │ ├── simulated_alignment.xml │ └── true_tree.nexus ├── figure_StructuredCoalescentColouredTree.xml ├── large_mrate_testing │ ├── large_mrate_inference.xml │ ├── large_mrate_inference_fixed_alignment.xml │ ├── large_mrate_test.template │ ├── largem_results.pdf │ ├── plot_results.R │ ├── runsamps.sh │ └── runsims.sh └── simulated_data │ ├── alignmentConverter │ ├── full_inference.xml │ ├── simulated_alignment.xml │ └── true_tree.nexus ├── action.yml ├── build.xml ├── doc ├── .gitignore └── operator_notes │ ├── SubtreeSlide.tex │ └── WilsonBaldingRandom.tex ├── examples ├── h3n2.fna ├── h3n2_2deme.fna └── structuredCoalescent.xml ├── fxtemplates ├── MTTClockModels.xml └── MultiTypeTree.xml ├── lib ├── LICENSE.guava ├── LICENSE.jblas ├── guava-15.0.jar └── jblas-1.2.5.jar ├── src └── multitypetree │ ├── app │ └── beauti │ │ ├── InitMigrationModelConnector.java │ │ ├── MigrationModelInputEditor.java │ │ ├── MigrationModelInputEditorFX.java │ │ └── TypeTraitSetInputEditor.java │ ├── distributions │ ├── ExcludablePrior.java │ ├── MRCATypePrior.java │ ├── MultiTypeTreeDistribution.java │ ├── PriorWithPole.java │ ├── StructuredCoalescentTreeDensity.java │ ├── StructuredCoalescentUntypedTreeDensity.java │ └── TypeChangeTimeCondition.java │ ├── evolution │ └── tree │ │ ├── FlatMultiTypeTree.java │ │ ├── MigrationModel.java │ │ ├── MultiTypeNode.java │ │ ├── MultiTypeTree.java │ │ ├── MultiTypeTreeFromFlatTree.java │ │ ├── MultiTypeTreeFromNewick.java │ │ ├── MultiTypeTreeFromUntypedNewick.java │ │ ├── RandomMultiTypeTree.java │ │ ├── SCMigrationModel.java │ │ ├── StructuredCoalescentMultiTypeTree.java │ │ ├── StructuredCoalescentUntypedTree.java │ │ └── TypeSet.java │ ├── operators │ ├── BeerliFelsenstein.java │ ├── MultiTypeTreeOperator.java │ ├── MultiTypeTreeScale.java │ ├── MultiTypeUniform.java │ ├── NodeRetype.java │ ├── NodeRetypeRandom.java │ ├── NodeShiftRetype.java │ ├── NodeShiftRetypeRandom.java │ ├── RandomRetypeOperator.java │ ├── SpecialTypeBirthDeath.java │ ├── TypeBirthDeath.java │ ├── TypeMergeSplit.java │ ├── TypeMergeSplitExtended.java │ ├── TypePairBirthDeath.java │ ├── TypedSubtreeExchange.java │ ├── TypedSubtreeExchangeEasy.java │ ├── TypedSubtreeExchangeRandom.java │ ├── TypedWilsonBalding.java │ ├── TypedWilsonBaldingEasy.java │ ├── TypedWilsonBaldingRandom.java │ ├── UniformizationRetypeOperator.java │ └── ZeroJump.java │ └── util │ ├── MAPTreeLogger.java │ ├── MigrationModelLogger.java │ ├── MultiTypeTreeStatLogger.java │ ├── NodeTypeCounts.java │ ├── TreeLengthLogger.java │ ├── TreeRootTypeLogger.java │ ├── TypeChangeCounts.java │ ├── TypeLengths.java │ ├── TypedNodeTreeLogger.java │ └── UtilMethods.java ├── test └── multitypetree │ ├── coalescent │ └── SCLikelihoodTest.java │ └── operators │ ├── Ewing_Test.java │ ├── NSR_Test.java │ ├── STXR_NRR_MTU_TS_Test.java │ ├── STX_NR_MTU_TS_Test.java │ ├── TWBR_TS_Test.java │ └── TWB_TS_Test.java └── version.xml /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Unit/integration tests 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: tgvaughan/MultiTypeTree@master 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories 2 | /dist 3 | /build 4 | /build-lib 5 | /out 6 | 7 | # netbeans cruft 8 | /nbbuild.xml 9 | /manifest.mf 10 | /nbproject 11 | 12 | # intellij cruft 13 | /.idea 14 | /MultiTypeTree.iml 15 | /META-INF 16 | 17 | # Ignore BEAST logfiles 18 | *.log 19 | *.trees 20 | *.state 21 | 22 | # Other cruft: 23 | .localrc 24 | .localrcbye 25 | /examples/h3n2 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile to build container for unit testing 2 | 3 | FROM debian:stable 4 | 5 | RUN apt-get update 6 | RUN apt-get install -y openjdk-17-jdk openjfx 7 | RUN apt-get install -y ant 8 | RUN apt-get install -y jblas 9 | 10 | WORKDIR /root 11 | 12 | ADD . ./ 13 | 14 | RUN rm lib/jblas-*.jar 15 | RUN ln -s /usr/share/java/jblas.jar lib/jblas.jar 16 | 17 | ENTRYPOINT JAVA_FX_HOME=/usr/share/java/ ant test 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MultiTypeTree 2 | ============= 3 | 4 | This is a BEAST 2 package which allows for the inference of multi-type 5 | or structured phylogenetic trees. It includes a full set of proposal 6 | operators and currently the structured coalescent model, allowing 7 | migration rates and sub-population sizes to be inferred from 8 | serially-sampled sequence data. The multi-type trees and the 9 | operators contained in this package can also be used as the basis for 10 | structured population inference under other models. 11 | 12 | For further information, please refer to the MultiTypeTree [web 13 | page](http://tgvaughan.github.io/MultiTypeTree). 14 | 15 | [![Build Status](https://github.com/tgvaughan/MultiTypeTree/workflows/Unit%2Fintegration%20tests/badge.svg)](https://github.com/tgvaughan/MultiTypeTree/actions?query=workflow%3A%22Unit%2Fintegration+tests%22) 16 | 17 | License 18 | ------- 19 | 20 | This software is free (as in freedom). With the exception of the 21 | libraries on which it depends, it is made available under the terms of 22 | the GNU General Public Licence version 3, which is contained in this 23 | directory in the file named COPYING. 24 | 25 | The following libraries are bundled with MultiTypeTree: 26 | 27 | * Google Guava (http://code.google.com/p/guava-libraries/) 28 | * jblas (http://mikiobraun.github.io/jblas/) 29 | 30 | That software is distributed under the licences provided in the 31 | LICENCE.* files included in this archive. 32 | 33 | Work on this project is made possible by generous funding from the 34 | [Allan Wilson Centre for Molecular Ecology and 35 | Evolution](http://www.allanwilsoncentre.ac.nz/). 36 | -------------------------------------------------------------------------------- /XML/ALLtest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 34 | 35 | 37 | 38 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /XML/BFTest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /XML/BSSVS/CVsampler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from scipy import * 4 | import scipy.stats as stats 5 | from matplotlib import pylab 6 | 7 | seterr(divide='raise') 8 | 9 | class State: 10 | x = None 11 | xStored = None 12 | 13 | def __init__(self, x0): 14 | self.x = x0 15 | 16 | def store(self): 17 | self.xStored = self.x 18 | 19 | def restore(self): 20 | self.x = self.xStored 21 | 22 | def proposeJump (state): 23 | logHR = 0 24 | 25 | if state.x>0: 26 | logHR = log(stats.beta.pdf(state.x,1,5)) 27 | state.x = 0 28 | else: 29 | state.x = stats.beta.rvs(1,5) 30 | logHR = -log(stats.beta.pdf(state.x,1,5)) 31 | 32 | return logHR 33 | 34 | def proposeScale (state): 35 | # if state.x>0: 36 | fmin = 0.9 37 | fmax = 1/0.9 38 | f = stats.uniform.rvs(fmin,(fmax-fmin)) 39 | 40 | state.x *= f 41 | 42 | return -log(f) 43 | 44 | # else: 45 | # return -float('inf') 46 | 47 | 48 | def targetDensity (state): 49 | 50 | p0 = 0.05 51 | 52 | if state.x>0: 53 | if state.x<1: 54 | return log((1-p0)*stats.beta.pdf(state.x,2,2)) 55 | else: 56 | return -float('inf') 57 | else: 58 | return log(p0) 59 | 60 | 61 | def MCMC (): 62 | 63 | burninFrac = 0.2 64 | maxiter = 100000 65 | sampPeriod = 10 66 | 67 | chain = [] 68 | 69 | state = State(0.5) 70 | 71 | for i in range(maxiter): 72 | 73 | state.store() 74 | 75 | logAlpha = -targetDensity(state) 76 | 77 | logHR = 0.0 78 | if stats.uniform.rvs(0,1)<0.5: 79 | logHR = proposeJump(state) 80 | else: 81 | logHR = proposeScale(state) 82 | 83 | logAlpha += targetDensity(state) + logHR 84 | 85 | if stats.uniform.rvs(0,1)>exp(logAlpha): 86 | state.restore() 87 | 88 | if i>maxiter*burninFrac and i%sampPeriod==0: 89 | chain.append(state.x) 90 | 91 | return chain 92 | 93 | 94 | if __name__ == '__main__': 95 | 96 | chain = MCMC() 97 | 98 | zeroFrac = sum([float(x==0) for x in chain])/len(chain) 99 | print "Zero fraction = {}".format(zeroFrac) 100 | 101 | pylab.hist(chain, bins=50) 102 | pylab.show() 103 | -------------------------------------------------------------------------------- /XML/BSSVS/CVwithPole.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /XML/BSSVS/pole_plots.R: -------------------------------------------------------------------------------- 1 | df <- read.table('4deme_pole.log', header=T) 2 | 3 | burninfrac = 0.1 4 | indices <- round(burninfrac*length(df$Sample)):length(df$Sample) 5 | 6 | nparams <- (df$migModel.rateMatrixBackward_0_1>0) + 7 | (df$migModel.rateMatrixBackward_0_2>0) + 8 | (df$migModel.rateMatrixBackward_0_3>0) + 9 | (df$migModel.rateMatrixBackward_1_0>0) + 10 | (df$migModel.rateMatrixBackward_1_2>0) + 11 | (df$migModel.rateMatrixBackward_1_3>0) + 12 | (df$migModel.rateMatrixBackward_2_0>0) + 13 | (df$migModel.rateMatrixBackward_2_1>0) + 14 | (df$migModel.rateMatrixBackward_2_3>0) + 15 | (df$migModel.rateMatrixBackward_3_0>0) + 16 | (df$migModel.rateMatrixBackward_3_1>0) + 17 | (df$migModel.rateMatrixBackward_3_2>0) 18 | -------------------------------------------------------------------------------- /XML/EwingTest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 36 | 37 | 38 | 39 | 42 | 44 | 45 | 48 | 49 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /XML/EwingTest2.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 34 | 37 | 38 | 39 | 40 | 43 | 44 | 46 | 47 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /XML/NSRtest1.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | (1[state='1']:1,2[state='0']:1)[state='0']:0; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /XML/NSRtest2.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | ((1[state='0']:1,2[state='0']:1)[state='0']:1,3[state='0']:2)[state='0']:0; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /XML/STXRtest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | ((1[state='0']:1,2[state='0']:1)[state='0']:1,3[state='0']:2)[state='0']:0; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 35 | 36 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /XML/STXtest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 34 | 35 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /XML/Sanity.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /XML/TWBRtest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | ((1[deme='0']:1,2[deme='0']:1)[deme='0']:1,3[deme='0']:2)[deme='0']:0; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /XML/TWBtest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | ((1[deme='0']:1,2[deme='0']:1)[deme='0']:1,3[deme='0']:2)[deme='0']:0; 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /XML/analysis/CWB_SC_3taxa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/analysis/CWB_SC_3taxa.pdf -------------------------------------------------------------------------------- /XML/analysis/SC_3taxa.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/analysis/SC_3taxa.pdf -------------------------------------------------------------------------------- /XML/analysis/SC_3taxa_combined.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/analysis/SC_3taxa_combined.pdf -------------------------------------------------------------------------------- /XML/analysis/fit_height_distribs.R: -------------------------------------------------------------------------------- 1 | # Fit gamma distributions to densities of sampled tree 2 | # heights and compare with exact results for summary statistics 3 | 4 | # Clear workspace 5 | rm(list=ls()) 6 | 7 | require(MASS) 8 | 9 | # Data set to use: 10 | mu=0.1 11 | 12 | muStr = format(mu, digits=1) 13 | 14 | # Fraction of chain to discard: 15 | burninFrac <- 0.2 16 | 17 | # Load results of MCMC runs: 18 | dfSame <- read.table(paste('SC_same',muStr,'.log',sep=''), header=T) 19 | dfDiff <- read.table(paste('SC_different',muStr,'.log',sep=''), header=T) 20 | 21 | burnin <- floor(length(dfSame$tree.height)*burninFrac) 22 | datSame <- dfSame$tree.height[seq(1,length(dfSame$tree.height))>burnin] 23 | burnin <- floor(length(dfDiff$tree.height)*burninFrac) 24 | datDiff <- dfDiff$tree.height[seq(1,length(dfDiff$tree.height))>burnin] 25 | 26 | sameMean <- mean(datSame) 27 | sameVar <- var(datSame) 28 | diffMean <- mean(datDiff) 29 | diffVar <- var(datDiff) 30 | 31 | # Set up plot: 32 | pdf(paste('fits',muStr,'.pdf',sep=''), width=7, height=5) 33 | 34 | histSame <- hist(datSame, breaks=500, plot=F) 35 | plot(histSame$mids, histSame$density, 'l', lwd=2, col='blue', 36 | xlab='Tree height', 37 | ylab='Density', 38 | main=substitute(paste("Tuning parameter ",mu==muval),list(muval=mu))) 39 | 40 | fitSame <- fitdistr(datSame, "gamma") 41 | lines(histSame$mids, dgamma(histSame$mids, shape=fitSame$estimate[[1]], 42 | rate=fitSame$estimate[[2]]), lwd=2, lty=2, col='blue') 43 | 44 | fitSameMean <- fitSame$estimate[[1]]/fitSame$estimate[[2]] 45 | fitSameVar <- fitSame$estimate[[1]]/fitSame$estimate[[2]]^2 46 | 47 | histDiff <- hist(datDiff, breaks=500, plot=F) 48 | lines(histDiff$mids, histDiff$density, lwd=2, col='red') 49 | 50 | fitDiff <- fitdistr(datDiff, "gamma") 51 | lines(histDiff$mids, dgamma(histDiff$mids, shape=fitDiff$estimate[[1]], 52 | rate=fitDiff$estimate[[2]]), lwd=2, lty=2, col='red') 53 | 54 | fitDiffMean <- fitDiff$estimate[[1]]/fitDiff$estimate[[2]] 55 | fitDiffVar <- fitDiff$estimate[[1]]/fitDiff$estimate[[2]]^2 56 | 57 | legend('topright', inset=.05, 58 | c(paste('Same deme: mean=', format(sameMean, digits=4), 59 | ' var=',format(sameVar, digits=4), sep=''), 60 | paste('Different demes: mean=', format(diffMean, digits=4), 61 | ' var=', format(diffVar, digits=4), sep=''), 62 | 'Fitted gamma distributions'), 63 | lty=c(1,1,2), col=c('blue','red','black'), lwd=2) 64 | 65 | dev.off() 66 | -------------------------------------------------------------------------------- /XML/analysis/fits0.1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/analysis/fits0.1.pdf -------------------------------------------------------------------------------- /XML/analysis/fits0.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/analysis/fits0.2.pdf -------------------------------------------------------------------------------- /XML/analysis/plot_3taxa.R: -------------------------------------------------------------------------------- 1 | # Analysis of 3 taxon sampling results 2 | 3 | # Clear workspace: 4 | rm(list=ls()) 5 | 6 | # Load data: 7 | dfSimSame <- read.table('SC_3taxa_same_sim.txt', header=T) 8 | dfSame <- read.table('SC_3taxa_same.log', header=T) 9 | dfSimDiff <- read.table('SC_3taxa_diff_sim.txt', header=T) 10 | dfDiff <- read.table('SC_3taxa_diff.log', header=T) 11 | dfSameComb <- read.table('SC_3taxa_same_combined.log', header=T) 12 | dfDiffComb <- read.table('SC_3taxa_diff_combined.log', header=T) 13 | 14 | # Calculate means and variances 15 | burninFrac <- 0.05 16 | 17 | noburnin <- function(x, f) { 18 | return(x[seq(1,length(x))>(f*length(x))]) 19 | } 20 | 21 | datSimSame <- noburnin(dfSimSame$h, burninFrac) 22 | meanSimSame <- mean(datSimSame) 23 | varSimSame <- var(datSimSame) 24 | semSimSame <- sqrt(varSimSame/length(datSimSame)) 25 | 26 | datSame <- noburnin(dfSame$tree.height, burninFrac) 27 | meanSame <- mean(datSame) 28 | varSame <- var(datSame) 29 | semSame <- sqrt(varSame/length(datSame)) 30 | 31 | datSimDiff <- noburnin(dfSimDiff$h, burninFrac) 32 | meanSimDiff <- mean(datSimDiff) 33 | varSimDiff <- var(datSimDiff) 34 | semSimDiff <- sqrt(varSimDiff/length(datSimDiff)) 35 | 36 | datDiff <- noburnin(dfDiff$tree.height, burninFrac) 37 | meanDiff <- mean(datDiff) 38 | varDiff <- var(datDiff) 39 | semDiff <- sqrt(varDiff/length(datDiff)) 40 | 41 | # Calculate densities: 42 | hSimSame <- hist(datSimSame, breaks=200, plot=F) 43 | hSame <- hist(datSame, breaks=200, plot=F) 44 | hSimDiff <- hist(datSimDiff, breaks=200, plot=F) 45 | hDiff <- hist(datDiff, breaks=200, plot=F) 46 | 47 | 48 | # Plot figure 49 | pdf('SC_3taxa.pdf', onefile=F, width=7, height=5) 50 | plot(hSame$mids, hSame$density, 'l', lwd=1, col='blue', 51 | xlab='Tree height', 52 | ylab='Rel. frequency', 53 | main='Coloured WB operator on 3 taxon trees under SC') 54 | lines(hSimSame$mids, hSimSame$density, lwd=2, lty=2, col='blue') 55 | lines(hDiff$mids, hDiff$density, 'l', lwd=1, col='red') 56 | lines(hSimDiff$mids, hSimDiff$density, lwd=2, lty=2, col='red') 57 | 58 | # Add descriptive legend 59 | legend('topright', inset=.05, 60 | c(paste('Same (MCMC): mean=', format(meanSame, digits=4), 61 | '+/-', format(semSame, digits=2), 62 | ', var=', format(varSame, digits=4), 63 | sep=''), 64 | paste('Same (sim): mean=', format(meanSimSame, digits=4), 65 | '+/-', format(semSimSame, digits=2), 66 | ', var=', format(varSimSame, digits=4), 67 | sep=''), 68 | paste('Diff (MCMC): mean=', format(meanDiff, digits=4), 69 | '+/-', format(semDiff, digits=2), 70 | ', var=', format(varDiff, digits=4), 71 | sep=''), 72 | paste('Diff (sim): mean=', format(meanSimDiff, digits=4), 73 | '+/-', format(semSimDiff, digits=2), 74 | ', var=', format(varSimDiff, digits=4), 75 | sep='')), 76 | lty=c(1,2,1,2), 77 | lwd=c(1,2,1,2), 78 | col=c('blue','blue','red','red')) 79 | 80 | # Close figure: 81 | dev.off() 82 | 83 | # Process combined operator results: 84 | datSameComb <- noburnin(dfSameComb$tree.height, burninFrac) 85 | meanSameComb <- mean(datSameComb) 86 | varSameComb <- var(datSameComb) 87 | semSameComb <- sqrt(varSameComb/length(datSameComb)) 88 | datDiffComb <- noburnin(dfDiffComb$tree.height, burninFrac) 89 | meanDiffComb <- mean(datDiffComb) 90 | varDiffComb <- var(datDiffComb) 91 | semDiffComb <- sqrt(varDiffComb/length(datDiffComb)) 92 | 93 | hSameComb <- hist(datSameComb, breaks=200, plot=F) 94 | hDiffComb <- hist(datDiffComb, breaks=200, plot=F) 95 | 96 | # Plot combined operator results: 97 | pdf('SC_3taxa_combined.pdf', onefile=F, width=7, height=5) 98 | plot(hSameComb$mids, hSameComb$density, 'l', lwd=1, col='blue', 99 | xlab='Tree height', 100 | ylab='Rel. frequency', 101 | main='CWBR and tree scale operators on 3 taxon trees under SC') 102 | lines(hSimSame$mids, hSimSame$density, lwd=2, lty=2, col='blue') 103 | lines(hDiffComb$mids, hDiffComb$density, 'l', lwd=1, col='red') 104 | lines(hSimDiff$mids, hSimDiff$density, lwd=2, lty=2, col='red') 105 | 106 | # Add descriptive legend 107 | legend('topright', inset=.05, 108 | c(paste('Same (MCMC): mean=', format(meanSameComb, digits=4), 109 | '+/-', format(semSameComb, digits=2), 110 | ', var=', format(varSameComb, digits=4), 111 | sep=''), 112 | paste('Same (sim): mean=', format(meanSimSame, digits=4), 113 | '+/-', format(semSimSame, digits=2), 114 | ', var=', format(varSimSame, digits=4), 115 | sep=''), 116 | paste('Diff (MCMC): mean=', format(meanDiffComb, digits=4), 117 | '+/-', format(semDiffComb, digits=2), 118 | ', var=', format(varDiffComb, digits=4), 119 | sep=''), 120 | paste('Diff (sim): mean=', format(meanSimDiff, digits=4), 121 | '+/-', format(semSimDiff, digits=2), 122 | ', var=', format(varSimDiff, digits=4), 123 | sep='')), 124 | lty=c(1,2,1,2), 125 | lwd=c(1,2,1,2), 126 | col=c('blue','blue','red','red')) 127 | 128 | # Close figure: 129 | dev.off() 130 | -------------------------------------------------------------------------------- /XML/analysis/plot_CWB_3taxa.R: -------------------------------------------------------------------------------- 1 | # Analysis of 3 taxon sampling results 2 | 3 | # Clear workspace: 4 | rm(list=ls()) 5 | 6 | # Load data: 7 | dfSimSame <- read.table('SC_3taxa_same_sim.txt', header=T) 8 | dfSame <- read.table('CWB_SC_3taxa_same.log', header=T) 9 | dfSimDiff <- read.table('SC_3taxa_diff_sim.txt', header=T) 10 | dfDiff <- read.table('CWB_SC_3taxa_diff.log', header=T) 11 | 12 | # Calculate means and variances 13 | burninFrac <- 0.05 14 | 15 | noburnin <- function(x, f) { 16 | return(x[seq(1,length(x))>(f*length(x))]) 17 | } 18 | 19 | datSimSame <- noburnin(dfSimSame$h, burninFrac) 20 | meanSimSame <- mean(datSimSame) 21 | varSimSame <- var(datSimSame) 22 | semSimSame <- sqrt(varSimSame/length(datSimSame)) 23 | 24 | datSame <- noburnin(dfSame$tree.height, burninFrac) 25 | meanSame <- mean(datSame) 26 | varSame <- var(datSame) 27 | semSame <- sqrt(varSame/length(datSame)) 28 | 29 | datSimDiff <- noburnin(dfSimDiff$h, burninFrac) 30 | meanSimDiff <- mean(datSimDiff) 31 | varSimDiff <- var(datSimDiff) 32 | semSimDiff <- sqrt(varSimDiff/length(datSimDiff)) 33 | 34 | datDiff <- noburnin(dfDiff$tree.height, burninFrac) 35 | meanDiff <- mean(datDiff) 36 | varDiff <- var(datDiff) 37 | semDiff <- sqrt(varDiff/length(datDiff)) 38 | 39 | # Calculate densities: 40 | hSimSame <- hist(datSimSame, breaks=200, plot=F) 41 | hSame <- hist(datSame, breaks=200, plot=F) 42 | hSimDiff <- hist(datSimDiff, breaks=200, plot=F) 43 | hDiff <- hist(datDiff, breaks=200, plot=F) 44 | 45 | 46 | # Plot figure 47 | pdf('CWB_SC_3taxa.pdf', onefile=F, width=7, height=5) 48 | plot(hSame$mids, hSame$density, 'l', lwd=1, col='blue', 49 | xlab='Tree height', 50 | ylab='Rel. frequency', 51 | main='Full CWB operator on 3 taxon trees under SC') 52 | lines(hSimSame$mids, hSimSame$density, lwd=2, lty=2, col='blue') 53 | lines(hDiff$mids, hDiff$density, 'l', lwd=1, col='red') 54 | lines(hSimDiff$mids, hSimDiff$density, lwd=2, lty=2, col='red') 55 | 56 | # Add descriptive legend 57 | legend('topright', inset=.05, 58 | c(paste('Same (MCMC): mean=', format(meanSame, digits=4), 59 | '+/-', format(semSame, digits=2), 60 | ', var=', format(varSame, digits=4), 61 | sep=''), 62 | paste('Same (sim): mean=', format(meanSimSame, digits=4), 63 | '+/-', format(semSimSame, digits=2), 64 | ', var=', format(varSimSame, digits=4), 65 | sep=''), 66 | paste('Diff (MCMC): mean=', format(meanDiff, digits=4), 67 | '+/-', format(semDiff, digits=2), 68 | ', var=', format(varDiff, digits=4), 69 | sep=''), 70 | paste('Diff (sim): mean=', format(meanSimDiff, digits=4), 71 | '+/-', format(semSimDiff, digits=2), 72 | ', var=', format(varSimDiff, digits=4), 73 | sep='')), 74 | lty=c(1,2,1,2), 75 | lwd=c(1,2,1,2), 76 | col=c('blue','blue','red','red')) 77 | 78 | # Close figure: 79 | dev.off() 80 | -------------------------------------------------------------------------------- /XML/badness_eradication/conditionedPathGen.R: -------------------------------------------------------------------------------- 1 | # Conditioned CTMC path generation 2 | 3 | drawPath <- function (startType, m, L) { 4 | 5 | times <- c() 6 | types <- c() 7 | 8 | t <- 0 9 | d <- startType 10 | while (TRUE) { 11 | 12 | t <- t + rexp(1,-Q[d,d]) 13 | 14 | destTypes <- which(Q[d,]>=0) 15 | d <- sample(destTypes, 1, prob=Q[d,destTypes]) 16 | 17 | if (t>L) { 18 | break 19 | } 20 | 21 | times <- append(times,t) 22 | types <- append(types,d) 23 | } 24 | 25 | res <- list() 26 | res$times <- times 27 | res$types <- types 28 | 29 | return (res) 30 | 31 | } 32 | 33 | drawConditionedPath <- function (startType, endType, m, L) { 34 | 35 | while (TRUE) { 36 | res <- drawPath(startType, m, L) 37 | if (length(res$types)>0) { 38 | if (res$types[length(res$types)]==endType) 39 | return (res) 40 | } else { 41 | if (startType == endType) 42 | return (res) 43 | } 44 | } 45 | 46 | } 47 | 48 | generateEnsemble <- function (N, startType, endType, m, L) { 49 | 50 | totalCounts <- c() 51 | counts <- list() 52 | 53 | for (i in 1:N) { 54 | path <- drawConditionedPath(startType, endType, m, L) 55 | totalCounts <- append(totalCounts, length(path$times)) 56 | if (i == 1) { 57 | for (c in 1:dim(m)[1]) 58 | counts[[c]] <- sum(path$types==c) 59 | } else { 60 | for (c in 1:dim(m)[1]) 61 | counts[[c]] <- append(counts[[c]], sum(path$types==c)) 62 | } 63 | } 64 | 65 | res <- list() 66 | res$totalCounts <- totalCounts 67 | res$counts <- counts 68 | 69 | return (res) 70 | 71 | } 72 | 73 | # Four-colour path generation 74 | Q = matrix( 75 | data=c( 76 | -0.25, 0.20, 0.02, 0.03, 77 | 0.04, -0.15, 0.05, 0.06, 78 | 0.07, 0.08, -0.24, 0.09, 79 | 0.10, 0.11, 0.12, -0.33 80 | ), 81 | nrow=4, ncol=4, byrow=T) 82 | 83 | ensemble <- generateEnsemble(10000, 4, 2, Q, 200) 84 | -------------------------------------------------------------------------------- /XML/badness_eradication/countSampling.R: -------------------------------------------------------------------------------- 1 | require(expm) 2 | 3 | # Set up matrices 4 | Q <- matrix(data=c(-208.516676, 208.516676, 0.002952, -0.002952), 5 | nrow=2, ncol=2, byrow=T) 6 | rho <- max(-diag(Q)) 7 | R <- Q/rho + diag(2) 8 | 9 | lambda <- 1.008736926575113 10 | mu <- 208.5166757820231 11 | a <- 0 12 | b <- 0 13 | 14 | N <- 2 15 | 16 | # Slow but correct count sampling function: 17 | sampleCount <- function(sampleSize) { 18 | 19 | res <- rep(0,sampleSize) 20 | for (i in 1:sampleSize) { 21 | cat(paste("Sample",i,"\n")) 22 | repeat { 23 | n <- rpois(1, mu*lambda) 24 | P_b_given_na <- (R %^% n)[a+1, b+1] 25 | if (runif(1)N) 58 | break; 59 | } 60 | } 61 | 62 | res[i] <- n 63 | } 64 | 65 | return (res) 66 | } 67 | -------------------------------------------------------------------------------- /XML/badness_eradication/plot_countProb.R: -------------------------------------------------------------------------------- 1 | require(expm) 2 | 3 | M <- matrix(data=c(0,0,0.05,0.05, 4 | 0.05,0,0.05,0, 5 | 0.05,0.05,0,0, 6 | 0,0,0,0), nrow=4, byrow=T) 7 | 8 | Q <- M - diag(rowSums(M)) 9 | rho <- max(-diag(Q)) 10 | R <- Q/rho + diag(4) 11 | 12 | a <- 2 13 | b <- 1 14 | L <- 0.04879582996085663 15 | 16 | muL <- rho*L 17 | Pba <- expm(Q*L)[a+1,b+1] 18 | 19 | n <- 0:200 20 | 21 | getLogP <- function(x) { 22 | logP <- (R %^% x)[a+1,b+1] + x*log(muL) - lgamma(x+1) - muL - Pba 23 | return(exp(logP)) 24 | } 25 | probs <- lapply(n, getLogP) 26 | -------------------------------------------------------------------------------- /XML/figure_StructuredCoalescentColouredTree.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 7 | 9 | 10 | 11 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /XML/large_mrate_testing/large_mrate_test.template: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 36 | 37 | 40 | 41 | 44 | 45 | 48 | 49 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /XML/large_mrate_testing/largem_results.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/XML/large_mrate_testing/largem_results.pdf -------------------------------------------------------------------------------- /XML/large_mrate_testing/plot_results.R: -------------------------------------------------------------------------------- 1 | dfsims <- list() 2 | dfsamps <- list() 3 | 4 | mvals <- c(0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64) 5 | 6 | simHeightMeans <- rep(0,length(mvals)) 7 | simHeightVars <- rep(0,length(mvals)) 8 | simCountMeans <- rep(0,length(mvals)) 9 | simCountVars <- rep(0,length(mvals)) 10 | 11 | sampHeightMeans <- rep(0,length(mvals)) 12 | sampHeightVars <- rep(0,length(mvals)) 13 | sampCountMeans <- rep(0,length(mvals)) 14 | sampCountVars <- rep(0,length(mvals)) 15 | 16 | 17 | for (i in seq(1,length(mvals))) { 18 | m <- mvals[i] 19 | simfname <- paste('heights',m,'.txt', sep='') 20 | sampfname <- paste('large_mrate_test_',m,'.log', sep='') 21 | 22 | dfsim <- read.table(simfname, header=T) 23 | simHeightMeans[i] <- mean(dfsim$h) 24 | simHeightVars[i] <- var(dfsim$h) 25 | simCountMeans[i] <- mean(dfsim$c) 26 | simCountVars[i] <- var(dfsim$c) 27 | 28 | dfsamp <- read.table(sampfname, header=T) 29 | sampHeightMeans[i] <- mean(dfsamp$tree.height) 30 | sampHeightVars[i] <- var(dfsamp$tree.height) 31 | sampCountMeans[i] <- mean(dfsamp$tree.count) 32 | sampCountVars[i] <- var(dfsamp$tree.count) 33 | } 34 | 35 | 36 | # Plot figure 37 | 38 | pdf('largem_results.pdf', onefile=F, width=7, height=5) 39 | par(mfrow=c(2,2)) 40 | par(mgp=c(1.5,0.5,0)) 41 | par(mar=c(3,3,1.5,0.5)) 42 | 43 | plot(mvals, simHeightMeans, 'o', col='red', xlab='m', ylab='tree height', 44 | main='tree height means', log='x') 45 | lines(mvals, sampHeightMeans, 'o', col='blue') 46 | 47 | plot(mvals, simCountMeans, 'o', col='red', xlab='m', ylab='migration count', 48 | main='migration count means', log='xy') 49 | lines(mvals, sampCountMeans, 'o', col='blue') 50 | 51 | plot(mvals, sqrt(simHeightVars), 'o', col='red', xlab='m', ylab='tree height', 52 | main='tree height std. dev.', log='x') 53 | lines(mvals, sqrt(sampHeightVars), 'o', col='blue') 54 | 55 | plot(mvals, sqrt(simCountVars), 'o', col='red', xlab='m', ylab='migration count', 56 | main='migration count std. dev.', log='xy') 57 | lines(mvals, sqrt(sampCountVars), 'o', col='blue') 58 | 59 | dev.off() 60 | -------------------------------------------------------------------------------- /XML/large_mrate_testing/runsamps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for m in 0.125 0.25 0.5 1 2 4 8 16 32 64; do 4 | sed 's/MRATE/'$m'/g' < large_mrate_test.template >large_mrate_test_$m.xml 5 | beast large_mrate_test_$m.xml 6 | done 7 | -------------------------------------------------------------------------------- /XML/large_mrate_testing/runsims.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for m in 0.125 0.25 0.5 1 2 4 8 16 32 64; do 4 | beast -main beast.evolution.tree.coalescent.StructuredCoalescentMultiTypeTree $m 5 | mv heights.txt heights$m.txt 6 | done -------------------------------------------------------------------------------- /XML/simulated_data/alignmentConverter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from sys import argv, exit, stdout 4 | from argparse import ArgumentParser 5 | from xml.etree.ElementTree import ElementTree 6 | 7 | def writeFasta(etree, outFile): 8 | ntaxa = len(etree.getroot().getchildren()) 9 | 10 | for i in range(ntaxa): 11 | taxon = etree.getroot().getchildren()[i].get('taxon') 12 | seq = etree.getroot().getchildren()[i].get('value') 13 | outFile.write('>{}\n{}\n'.format(taxon,seq)) 14 | 15 | def writeNexus(etree, outFile): 16 | 17 | # Get sequence length and taxon count 18 | ntaxa = len(etree.getroot().getchildren()) 19 | nchar = len(etree.getroot().getchildren()[0].get('value')) 20 | 21 | # Write nexus boilerplate 22 | outFile.write("""#nexus 23 | 24 | Begin data; 25 | Dimensions ntax={} nchar={}; 26 | Format datatype=dna symbols="ACTG" missing=? gap=-; 27 | Matrix 28 | """.format(ntaxa, nchar)) 29 | 30 | # Write sequences 31 | for i in range(ntaxa): 32 | taxon = etree.getroot().getchildren()[i].get('taxon') 33 | seq = etree.getroot().getchildren()[i].get('value') 34 | outFile.write('{}\t{}\n'.format(taxon,seq)) 35 | 36 | # Write end boilerplate 37 | outFile.write(";\nEnd;\n") 38 | 39 | 40 | outputFunctions = {'fasta': writeFasta, 'nexus': writeNexus} 41 | 42 | 43 | if __name__=='__main__': 44 | 45 | availableFormats = "" 46 | for fmt in outputFunctions.keys(): 47 | if len(availableFormats)>0: 48 | availableFormats += ", " 49 | availableFormats += fmt 50 | 51 | parser = ArgumentParser(description="Convert alignment from BEAST 2 XML to other format.") 52 | parser.add_argument("format", type=str, help="Output format ({}).".format(availableFormats)) 53 | parser.add_argument("xml_file", type=str, help="File containing alignment in BEAST 2 XML.") 54 | parser.add_argument("-o","--output", type=str, help="Optional name of output file.") 55 | 56 | if len(argv)<2: 57 | parser.print_usage() 58 | exit(0) 59 | 60 | args = parser.parse_args(argv[1:]) 61 | 62 | if args.format not in outputFunctions.keys(): 63 | print "Unsupported output format. Currently supported formats are: {}".format(availableFormats) 64 | exit(1) 65 | 66 | try: 67 | etree = ElementTree(file=args.xml_file) 68 | except: 69 | print "Error opening file '{}' for reading.".format(args.xml_file) 70 | exit(1) 71 | 72 | if args.output == None: 73 | outFile = stdout 74 | else: 75 | try: 76 | outFile = open(args.output, 'w') 77 | except: 78 | print "Error opening file '{}' for writing.".format(args.o) 79 | exit(1) 80 | 81 | 82 | outputFunctions.get(args.format)(etree, outFile) 83 | 84 | if args.output != None: 85 | outFile.close() 86 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Unit Tests' 2 | description: 'Run unit tests for MultiTypeTree' 3 | runs: 4 | using: 'docker' 5 | image: 'Dockerfile' 6 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.aux 3 | ColouredTree.pdf 4 | MultitypeSampler.pdf 5 | *.bbl 6 | *.blg 7 | *.bib 8 | -------------------------------------------------------------------------------- /doc/operator_notes/SubtreeSlide.tex: -------------------------------------------------------------------------------- 1 | \documentclass[a4paper, 11pt]{article} 2 | 3 | \usepackage{amsmath} 4 | 5 | \begin{document} 6 | 7 | \section{Root move acceptance probability} 8 | 9 | The root move acts on a state vector $x=\{T;n_A,t_A,c_A;n_B,t_B,c_B\}$ 10 | where $T$ is the age of the root, $n_A$ and $n_B$ are the numbers of 11 | migration events along each of the two root-connected edges, the 12 | vectors $t_A$ and $t_B$ contain the times of these events and the 13 | vectors $c_A$ and $c_B$ similarly contain the destination colours of 14 | the events. 15 | 16 | The subtree slide move draws a new state $x'=\{T',t_A,t'_B\}$ 17 | from the following proposal density: 18 | \begin{align} 19 | q(x'|x) &= P(T',\vec{t}'_A,n'_A,\vec{t}'_B,n'_B|x) \nonumber \\ 20 | & = P(T'|T)P(n'_A|T',T)P(n'_B|T',T)P(t_A|n'_A)P(\vec{t}_B|n'_B) 21 | \end{align} 22 | 23 | \subsection{Tree height proposal} 24 | 25 | The new tree height is selected by drawing $f'\sim g_\gamma=U(1/\gamma,\gamma)$ 26 | then setting $T'=f'T$. We can then write 27 | \begin{align} 28 | P(T'|T)&=\int df'\delta(T'-f'T)g_\gamma(f')\nonumber\\ 29 | &=\int_{\gamma^{-1}}^{\gamma}df'\delta(T'-f'T)\frac{1}{\gamma-\gamma^{-1}} 30 | \end{align} 31 | Performing the change of variables $f'\rightarrow z$ where $z=Tf'$ 32 | allows us to evaluate this and obtain 33 | \begin{equation} 34 | P(T'|T)=\left\{\begin{array}{rl} 35 | \frac{1}{T(\gamma-\gamma^{-1})} & \text{for } \frac{T}{\gamma}\leq T' \leq\gamma T\\ 36 | 0 & \text{otherwise} 37 | \end{array}\right. 38 | \end{equation} 39 | 40 | \subsection{Migration count proposal} 41 | 42 | The new migration counts $n'_A$ and $n'_B$ are chosen from Poissonian 43 | distributions with means of $T'\mu$, where $\mu$ is a tuning 44 | parameter. That is, 45 | \begin{equation} 46 | P(n'_A|T',T)=e^{-T'\mu}\frac{(T'\mu)^{n'_A}}{n'_A!} 47 | \end{equation} 48 | and similarly for $P(n'_B|T',T)$. 49 | 50 | \subsection{Migration times proposal} 51 | 52 | Each of the new migration times in $t'_A$ is chosen by drawing $n'_A$ 53 | values $\tau'_A$ independently from $U(0,T')$, then sorting them from smallest 54 | to largest. If we denote the sorting function $t'_A=S(\tau'_A)$ we 55 | can write 56 | \begin{align} 57 | P(t'_A|n'_A) &= \int_{[0,T']^{n'_A}} 58 | d^{n'_A}\tau'_A\delta(t'_A-S(\tau'_A))(T')^{-n'_A}\nonumber\\ 59 | &= \frac{n'_A!}{(T')^{n'_A}} 60 | \end{align} 61 | The proposal $P(t'_B|n'_B)$ is defined similarly. 62 | 63 | \subsection{Migration colours proposal} 64 | 65 | Each migration event results in a change of colour at that point in 66 | the lineage. The new colour is selected at random from those remaining 67 | after the present colour is excluded. That is 68 | \begin{equation} 69 | P(c'_A|n'_A) = \frac{1}{(N-1)^{n'_A}} 70 | \end{equation} 71 | where $N$ is the total number of available colours. $P(c'_A|n'_B)$ is 72 | defined similarly. 73 | 74 | \subsection{Full proposal} 75 | 76 | Combining these partial proposals yields the following expression for 77 | the proposal distribution: 78 | \begin{align} 79 | q(x'|x)&= 80 | \frac{1}{T(\gamma-\gamma^{-1})}I(\frac{T'}{T}\in[\frac{1}{\gamma},\gamma])\times 81 | e^{-T'\mu}\frac{(T'\mu)^{n'_A}}{n'_A!}\times 82 | \frac{n'_A!}{(T')^{n'_A}}\times \frac{1}{(N-1)^{n'_A}}\nonumber\\ 83 | &\times e^{-T'\mu}\frac{(T'\mu)^{n'_B}}{n'_B!}\times 84 | \frac{n'_B!}{(T')^{n'_B}}\times \frac{1}{(N-1)^{n'_B}}\nonumber\\ 85 | &=\left\{\begin{array}{rl} 86 | \frac{1}{T(\gamma-\gamma^{-1})}e^{-2T'\mu}\left(\frac{\mu}{N-1}\right)^{n'_A+n'_B} & \text{ for } 87 | \frac{T}{\gamma}\leq T' \leq T\gamma\\ 88 | 0 & \text{ otherwise} 89 | \end{array}\right. 90 | \end{align} 91 | 92 | \subsection{Acceptance probability} 93 | 94 | The acceptance probability $\alpha(x'|x)$ must be defined so that 95 | \begin{equation} 96 | \int_Wdx'\int_Vdx\,q(x'|x)\alpha(x'|x)\pi(x)=\int_Wdx'\int_Vdx\,q(x|x')\alpha(x|x')\pi(x') 97 | \end{equation} 98 | Therefore we choose 99 | \begin{equation} 100 | \alpha(x'|x)=\min\left[1,\frac{\pi(x')q(x|x')}{\pi(x)q(x'|x)}\right] 101 | \end{equation} 102 | 103 | \section{Non-root move acceptance probability} 104 | 105 | 106 | 107 | \end{document} 108 | -------------------------------------------------------------------------------- /lib/LICENSE.jblas: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Mikio L. Braun and contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the Technische Universität Berlin nor the 17 | names of its contributors may be used to endorse or promote 18 | products derived from this software without specific prior 19 | written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /lib/guava-15.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/lib/guava-15.0.jar -------------------------------------------------------------------------------- /lib/jblas-1.2.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgvaughan/MultiTypeTree/2106aefdb9de9f0d60bc3d1411a29b43a522cd21/lib/jblas-1.2.5.jar -------------------------------------------------------------------------------- /src/multitypetree/app/beauti/InitMigrationModelConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Tim Vaughan (tgvaughan@gmail.com) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.app.beauti; 18 | 19 | import beastfx.app.inputeditor.BeautiDoc; 20 | import beast.base.core.BEASTInterface; 21 | import beast.base.inference.parameter.Parameter; 22 | import beast.base.inference.parameter.RealParameter; 23 | import beast.base.evolution.likelihood.TreeLikelihood; 24 | import beast.base.evolution.tree.TraitSet; 25 | import multitypetree.evolution.tree.SCMigrationModel; 26 | import multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree; 27 | 28 | import java.util.*; 29 | 30 | 31 | 32 | /** 33 | * Class containing a static method used as a "custom connector" in the 34 | * MultiTypeTree BEAUti template. This connector ensures that the 35 | * simulation used to produce the initial tree uses a migration model 36 | * containing the same rates and population sizes as the one specified 37 | * in the tree prior. 38 | * 39 | * @author Tim Vaughan (tgvaughan@gmail.com) 40 | */ 41 | public class InitMigrationModelConnector { 42 | 43 | public static List uniqueTraitsInData(StructuredCoalescentMultiTypeTree scTree) { 44 | SortedSet uniqueTypes = new TreeSet<>(); 45 | TraitSet typeTraitSet = scTree.typeTraitInput.get(); 46 | for (String taxonName : typeTraitSet.taxaInput.get().getTaxaNames()) 47 | uniqueTypes.add(typeTraitSet.getStringValue(taxonName)); 48 | 49 | return new ArrayList<>(uniqueTypes); 50 | } 51 | 52 | public static boolean customConnector(BeautiDoc doc) { 53 | 54 | for (BEASTInterface p : doc.getPartitions("Tree")) { 55 | TreeLikelihood treeLikelihood = (TreeLikelihood) p; 56 | StructuredCoalescentMultiTypeTree tree = 57 | (StructuredCoalescentMultiTypeTree) treeLikelihood.treeInput.get(); 58 | 59 | String pID = BeautiDoc.parsePartition(tree.getID()); 60 | 61 | SCMigrationModel migModel = (SCMigrationModel)doc.pluginmap.get( 62 | "migModel.t:" + pID); 63 | 64 | SCMigrationModel migModelInit = (SCMigrationModel)doc.pluginmap.get( 65 | "migModelInit.t:" + pID); 66 | 67 | String rateMatrixStr = getParameterString((RealParameter)migModel.rateMatrixInput.get()); 68 | String popSizesStr = getParameterString((RealParameter)migModel.popSizesInput.get()); 69 | 70 | // Ensure model has appropriate number of demes 71 | 72 | int uniqueTraitCount = uniqueTraitsInData(tree).size(); 73 | StringBuilder rateMatrixStrBuilder = new StringBuilder(); 74 | StringBuilder popSizesStrBuilder = new StringBuilder(); 75 | 76 | migModel.getTypeSet().initAndValidate(); 77 | 78 | if (migModel.popSizesInput.get().getDimension() != migModel.getNTypes()) { 79 | for (int i=0; i) param.valuesInput.get()) { 133 | if (str.length()>0) 134 | str += " "; 135 | str += value; 136 | } 137 | 138 | return str; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/multitypetree/distributions/ExcludablePrior.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.distributions; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.core.Input.Validate; 22 | import beast.base.core.Function; 23 | import beast.base.inference.parameter.BooleanParameter; 24 | import beast.base.inference.parameter.IntegerParameter; 25 | import beast.base.inference.parameter.RealParameter; 26 | import beast.base.inference.distribution.Prior; 27 | 28 | /** 29 | * @author Tim Vaughan 30 | */ 31 | @Description("Just as with Prior, produces log probability of the parameter x. " 32 | + "This variant however allows one to explicitly exclude individual " 33 | + "elements of multidimensional parameters from the result.") 34 | public class ExcludablePrior extends Prior { 35 | 36 | public Input xIncludeInput = new Input( 37 | "xInclude", "Array of true/false values specifying which elements" 38 | + " of x to include", Validate.REQUIRED); 39 | 40 | @Override 41 | public void initAndValidate() { 42 | super.initAndValidate(); 43 | 44 | Function x = m_x.get(); 45 | if (x instanceof RealParameter || x instanceof IntegerParameter) { 46 | if (x.getDimension() != xIncludeInput.get().getDimension()) 47 | throw new IllegalArgumentException("Length of xInclude does " 48 | + "not match length of x."); 49 | } 50 | } 51 | 52 | @Override 53 | public double calculateLogP() { 54 | Function x = m_x.get(); 55 | if (x instanceof RealParameter || x instanceof IntegerParameter) { 56 | // test that parameter is inside its bounds 57 | double l = 0.0; 58 | double h = 0.0; 59 | if (x instanceof RealParameter) { 60 | l = ((RealParameter) x).getLower(); 61 | h = ((RealParameter) x).getUpper(); 62 | } else { 63 | l = ((IntegerParameter) x).getLower(); 64 | h = ((IntegerParameter) x).getUpper(); 65 | } 66 | for (int i = 0; i < x.getDimension(); i++) { 67 | if (!xIncludeInput.get().getValue(i)) 68 | continue; 69 | double value = x.getArrayValue(i); 70 | if (value < l || value > h) { 71 | return Double.NEGATIVE_INFINITY; 72 | } 73 | } 74 | } 75 | 76 | // Inline modified version of ParametricDistribution.calcLogP() 77 | 78 | final double fOffset = distInput.get().offsetInput.get(); 79 | logP = 0; 80 | for (int i = 0; i < x.getDimension(); i++) { 81 | if (!xIncludeInput.get().getValue(i)) 82 | continue; 83 | final double fX = x.getArrayValue(i) - fOffset; 84 | logP += distInput.get().logDensity(fX); 85 | } 86 | 87 | return logP; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/multitypetree/distributions/MRCATypePrior.java: -------------------------------------------------------------------------------- 1 | package multitypetree.distributions; 2 | 3 | import beast.base.core.Input; 4 | import beast.base.inference.parameter.RealParameter; 5 | import beast.base.evolution.alignment.TaxonSet; 6 | import beast.base.evolution.tree.TreeUtils; 7 | import multitypetree.evolution.tree.MigrationModel; 8 | import multitypetree.evolution.tree.MultiTypeNode; 9 | 10 | /** 11 | * @author Tim Vaughan 12 | */ 13 | public class MRCATypePrior extends MultiTypeTreeDistribution { 14 | 15 | public Input taxonSetInput = new Input<>("taxonSet", 16 | "Set of taxa for which prior information is available.", 17 | Input.Validate.REQUIRED); 18 | 19 | public Input typeProbsInput = new Input<>("typeProbs", 20 | "Parameter specifying individual type probabilities."); 21 | 22 | public Input typeNameInput = new Input<>("typeName", 23 | "Name of type MRCA is constrained to be."); 24 | 25 | public Input typeInput = new Input<>("type", 26 | "Index of type MRCA is constrained to be."); 27 | 28 | public Input migrationModelInput = new Input<>("migrationModel", 29 | "Migration model"); 30 | 31 | protected int type; 32 | 33 | @Override 34 | public void initAndValidate() { 35 | super.initAndValidate(); 36 | if (typeProbsInput.get() != null 37 | && typeProbsInput.get().getDimension() != migrationModelInput.get().getNTypes()) { 38 | throw new IllegalArgumentException("Dimension of type probability" + 39 | " parameter must match number of types."); 40 | } else { 41 | if (typeNameInput.get() != null) { 42 | if (!mtTree.getTypeSet().getTypesAsList().contains(typeNameInput.get())) 43 | throw new IllegalArgumentException("Type set does not contain" + 44 | " type '" + typeNameInput.get() + "'."); 45 | else 46 | type = mtTree.getTypeSet().getTypeIndex(typeNameInput.get()); 47 | } else { 48 | if (typeInput.get() == null) 49 | throw new IllegalArgumentException("Must specify typeProbs, " + 50 | "typeName or type inputs to MRCATypePrior."); 51 | 52 | if (typeInput.get()<0 || typeInput.get()>=migrationModelInput.get().getNTypes()) 53 | throw new IllegalArgumentException("Invalid type index " + 54 | "specified for type input of MRCATypePrior."); 55 | 56 | type = typeInput.get(); 57 | } 58 | } 59 | } 60 | 61 | @Override 62 | public double calculateLogP() { 63 | 64 | MultiTypeNode mrca = (MultiTypeNode)TreeUtils.getCommonAncestorNode( 65 | mtTree, taxonSetInput.get().getTaxaNames()); 66 | 67 | if (typeProbsInput.get() != null) 68 | logP = Math.log(typeProbsInput.get().getValue(mrca.getNodeType())); 69 | else 70 | logP = mrca.getNodeType() == type ? 0.0 : Double.NEGATIVE_INFINITY; 71 | 72 | return logP; 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/multitypetree/distributions/MultiTypeTreeDistribution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.distributions; 18 | 19 | import beast.base.core.Input; 20 | import beast.base.core.Input.Validate; 21 | import beast.base.inference.Distribution; 22 | import beast.base.inference.State; 23 | import multitypetree.evolution.tree.MultiTypeTree; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | import java.util.Random; 28 | 29 | /** 30 | * 31 | * @author Tim Vaughan 32 | */ 33 | public abstract class MultiTypeTreeDistribution extends Distribution { 34 | 35 | public Input mtTreeInput = new Input<>("multiTypeTree", 36 | "Multi-type tree.", Validate.REQUIRED); 37 | 38 | protected MultiTypeTree mtTree; 39 | 40 | @Override 41 | public void initAndValidate() { 42 | mtTree = mtTreeInput.get(); 43 | } 44 | 45 | // Interface requirements: 46 | 47 | 48 | @Override 49 | public List getArguments() { 50 | return null; 51 | } 52 | 53 | @Override 54 | public List getConditions() { 55 | return null; 56 | } 57 | 58 | @Override 59 | public void sample(State state, Random random) { 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/multitypetree/distributions/PriorWithPole.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.distributions; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.core.Input.Validate; 22 | import beast.base.inference.parameter.RealParameter; 23 | import beast.base.inference.distribution.Prior; 24 | 25 | /** 26 | * @author Tim Vaughan 27 | */ 28 | @Description("Use when a RealParameter prior has a pole at zero.") 29 | public class PriorWithPole extends Prior { 30 | 31 | public Input p0Input = new Input("p0", "Probability " 32 | + "with which each element takes the value 0.0", 33 | Validate.REQUIRED); 34 | 35 | RealParameter x; 36 | double p0; 37 | 38 | @Override 39 | public void initAndValidate() { 40 | 41 | if (!(m_x.get() instanceof RealParameter)) 42 | throw new RuntimeException("PriorWithPole only applies to" 43 | + " RealParameters."); 44 | 45 | x = (RealParameter)m_x.get(); 46 | p0 = p0Input.get(); 47 | 48 | super.initAndValidate(); 49 | } 50 | 51 | @Override 52 | public double calculateLogP() { 53 | double l = x.getLower(); 54 | double h = x.getUpper(); 55 | 56 | for (int i=0; ih) 59 | return Double.NEGATIVE_INFINITY; 60 | } 61 | 62 | final double offset = distInput.get().offsetInput.get(); 63 | logP = 0.0; 64 | for (int i=0; i0) { 67 | value -= offset; 68 | logP += Math.log(1.0-p0) + distInput.get().logDensity(value); 69 | } else { 70 | logP += Math.log(p0); 71 | } 72 | } 73 | 74 | return logP; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/multitypetree/distributions/TypeChangeTimeCondition.java: -------------------------------------------------------------------------------- 1 | package multitypetree.distributions; 2 | 3 | import beast.base.core.Description; 4 | import beast.base.core.Input; 5 | import beast.base.inference.parameter.IntegerParameter; 6 | import beast.base.inference.parameter.RealParameter; 7 | import beast.base.evolution.tree.Node; 8 | import multitypetree.evolution.tree.MultiTypeNode; 9 | 10 | import java.util.Arrays; 11 | import java.util.HashSet; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | /** 16 | * @author Tim Vaughan 17 | */ 18 | @Description("Conditions AGAINST specific type changes in given region.") 19 | public class TypeChangeTimeCondition extends MultiTypeTreeDistribution { 20 | 21 | public Input h1Input = new Input<>( 22 | "h1", 23 | "Smaller height in height range.", 24 | new RealParameter(new Double[] {0.0})); 25 | 26 | public Input h2Input = new Input<>( 27 | "h2", 28 | "Larger height in height range.", 29 | new RealParameter(new Double[] {Double.POSITIVE_INFINITY})); 30 | 31 | public Input fromTypesInput = new Input<>( 32 | "fromTypes", 33 | "Specify type changes FROM these types."); 34 | 35 | public Input fromTypeNamesInput = new Input<>( 36 | "fromTypeNames", 37 | "Specify comma-delimited list of FROM type names.", 38 | Input.Validate.XOR, fromTypesInput); 39 | 40 | public Input toTypesInput = new Input<>( 41 | "toTypes", 42 | "Specify type changes TO these types.", 43 | Input.Validate.REQUIRED); 44 | 45 | public Input toTypeNamesInput = new Input<>( 46 | "toTypeNames", 47 | "Specify comma-delimited list of FROM type names.", 48 | Input.Validate.XOR, toTypesInput); 49 | 50 | protected Set fromTypes, toTypes; 51 | protected boolean needsUpdate; 52 | 53 | protected RealParameter h1, h2; 54 | 55 | @Override 56 | public void initAndValidate() { 57 | super.initAndValidate(); 58 | 59 | h1 = h1Input.get(); 60 | h2 = h2Input.get(); 61 | 62 | fromTypes = new HashSet<>(); 63 | toTypes = new HashSet<>(); 64 | 65 | needsUpdate = true; 66 | } 67 | 68 | @Override 69 | public double calculateLogP() { 70 | update(); 71 | 72 | logP = 0.0; 73 | 74 | for (Node node : mtTree.getNodesAsArray()) { 75 | if (node.isRoot()) 76 | continue; 77 | 78 | if (node.getHeight()>h2.getValue() 79 | || node.getParent().getHeight()h1.getValue() 87 | && mtNode.getChangeTime(i). 16 | */ 17 | package multitypetree.evolution.tree; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Tree; 22 | 23 | /** 24 | * A plugin useful for interpreting ColouredTree objects as standard BEAST 25 | * tree objects, on which single-child nodes are used to represent each 26 | * colour change. 27 | * 28 | * @author Tim Vaughan 29 | */ 30 | @Description("A standard BEAST tree representation of a TreeColour object.") 31 | public class FlatMultiTypeTree extends Tree { 32 | 33 | public Input multiTypeTreeInput = new Input( 34 | "multiType", "Multi-type tree to flatten."); 35 | 36 | protected MultiTypeTree multiTypeTree; 37 | 38 | public FlatMultiTypeTree() {}; 39 | 40 | @Override 41 | public void initAndValidate() { 42 | multiTypeTree = multiTypeTreeInput.get(); 43 | setRoot(multiTypeTree.getFlattenedTree(false).getRoot()); 44 | initArrays(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/multitypetree/evolution/tree/MigrationModel.java: -------------------------------------------------------------------------------- 1 | package multitypetree.evolution.tree; 2 | 3 | import beast.base.core.BEASTInterface; 4 | import org.jblas.DoubleMatrix; 5 | 6 | /** 7 | * @author Tim Vaughan 8 | */ 9 | public interface MigrationModel extends BEASTInterface { 10 | int getNTypes(); 11 | 12 | TypeSet getTypeSet(); 13 | 14 | double getBackwardRate(int i, int j); 15 | 16 | double getForwardRate(int i, int j); 17 | 18 | double getMu(boolean symmetric); 19 | 20 | DoubleMatrix getR(boolean symmetric); 21 | 22 | DoubleMatrix getQ(boolean symmetric); 23 | 24 | DoubleMatrix getRpowN(int n, boolean symmetric); 25 | 26 | int RpowSteadyN(boolean symmetric); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/multitypetree/evolution/tree/MultiTypeTreeFromFlatTree.java: -------------------------------------------------------------------------------- 1 | package multitypetree.evolution.tree; 2 | 3 | import beast.base.core.Description; 4 | import beast.base.core.Input; 5 | import beast.base.evolution.tree.Tree; 6 | 7 | /** 8 | * @author Tim Vaughan 9 | */ 10 | @Description("Class to initialise a MultiTypeTree from a " + 11 | "BEAST tree in which single-child nodes represent " + 12 | "type changes.") 13 | public class MultiTypeTreeFromFlatTree extends MultiTypeTree { 14 | 15 | public Input flatTreeInput = new Input<>( 16 | "flatTree", 17 | "Flat representation of multi-type tree.", 18 | Input.Validate.REQUIRED); 19 | 20 | @Override 21 | public void initAndValidate() { 22 | super.initAndValidate(); 23 | 24 | initFromFlatTree(flatTreeInput.get(), true); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/multitypetree/evolution/tree/MultiTypeTreeFromNewick.java: -------------------------------------------------------------------------------- 1 | package multitypetree.evolution.tree; 2 | 3 | 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import beast.base.core.Description; 9 | import beast.base.core.Input; 10 | import beast.base.core.Input.Validate; 11 | import beast.base.evolution.tree.TreeParser; 12 | import beast.base.inference.StateNode; 13 | import beast.base.inference.StateNodeInitialiser; 14 | 15 | /** 16 | * @author dkuh004 17 | * Date: Jun 18, 2012 18 | * Time: 3:03:07 PM 19 | */ 20 | 21 | @Description("Class to initialize a MultiTypeTree from single child newick tree with type metadata") 22 | public class MultiTypeTreeFromNewick extends MultiTypeTree implements StateNodeInitialiser { 23 | 24 | public Input newickStringInput = new Input<>("value", 25 | "Tree in Newick format.", Validate.REQUIRED); 26 | 27 | public Input adjustTipHeightsInput = new Input<>("adjustTipHeights", 28 | "Adjust tip heights in tree? Default false.", false); 29 | 30 | @Override 31 | public void initAndValidate() { 32 | 33 | super.initAndValidate(); 34 | 35 | TreeParser parser = new TreeParser(); 36 | parser.initByName( 37 | "IsLabelledNewick", true, 38 | "adjustTipHeights", adjustTipHeightsInput.get(), 39 | "singlechild", true, 40 | "newick", newickStringInput.get()); 41 | 42 | initFromFlatTree(parser, true); 43 | } 44 | 45 | @Override 46 | public void initStateNodes() { } 47 | 48 | 49 | @Override 50 | public void getInitialisedStateNodes(List stateNodes) { 51 | stateNodes.add(this); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/multitypetree/evolution/tree/RandomMultiTypeTree.java: -------------------------------------------------------------------------------- 1 | package multitypetree.evolution.tree; 2 | 3 | import beast.base.core.Description; 4 | import beast.base.evolution.tree.Node; 5 | import beast.base.inference.StateNode; 6 | import beast.base.inference.StateNodeInitialiser; 7 | import beast.base.util.Randomizer; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * @author dkuh004 13 | * Date: Jul 16, 2012 14 | * Time: 4:22:40 PM 15 | * 16 | * May not be very useful in its current form. Need a full implementation of 17 | * RandomTree for multi-type trees. 18 | */ 19 | @Description("Class to initialize a MultiTypeTree from random tree by adding minimum number of changes needed") 20 | public class RandomMultiTypeTree extends MultiTypeTree implements StateNodeInitialiser { 21 | 22 | @Override 23 | public void initAndValidate() { 24 | super.initAndValidate(); 25 | 26 | if (!hasTypeTrait()) 27 | throw new IllegalArgumentException("No trait set with name '" + typeLabel + "' " 28 | + "identified. Needed to specify taxon locations."); 29 | 30 | // Fill leaf colour array: 31 | for (int i = 0; i= 3){ 54 | Node left = node.getChild(0); 55 | Node right = node.getChild(1); 56 | 57 | if (left.getNodeCount() >= 3) generateTyping(left); 58 | if (right.getNodeCount() >= 3) generateTyping(right); 59 | 60 | int leftCol = ((MultiTypeNode)left).getFinalType(); 61 | int rightCol = ((MultiTypeNode)right).getFinalType(); 62 | 63 | if (leftCol == rightCol) 64 | ((MultiTypeNode)node).setNodeType(leftCol); 65 | 66 | else { 67 | 68 | Node nodeToKeep, other; 69 | if (Randomizer.nextBoolean()) { 70 | nodeToKeep = left; 71 | other = right; 72 | } else { 73 | nodeToKeep = right; 74 | other = left; 75 | } 76 | 77 | ((MultiTypeNode)node).setNodeType(((MultiTypeNode)nodeToKeep).getNodeType()); 78 | 79 | double changeTime = Randomizer.nextDouble()*(node.getHeight()-other.getHeight()) + other.getHeight(); 80 | ((MultiTypeNode)other).addChange(((MultiTypeNode)node).getNodeType(), changeTime); 81 | } 82 | } 83 | } 84 | 85 | 86 | 87 | @Override 88 | public void getInitialisedStateNodes(List stateNodes) { 89 | stateNodes.add(this); 90 | } 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/multitypetree/evolution/tree/TypeSet.java: -------------------------------------------------------------------------------- 1 | package multitypetree.evolution.tree; 2 | 3 | import beast.base.core.BEASTObject; 4 | import beast.base.core.Input; 5 | import beast.base.evolution.tree.TraitSet; 6 | 7 | import java.util.*; 8 | 9 | /** 10 | * Ordered set of type names. 11 | * 12 | * @author Tim Vaughan 13 | */ 14 | public class TypeSet extends BEASTObject { 15 | 16 | public Input valueInput = new Input<>("value", "Comma-delmited list of types."); 17 | public Input typeTraitSetInput = new Input<>("typeTraitSet", "Type trait set defining list of types."); 18 | 19 | protected SortedSet typeNameSet = new TreeSet<>(); 20 | 21 | public TypeSet() {} 22 | 23 | /** 24 | * Constructor to produce type set containing types provided as arguments. 25 | * Useful for testing. 26 | * 27 | * @param typeNames varargs array of type names to include 28 | */ 29 | public TypeSet(String ... typeNames) { 30 | typeNameSet.addAll(Arrays.asList(typeNames)); 31 | } 32 | 33 | @Override 34 | public void initAndValidate() { 35 | typeNameSet.clear(); 36 | 37 | if (valueInput.get() != null) { 38 | for (String typeName : valueInput.get().split(",")) 39 | if (!typeName.isEmpty()) 40 | typeNameSet.add(typeName); 41 | } 42 | 43 | if (typeTraitSetInput.get() != null) 44 | addTypesFromTypeTraitSet(typeTraitSetInput.get()); 45 | } 46 | 47 | /** 48 | * Incorporates all of the traits present in the given trait set into the type set. 49 | * 50 | * @param typeTraitSet 51 | */ 52 | public void addTypesFromTypeTraitSet(TraitSet typeTraitSet) { 53 | typeNameSet.addAll(Arrays.asList(typeTraitSet.getTaxonValues())); 54 | } 55 | 56 | /** 57 | * @return the number of unique types defined in this type set 58 | */ 59 | public int getNTypes() { 60 | return Math.max(typeNameSet.size(), 2); 61 | } 62 | 63 | /** 64 | * @param typeName name of type 65 | * @return numerical index representing type 66 | */ 67 | public int getTypeIndex(String typeName) { 68 | if (typeNameSet.contains(typeName)) 69 | return (new ArrayList<>(typeNameSet).indexOf(typeName)); 70 | else 71 | throw new IllegalArgumentException("TypeSet does not contain type with name " + typeName); 72 | } 73 | 74 | /** 75 | * @param typeIdx numerical index representing type 76 | * @return name of type 77 | */ 78 | public String getTypeName(int typeIdx) { 79 | if (typeIdx(typeNameSet).get(typeIdx)); 81 | else 82 | return "type_" + typeIdx; 83 | } 84 | 85 | /** 86 | * @param typeName name of type 87 | * @return true iff this TypeSet contains a type with name typeName. 88 | */ 89 | public boolean containsTypeWithName(String typeName) { 90 | return typeNameSet.contains(typeName); 91 | } 92 | 93 | /** 94 | * @return list of type names ordered according to type index 95 | */ 96 | public List getTypesAsList() { 97 | return new ArrayList<>(typeNameSet); 98 | } 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/multitypetree/operators/MultiTypeUniform.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | 20 | import beast.base.core.Description; 21 | import beast.base.core.Input; 22 | import beast.base.evolution.tree.Node; 23 | import beast.base.util.Randomizer; 24 | import multitypetree.evolution.tree.MultiTypeNode; 25 | 26 | /** 27 | * @author Denise Kuhnert 28 | */ 29 | @Description("Randomly selects true internal tree node (i.e. not the root) and" 30 | + " moves node height uniformly in interval restricted by the node's" 31 | + " parent and children.") 32 | public class MultiTypeUniform extends MultiTypeTreeOperator { 33 | 34 | public Input includeRootInput = new Input<>("includeRoot", 35 | "Allow modification of root node.", false); 36 | 37 | public Input rootScaleFactorInput = new Input<>("rootScaleFactor", 38 | "Root scale factor.", 0.9); 39 | 40 | /** 41 | * Change the node height and return the hastings ratio. 42 | * 43 | * @return log of Hastings Ratio 44 | */ 45 | @Override 46 | public double proposal() { 47 | // Randomly select event on tree: 48 | int event = Randomizer.nextInt(mtTree.getInternalNodeCount() + mtTree.getTotalNumberOfChanges()); 49 | 50 | 51 | MultiTypeNode node = null; 52 | int changeIdx = -1; 53 | if (event0 94 | ? node.getChangeTime(0) 95 | : node.getParent().getHeight(); 96 | 97 | double u = Randomizer.nextDouble(); 98 | double tnew = u*tmin + (1.0-u)*tmax; 99 | 100 | node.setHeight(tnew); 101 | return 0.0; 102 | } 103 | } else { 104 | double tmin, tmax; 105 | if (changeIdx+1 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.evolution.tree.Node; 21 | import beast.base.util.Randomizer; 22 | import multitypetree.evolution.tree.MultiTypeNode; 23 | 24 | /** 25 | * @author Tim Vaughan 26 | */ 27 | @Description("Retypes a randomly chosen node and its attached branches. " 28 | + "This variant uses the uniformization branch retyping procedure.") 29 | public class NodeRetype extends UniformizationRetypeOperator { 30 | 31 | @Override 32 | public double proposal() { 33 | double logHR = 0.0; 34 | 35 | // Select node: 36 | Node node = mtTree.getNode(mtTree.getLeafNodeCount() 37 | + Randomizer.nextInt(mtTree.getInternalNodeCount())); 38 | 39 | // Record probability of current types along attached branches: 40 | if (!node.isRoot()) 41 | logHR += getBranchTypeProb(node); 42 | 43 | logHR += getBranchTypeProb(node.getLeft()) 44 | + getBranchTypeProb(node.getRight()); 45 | 46 | // Select new node type: 47 | ((MultiTypeNode)node).setNodeType( 48 | Randomizer.nextInt(migModel.getNTypes())); 49 | 50 | // Retype attached branches: 51 | try { 52 | if (!node.isRoot()) 53 | logHR -= retypeBranch(node); 54 | 55 | logHR -= retypeBranch(node.getLeft()) 56 | + retypeBranch(node.getRight()); 57 | } catch (NoValidPathException e) { 58 | return Double.NEGATIVE_INFINITY; 59 | } 60 | 61 | 62 | return logHR; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/multitypetree/operators/NodeRetypeRandom.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.evolution.tree.Node; 21 | import beast.base.evolution.tree.Tree; 22 | import beast.base.util.Randomizer; 23 | import multitypetree.evolution.tree.MultiTypeNode; 24 | 25 | /** 26 | * @author Tim Vaughan 27 | */ 28 | @Description("Retypes a randomly chosen node and its attached branches. " 29 | + "This variant uses an unconditioned random walk for branch retyping.") 30 | public class NodeRetypeRandom extends RandomRetypeOperator { 31 | 32 | @Override 33 | public double proposal() { 34 | double logHR = 0.0; 35 | 36 | // Select node: 37 | Node node; 38 | do { 39 | node = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 40 | } while (node.isLeaf()); 41 | 42 | // Record probability of current types along attached branches: 43 | if (!node.isRoot()) 44 | logHR += getBranchTypeProb(node); 45 | 46 | logHR += getBranchTypeProb(node.getLeft()) 47 | + getBranchTypeProb(node.getRight()); 48 | 49 | // Select new node type: 50 | ((MultiTypeNode)node).setNodeType(Randomizer.nextInt(migModel.getNTypes())); 51 | 52 | // Retype attached branches, forcing reject if inconsistent: 53 | if (!node.isRoot()) { 54 | logHR -= retypeBranch(node); 55 | 56 | if (((MultiTypeNode)node).getFinalType() != ((MultiTypeNode)node.getParent()).getNodeType()) 57 | return Double.NEGATIVE_INFINITY; 58 | } 59 | 60 | logHR -= retypeBranch(node.getLeft()) 61 | + retypeBranch(node.getRight()); 62 | 63 | if ((((MultiTypeNode)node.getLeft()).getFinalType() != ((MultiTypeNode)node).getNodeType()) 64 | || (((MultiTypeNode)node.getRight()).getFinalType() != ((MultiTypeNode)node).getNodeType())) 65 | return Double.NEGATIVE_INFINITY; 66 | 67 | // WHY IS THIS NECESSARY!? 68 | node.makeDirty(Tree.IS_DIRTY); 69 | node.getLeft().makeDirty(Tree.IS_DIRTY); 70 | node.getRight().makeDirty(Tree.IS_DIRTY); 71 | 72 | return logHR; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/multitypetree/operators/NodeShiftRetype.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Node; 22 | import beast.base.util.Randomizer; 23 | import multitypetree.evolution.tree.MultiTypeNode; 24 | 25 | /** 26 | * @author Tim Vaughan 27 | */ 28 | @Description("Operator implementing a very similar move to MultiTypeUniform, " 29 | + " with the difference that this operator draws new node positions" 30 | + " uniformly between the parent and oldest child heights and" 31 | + " retypes the modified branches. Additionally, this operator" 32 | + " can act on the root node.") 33 | public class NodeShiftRetype extends UniformizationRetypeOperator { 34 | 35 | public Input rootOnlyInput = new Input<>("rootOnly", 36 | "Always select root node for height adjustment.", false); 37 | 38 | public Input noRootInput = new Input<>("noRoot", 39 | "Never select root node for height adjustment.", false); 40 | 41 | public Input rootScaleFactorInput = new Input<>("rootScaleFactor", 42 | "Scale factor used in root height proposals. (Default 0.8)", 0.8); 43 | 44 | @Override 45 | public void initAndValidate() { 46 | super.initAndValidate(); 47 | 48 | if (rootOnlyInput.get() && noRootInput.get()) 49 | throw new IllegalArgumentException("rootOnly and noRoot inputs " 50 | + "cannot both be set to true simultaneously."); 51 | } 52 | 53 | @Override 54 | public double proposal() { 55 | // Select internal node to adjust: 56 | Node node; 57 | if (rootOnlyInput.get()) 58 | node = mtTree.getRoot(); 59 | else 60 | do { 61 | node = mtTree.getNode(mtTree.getLeafNodeCount() 62 | + Randomizer.nextInt(mtTree.getInternalNodeCount())); 63 | } while (noRootInput.get() && node.isRoot()); 64 | 65 | 66 | // Generate relevant proposal: 67 | if (node.isRoot()) 68 | return rootProposal(node); 69 | else 70 | return nonRootProposal(node); 71 | } 72 | 73 | /** 74 | * Root node proposal. 75 | * @param root 76 | * @return log of HR 77 | */ 78 | private double rootProposal(Node root) { 79 | 80 | double logHR = 0.0; 81 | 82 | // Record probability of current typing: 83 | logHR += getBranchTypeProb(root.getLeft()) 84 | + getBranchTypeProb(root.getRight()); 85 | 86 | // Select new root height: 87 | double u = Randomizer.nextDouble(); 88 | double f = u*rootScaleFactorInput.get() + (1-u)/rootScaleFactorInput.get(); 89 | double oldestChildHeight = Math.max( 90 | root.getLeft().getHeight(), 91 | root.getRight().getHeight()); 92 | root.setHeight(oldestChildHeight + f*(root.getHeight()-oldestChildHeight)); 93 | logHR -= Math.log(f); 94 | 95 | // Select new root node type: 96 | ((MultiTypeNode)root).setNodeType(Randomizer.nextInt(migModel.getNTypes())); 97 | 98 | // Recolour branches below root: 99 | try { 100 | logHR -= retypeBranch(root.getLeft()) 101 | + retypeBranch(root.getRight()); 102 | } catch (NoValidPathException e) { 103 | return Double.NEGATIVE_INFINITY; 104 | } 105 | 106 | return logHR; 107 | } 108 | 109 | /** 110 | * Non-root internal node proposal. 111 | * @param node 112 | * @return log of HR of move 113 | */ 114 | private double nonRootProposal(Node node) { 115 | 116 | double logHR = 0.0; 117 | 118 | // Record probability of current colouring: 119 | logHR += getBranchTypeProb(node) 120 | + getBranchTypeProb(node.getLeft()) 121 | + getBranchTypeProb(node.getRight()); 122 | 123 | // Select new node height: 124 | double upperBound = node.getParent().getHeight(); 125 | double lowerBound = Math.max( 126 | node.getLeft().getHeight(), 127 | node.getRight().getHeight()); 128 | node.setHeight(lowerBound+(upperBound-lowerBound)*Randomizer.nextDouble()); 129 | 130 | // Select new node colour: 131 | ((MultiTypeNode)node).setNodeType(Randomizer.nextInt(migModel.getNTypes())); 132 | 133 | // Recolour branches connected to node: 134 | try { 135 | logHR -= retypeBranch(node) 136 | + retypeBranch(node.getLeft()) 137 | + retypeBranch(node.getRight()); 138 | } catch (NoValidPathException e) { 139 | return Double.NEGATIVE_INFINITY; 140 | } 141 | 142 | return logHR; 143 | } 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/multitypetree/operators/NodeShiftRetypeRandom.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Node; 22 | import beast.base.util.Randomizer; 23 | import multitypetree.evolution.tree.MultiTypeNode; 24 | 25 | /** 26 | * @author Tim Vaughan 27 | */ 28 | @Description("Operator implementing a very similar move to MultiTypeUniform, " 29 | + " with the difference that this operator draws new node positions" 30 | + " uniformly between the parent and oldest child heights and" 31 | + " retypes the modified branches. Additionally, this operator" 32 | + " can act on the root node.") 33 | public class NodeShiftRetypeRandom extends RandomRetypeOperator { 34 | 35 | public Input rootOnlyInput = new Input<>("rootOnly", 36 | "Always select root node for height adjustment.", false); 37 | 38 | public Input noRootInput = new Input<>("noRoot", 39 | "Never select root node for height adjustment.", false); 40 | 41 | public Input rootScaleFactorInput = new Input<>("rootScaleFactor", 42 | "Scale factor used in root height proposals. (Default 0.8)", 0.8); 43 | 44 | @Override 45 | public void initAndValidate() { 46 | super.initAndValidate(); 47 | 48 | if (rootOnlyInput.get() && noRootInput.get()) 49 | throw new IllegalArgumentException("rootOnly and noRoot inputs " 50 | + "cannot both be set to true simultaneously."); 51 | } 52 | 53 | @Override 54 | public double proposal() { 55 | // Select internal node to adjust: 56 | Node node; 57 | if (rootOnlyInput.get()) 58 | node = mtTree.getRoot(); 59 | else 60 | do { 61 | node = mtTree.getNode(mtTree.getLeafNodeCount() 62 | + Randomizer.nextInt(mtTree.getInternalNodeCount())); 63 | } while (noRootInput.get() && node.isRoot()); 64 | 65 | 66 | // Generate relevant proposal: 67 | if (node.isRoot()) 68 | return rootProposal(node); 69 | else 70 | return nonRootProposal(node); 71 | } 72 | 73 | /** 74 | * Root node proposal. 75 | * @param root 76 | * @return log of HR 77 | */ 78 | private double rootProposal(Node root) { 79 | 80 | double logHR = 0.0; 81 | 82 | // Record probability of current colouring: 83 | logHR += getBranchTypeProb(root.getLeft()) 84 | + getBranchTypeProb(root.getRight()); 85 | 86 | // Select new root height: 87 | double u = Randomizer.nextDouble(); 88 | double f = u*rootScaleFactorInput.get() + (1-u)/rootScaleFactorInput.get(); 89 | double oldestChildHeight = Math.max( 90 | root.getLeft().getHeight(), 91 | root.getRight().getHeight()); 92 | root.setHeight(oldestChildHeight + f*(root.getHeight()-oldestChildHeight)); 93 | logHR -= Math.log(f); 94 | 95 | // Select new root node colour: 96 | ((MultiTypeNode)root).setNodeType(Randomizer.nextInt(migModel.getNTypes())); 97 | 98 | // Recolour branches below root: 99 | logHR -= retypeBranch(root.getLeft()) 100 | + retypeBranch(root.getRight()); 101 | 102 | // Reject if new colouring inconsistent: 103 | if ((((MultiTypeNode)root.getLeft()).getFinalType() != ((MultiTypeNode)root).getNodeType()) 104 | || (((MultiTypeNode)root.getRight()).getFinalType() != ((MultiTypeNode)root).getNodeType())) 105 | return Double.NEGATIVE_INFINITY; 106 | 107 | return logHR; 108 | } 109 | 110 | /** 111 | * Non-root internal node proposal. 112 | * @param node 113 | * @return log of HR of move 114 | */ 115 | private double nonRootProposal(Node node) { 116 | 117 | double logHR = 0.0; 118 | 119 | // Record probability of current colouring: 120 | logHR += getBranchTypeProb(node) 121 | + getBranchTypeProb(node.getLeft()) 122 | + getBranchTypeProb(node.getRight()); 123 | 124 | // Select new node height: 125 | double upperBound = node.getParent().getHeight(); 126 | double lowerBound = Math.max( 127 | node.getLeft().getHeight(), 128 | node.getRight().getHeight()); 129 | node.setHeight(lowerBound+(upperBound-lowerBound)*Randomizer.nextDouble()); 130 | 131 | // Select new node colour: 132 | ((MultiTypeNode)node).setNodeType(Randomizer.nextInt(migModel.getNTypes())); 133 | 134 | // Recolour branches connected to node: 135 | logHR -= retypeBranch(node) 136 | + retypeBranch(node.getLeft()) 137 | + retypeBranch(node.getRight()); 138 | 139 | // Reject if new colouring inconsistent: 140 | if ((((MultiTypeNode)node.getLeft()).getFinalType() != ((MultiTypeNode)node).getNodeType()) 141 | || (((MultiTypeNode)node.getRight()).getFinalType() != ((MultiTypeNode)node).getNodeType()) 142 | || (((MultiTypeNode)node).getFinalType() != ((MultiTypeNode)node.getParent()).getNodeType())) 143 | return Double.NEGATIVE_INFINITY; 144 | 145 | return logHR; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/multitypetree/operators/RandomRetypeOperator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Input; 20 | import beast.base.evolution.tree.Node; 21 | import beast.base.util.Randomizer; 22 | import multitypetree.evolution.tree.MultiTypeNode; 23 | 24 | /** 25 | * Abstract class of operators on MultiTypeTrees which retype branches using a 26 | * fixed-rate continuous time random walk across demes. 27 | * 28 | * @author Tim Vaughan 29 | */ 30 | public abstract class RandomRetypeOperator extends MultiTypeTreeOperator { 31 | 32 | public Input muInput = new Input<>("mu", 33 | "Migration rate for proposal distribution", Input.Validate.REQUIRED); 34 | 35 | /** 36 | * Retype branch between srcNode and its parent with rate fixed by the 37 | * tuning parameter mu. 38 | * 39 | * @param srcNode 40 | * @return Probability of branch typing 41 | */ 42 | protected double retypeBranch(Node srcNode) { 43 | 44 | double mu = muInput.get(); 45 | 46 | Node srcNodeParent = srcNode.getParent(); 47 | double t_srcNode = srcNode.getHeight(); 48 | double t_srcNodeParent = srcNodeParent.getHeight(); 49 | 50 | int srcNodeType = ((MultiTypeNode)srcNode).getNodeType(); 51 | 52 | // Clear existing changes in preparation for adding replacements: 53 | ((MultiTypeNode)srcNode).clearChanges(); 54 | 55 | double t = t_srcNode; 56 | int lastType = srcNodeType; 57 | while (t < t_srcNodeParent) { 58 | 59 | // Determine time to next migration event: 60 | t += Randomizer.nextExponential(mu); 61 | 62 | if (t < t_srcNodeParent) { 63 | 64 | // Select new colour: 65 | int newType = Randomizer.nextInt(migModel.getNTypes() - 1); 66 | if (newType >= lastType) 67 | newType += 1; 68 | ((MultiTypeNode)srcNode).addChange(newType, t); 69 | 70 | lastType = newType; 71 | } 72 | } 73 | 74 | // Return log of branch type probability: 75 | return -mu*(t_srcNodeParent - t_srcNode) 76 | + ((MultiTypeNode)srcNode).getChangeCount()*Math.log(mu/(migModel.getNTypes()-1)); 77 | 78 | } 79 | 80 | /** 81 | * Get probability of the colouring along the branch between srcNode 82 | * and its parent. 83 | * 84 | * @param srcNode 85 | * @return Probability of the colouring. 86 | */ 87 | protected double getBranchTypeProb(Node srcNode) { 88 | 89 | double mu = muInput.get(); 90 | double T = srcNode.getParent().getHeight() 91 | - srcNode.getHeight(); 92 | int n = ((MultiTypeNode)srcNode).getChangeCount(); 93 | int N = migModel.getNTypes(); 94 | 95 | if (N == 0) 96 | return 0.0; 97 | else 98 | return -mu*T + n*Math.log(mu/(N-1)); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/multitypetree/operators/SpecialTypeBirthDeath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.util.Randomizer; 21 | import multitypetree.evolution.tree.MultiTypeNode; 22 | 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | 26 | /** 27 | * 28 | * @author Tim Vaughan 29 | */ 30 | @Description("Special move specific to two taxon trees for debugging only.") 31 | public class SpecialTypeBirthDeath extends MultiTypeTreeOperator { 32 | 33 | @Override 34 | public double proposal() { 35 | if (mtTree.getLeafNodeCount() != 2) 36 | throw new IllegalArgumentException("SpecialTypeBirthDeath only valid for 2 taxon trees."); 37 | 38 | // Cannot produce or operate on trees without any changes 39 | if (mtTree.getTotalNumberOfChanges()==0) 40 | return Double.NEGATIVE_INFINITY; 41 | 42 | //String startTree = mtTree.toString(); 43 | 44 | MultiTypeNode root = (MultiTypeNode)mtTree.getRoot(); 45 | MultiTypeNode left = (MultiTypeNode)root.getLeft(); 46 | MultiTypeNode right = (MultiTypeNode)root.getRight(); 47 | 48 | // Select event: 49 | int event = Randomizer.nextInt(mtTree.getTotalNumberOfChanges()); 50 | 51 | MultiTypeNode node, sister; 52 | int changeIdx; 53 | if (event illegalTypesBirth = new HashSet(); 117 | 118 | illegalTypesBirth.add(node.getChangeType(changeIdx)); 119 | if (changeIdx>0) 120 | illegalTypesBirth.add(node.getChangeType(changeIdx-1)); 121 | else 122 | illegalTypesBirth.add(node.getNodeType()); 123 | 124 | tmin = node.getChangeTime(changeIdx); 125 | 126 | if (changeIdx+1>=node.getChangeCount()) { 127 | tmax = node.getParent().getHeight(); 128 | } else 129 | tmax = node.getChangeTime(changeIdx+1); 130 | 131 | 132 | // Backward move probability: 133 | logHR += Math.log(1.0/(mtTree.getTotalNumberOfChanges()+1)); 134 | 135 | // Forward move probability: 136 | int Cbirth = migModel.getNTypes() - illegalTypesBirth.size(); 137 | logHR -= Math.log(1.0/(Cbirth*mtTree.getTotalNumberOfChanges()*(tmax-tmin))); 138 | 139 | // Add new event: 140 | double tnew = tmin + Randomizer.nextDouble()*(tmax-tmin); 141 | int aboveType = node.getChangeType(changeIdx); 142 | node.insertChange(changeIdx+1, aboveType, tnew); 143 | 144 | // Select and apply new type: 145 | int n = Randomizer.nextInt(Cbirth); 146 | int changeType; 147 | for (changeType = 0; changeType=node.getChangeCount()) 165 | return Double.NEGATIVE_INFINITY; 166 | 167 | double logHR = 0.0; 168 | 169 | double tmin, tmax; 170 | 171 | Set illegalTypesBirth = new HashSet(); 172 | 173 | illegalTypesBirth.add(node.getChangeType(changeIdx+1)); 174 | if (changeIdx>0) 175 | illegalTypesBirth.add(node.getChangeType(changeIdx-1)); 176 | else 177 | illegalTypesBirth.add(node.getNodeType()); 178 | 179 | tmin = node.getChangeTime(changeIdx); 180 | 181 | if (changeIdx+2>=node.getChangeCount()) 182 | tmax = node.getParent().getHeight(); 183 | else 184 | tmax = node.getChangeTime(changeIdx+2); 185 | 186 | // Backward move probability: 187 | int Cbirth = migModel.getNTypes() - illegalTypesBirth.size(); 188 | logHR += Math.log(1.0/(Cbirth*(mtTree.getTotalNumberOfChanges()-1)*(tmax-tmin))); 189 | 190 | // Forward move probability: 191 | logHR -= Math.log(1.0/mtTree.getTotalNumberOfChanges()); 192 | 193 | // Record colour above node to remove: 194 | int changeType = node.getChangeType(changeIdx+1); 195 | 196 | // Remove change: 197 | node.removeChange(changeIdx+1); 198 | 199 | // Update colouring 200 | node.setChangeType(changeIdx, changeType); 201 | 202 | return logHR; 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /src/multitypetree/operators/TypePairBirthDeath.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.evolution.tree.Node; 21 | import beast.base.util.Randomizer; 22 | import multitypetree.evolution.tree.MultiTypeNode; 23 | 24 | /** 25 | * @author Tim Vaughan 26 | */ 27 | @Description("Implements type change (migration) pair birth/death move " 28 | + "described by Ewing et al., Genetics (2004).") 29 | public class TypePairBirthDeath extends MultiTypeTreeOperator { 30 | 31 | @Override 32 | public double proposal() { 33 | int n = mtTree.getLeafNodeCount(); 34 | int m = mtTree.getTotalNumberOfChanges(); 35 | 36 | // Select sub-edge at random: 37 | int edgeNum = Randomizer.nextInt(2*n - 2 + m); 38 | 39 | // Find edge that sub-edge lies on: 40 | Node selectedNode = null; 41 | for (Node node : mtTree.getNodesAsArray()) { 42 | if (node.isRoot()) 43 | continue; 44 | 45 | if (edgeNum<((MultiTypeNode)node).getChangeCount()+1) { 46 | selectedNode = node; 47 | break; 48 | } 49 | edgeNum -= ((MultiTypeNode)node).getChangeCount()+1; 50 | } 51 | 52 | // Complete either pair birth or pair death proposal: 53 | if (Randomizer.nextDouble()<0.5) 54 | return birthProposal(selectedNode, edgeNum, n, m); 55 | else 56 | return deathProposal(selectedNode, edgeNum, n, m); 57 | 58 | } 59 | 60 | /** 61 | * Type change pair birth proposal. 62 | * 63 | * @param node Node above which selected edge lies 64 | * @param edgeNum Number of selected edge 65 | * @param n Number of nodes on tree. 66 | * @param m Number of type changes currently on tree. 67 | * @return log of Hastings factor of move. 68 | */ 69 | private double birthProposal(Node node, int edgeNum, int n, int m) { 70 | 71 | MultiTypeNode mtNode = (MultiTypeNode)node; 72 | 73 | int ridx = edgeNum; 74 | int sidx = edgeNum-1; 75 | 76 | double ts, tr; 77 | int oldEdgeType; 78 | if (sidx<0) { 79 | ts = node.getHeight(); 80 | oldEdgeType = mtNode.getNodeType(); 81 | } else { 82 | ts = mtNode.getChangeTime(sidx); 83 | oldEdgeType = mtNode.getChangeType(sidx); 84 | } 85 | 86 | if (ridx>mtNode.getChangeCount()-1) 87 | tr = node.getParent().getHeight(); 88 | else 89 | tr = mtNode.getChangeTime(ridx); 90 | 91 | int newEdgeType; 92 | do { 93 | newEdgeType = Randomizer.nextInt(migModel.getNTypes()); 94 | } while (newEdgeType == oldEdgeType); 95 | 96 | double tau1 = Randomizer.nextDouble()*(tr-ts) + ts; 97 | double tau2 = Randomizer.nextDouble()*(tr-ts) + ts; 98 | double tauMin = Math.min(tau1, tau2); 99 | double tauMax = Math.max(tau1, tau2); 100 | 101 | mtNode.insertChange(edgeNum, oldEdgeType, tauMax); 102 | mtNode.insertChange(edgeNum, newEdgeType, tauMin); 103 | 104 | return Math.log((migModel.getNTypes()-1)*(m + 2*n - 2)*(tr-ts)*(tr-ts)) 105 | - Math.log(2*(m + 2*n)); 106 | } 107 | 108 | /** 109 | * Colour change pair death proposal. 110 | * 111 | * @param node Node above which selected edge lies 112 | * @param edgeNum Number of selected edge 113 | * @param n Number of nodes on tree 114 | * @param m Number of colour changes currently on tree 115 | * @return log of Hastings factor of move. 116 | */ 117 | private double deathProposal(Node node, int edgeNum, int n, int m) { 118 | 119 | MultiTypeNode mtNode = (MultiTypeNode)node; 120 | 121 | int idx = edgeNum-1; 122 | int sidx = edgeNum-2; 123 | int ridx = edgeNum+1; 124 | 125 | if (sidx<-1 || ridx > mtNode.getChangeCount()) 126 | return Double.NEGATIVE_INFINITY; 127 | 128 | double ts, tr; 129 | int is, ir; 130 | if (sidx<0) { 131 | ts = node.getHeight(); 132 | is = mtNode.getNodeType(); 133 | } else { 134 | ts = mtNode.getChangeTime(sidx); 135 | is = mtNode.getChangeType(sidx); 136 | } 137 | 138 | if (ridx>mtNode.getChangeCount()-1) 139 | tr = node.getParent().getHeight(); 140 | else 141 | tr = mtNode.getChangeTime(ridx); 142 | ir = mtNode.getChangeType(ridx-1); 143 | 144 | if (is != ir) 145 | return Double.NEGATIVE_INFINITY; 146 | 147 | mtNode.removeChange(idx); 148 | mtNode.removeChange(idx); 149 | 150 | return Math.log(2*(m + 2*n - 2)) 151 | - Math.log((migModel.getNTypes()-1)*(m+2*n-4)*(tr-ts)*(tr-ts)); 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/multitypetree/operators/TypedSubtreeExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Node; 22 | import beast.base.util.Randomizer; 23 | 24 | /** 25 | * @author Tim Vaughan 26 | */ 27 | @Description("Subtree/branch exchange operator for coloured trees. This" 28 | + " is the `uniformized recolouring' variant where new branch colourings" 29 | + " are selected using random walk conditional on the colours at both" 30 | + " ends of the branch.") 31 | public class TypedSubtreeExchange extends UniformizationRetypeOperator { 32 | 33 | public Input isNarrowInput = new Input("isNarrow", 34 | "Whether or not to use narrow exchange. (Default true.)", true); 35 | 36 | @Override 37 | public double proposal() { 38 | double logHR = 0.0; 39 | 40 | // Select source and destination nodes: 41 | 42 | Node srcNode, srcNodeParent, destNode, destNodeParent; 43 | if (isNarrowInput.get()) { 44 | 45 | // Narrow exchange selection: 46 | do { 47 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 48 | } while (srcNode.isRoot() || srcNode.getParent().isRoot()); 49 | srcNodeParent = srcNode.getParent(); 50 | destNode = getOtherChild(srcNodeParent.getParent(), srcNodeParent); 51 | destNodeParent = destNode.getParent(); 52 | 53 | } else { 54 | 55 | // Wide exchange selection: 56 | do { 57 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 58 | } while (srcNode.isRoot()); 59 | srcNodeParent = srcNode.getParent(); 60 | 61 | do { 62 | destNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 63 | } while(destNode == srcNode 64 | || destNode.isRoot() 65 | || destNode.getParent() == srcNode.getParent()); 66 | destNodeParent = destNode.getParent(); 67 | } 68 | 69 | // Reject if substitution would result in negative branch lengths: 70 | if (destNode.getHeight()>srcNodeParent.getHeight() 71 | || srcNode.getHeight()>destNodeParent.getHeight()) 72 | return Double.NEGATIVE_INFINITY; 73 | 74 | // Record probability of old colours: 75 | logHR += getBranchTypeProb(srcNode) + getBranchTypeProb(destNode); 76 | 77 | // Make changes to tree topology: 78 | replace(srcNodeParent, srcNode, destNode); 79 | replace(destNodeParent, destNode, srcNode); 80 | 81 | // Recolour branches involved: 82 | try { 83 | logHR -= retypeBranch(srcNode) + retypeBranch(destNode); 84 | } catch (NoValidPathException e) { 85 | return Double.NEGATIVE_INFINITY; 86 | } 87 | 88 | return logHR; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/multitypetree/operators/TypedSubtreeExchangeEasy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Node; 22 | import beast.base.util.Randomizer; 23 | 24 | /** 25 | * @author Tim Vaughan 26 | */ 27 | @Description("Subtree/branch exchange operator for coloured trees. This" 28 | + " is the `no recolouring' variant where the new topology is selected" 29 | + " irrespective of the colouring of the branches involved. Used for" 30 | + " testing Ewing et al.'s sampler moves.") 31 | public class TypedSubtreeExchangeEasy extends MultiTypeTreeOperator { 32 | 33 | public Input isNarrowInput = new Input<>("isNarrow", 34 | "Whether or not to use narrow exchange. (Default true.)", true); 35 | 36 | @Override 37 | public double proposal() { 38 | // Select source and destination nodes: 39 | 40 | Node srcNode, srcNodeParent, destNode, destNodeParent; 41 | if (isNarrowInput.get()) { 42 | 43 | // Narrow exchange selection: 44 | do { 45 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 46 | } while (srcNode.isRoot() || srcNode.getParent().isRoot()); 47 | srcNodeParent = srcNode.getParent(); 48 | destNode = getOtherChild(srcNodeParent.getParent(), srcNodeParent); 49 | destNodeParent = destNode.getParent(); 50 | 51 | } else { 52 | 53 | // Wide exchange selection: 54 | do { 55 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 56 | } while (srcNode.isRoot()); 57 | srcNodeParent = srcNode.getParent(); 58 | 59 | do { 60 | destNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 61 | } while(destNode == srcNode 62 | || destNode.isRoot() 63 | || destNode.getParent() == srcNodeParent); 64 | destNodeParent = destNode.getParent(); 65 | 66 | // Explicitly reject outrageous node selections: 67 | // (Dangerous to make this a condition of destNode selection, 68 | // as doing so can lead to infinite loops.) 69 | if (srcNodeParent == destNode || destNodeParent == srcNode) 70 | return Double.NEGATIVE_INFINITY; 71 | } 72 | 73 | // Reject outright if substitution would result in negative branch 74 | // lengths: 75 | if (destNode.getHeight()>srcNodeParent.getHeight() 76 | || srcNode.getHeight()>destNodeParent.getHeight()) 77 | return Double.NEGATIVE_INFINITY; 78 | 79 | // Make changes to tree topology: 80 | replace(srcNodeParent, srcNode, destNode); 81 | replace(destNodeParent, destNode, srcNode); 82 | 83 | // Force rejection if resulting multi-type tree invalid: 84 | if (!mtTree.isValid()) 85 | return Double.NEGATIVE_INFINITY; 86 | 87 | return 0.0; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/multitypetree/operators/TypedSubtreeExchangeRandom.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.evolution.tree.Node; 22 | import beast.base.util.Randomizer; 23 | import multitypetree.evolution.tree.MultiTypeNode; 24 | 25 | /** 26 | * @author Tim Vaughan 27 | */ 28 | @Description("Subtree/branch exchange operator for coloured trees. This" 29 | + " is the `random recolouring' variant where new branch colourings" 30 | + " are selected using an unconditioned random walk. This will likely" 31 | + " be very inefficient as this operator requires recolouring of" 32 | + " two branches, meaning that the acceptance probability goes as" 33 | + " the _square_ of the inverse of the number of colours.") 34 | public class TypedSubtreeExchangeRandom extends RandomRetypeOperator { 35 | 36 | public Input isNarrowInput = new Input("isNarrow", 37 | "Whether or not to use narrow exchange. (Default true.)", true); 38 | 39 | @Override 40 | public double proposal() { 41 | double logHR = 0.0; 42 | 43 | // Select source and destination nodes: 44 | 45 | Node srcNode, srcNodeParent, destNode, destNodeParent; 46 | if (isNarrowInput.get()) { 47 | 48 | // Narrow exchange selection: 49 | do { 50 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 51 | } while (srcNode.isRoot() || srcNode.getParent().isRoot()); 52 | srcNodeParent = srcNode.getParent(); 53 | destNode = getOtherChild(srcNodeParent.getParent(), srcNodeParent); 54 | destNodeParent = destNode.getParent(); 55 | 56 | } else { 57 | 58 | // Wide exchange selection: 59 | do { 60 | srcNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 61 | } while (srcNode.isRoot()); 62 | srcNodeParent = srcNode.getParent(); 63 | 64 | do { 65 | destNode = mtTree.getNode(Randomizer.nextInt(mtTree.getNodeCount())); 66 | } while(destNode == srcNode 67 | || destNode.isRoot() 68 | || destNode.getParent() == srcNode.getParent()); 69 | destNodeParent = destNode.getParent(); 70 | } 71 | 72 | // Reject outright if substitution would result in negative branch 73 | // lengths: 74 | if (destNode.getHeight()>srcNodeParent.getHeight() 75 | || srcNode.getHeight()>destNodeParent.getHeight()) 76 | return Double.NEGATIVE_INFINITY; 77 | 78 | // Record probability of old colours: 79 | logHR += getBranchTypeProb(srcNode) + getBranchTypeProb(destNode); 80 | 81 | // Make changes to tree topology: 82 | replace(srcNodeParent, srcNode, destNode); 83 | replace(destNodeParent, destNode, srcNode); 84 | 85 | // Recolour branches involved: 86 | logHR -= retypeBranch(srcNode) + retypeBranch(destNode); 87 | 88 | // Force rejection if colouring inconsistent: 89 | if ((((MultiTypeNode)srcNode).getFinalType() != ((MultiTypeNode)destNodeParent).getNodeType()) 90 | || (((MultiTypeNode)destNode).getFinalType() != ((MultiTypeNode)srcNodeParent).getNodeType())) 91 | return Double.NEGATIVE_INFINITY; 92 | 93 | return logHR; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/multitypetree/operators/ZeroJump.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Input; 21 | import beast.base.core.Input.Validate; 22 | import beast.base.inference.Operator; 23 | import beast.base.inference.parameter.RealParameter; 24 | import beast.base.util.Randomizer; 25 | 26 | /** 27 | * @author Tim Vaughan 28 | */ 29 | @Description("Reversibly sets a RealParameter element to zero. Used for" 30 | + " variable selection.") 31 | public class ZeroJump extends Operator { 32 | 33 | public Input parameterInput = new Input<>( 34 | "parameter", "RealParameter to operate on.", Validate.REQUIRED); 35 | 36 | public Input alphaInput = new Input("mean", 37 | "Mean of exponential from which non-zero values are drawn in the" 38 | + " reverse jump.", Validate.REQUIRED); 39 | 40 | private RealParameter parameter; 41 | private double alpha; 42 | 43 | @Override 44 | public void initAndValidate() { 45 | parameter = parameterInput.get(); 46 | alpha = alphaInput.get(); 47 | } 48 | 49 | @Override 50 | public double proposal() { 51 | 52 | double logHR; 53 | 54 | // Select random element: 55 | int idx = Randomizer.nextInt(parameter.getDimension()); 56 | 57 | double x = parameter.getValue(idx); 58 | if (x>0) { 59 | logHR = Math.log(Math.exp(-x/alpha)/alpha); 60 | x = 0.0; 61 | } else { 62 | x = Randomizer.nextExponential(1.0/alpha); 63 | logHR = -Math.log(Math.exp(-x/alpha)/alpha); 64 | } 65 | 66 | parameter.setValue(idx, x); 67 | return logHR; 68 | } 69 | 70 | 71 | public static void main(String[] args) { 72 | 73 | for (int i=0; i<100; i++) { 74 | System.out.println(Randomizer.nextExponential(10)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/multitypetree/util/MAPTreeLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Tim Vaughan (tgvaughan@gmail.com) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.inference.Distribution; 20 | import beast.base.core.Input; 21 | import beast.base.core.Input.Validate; 22 | import beast.base.evolution.tree.Tree; 23 | import multitypetree.evolution.tree.MigrationModel; 24 | import multitypetree.evolution.tree.MultiTypeTree; 25 | 26 | import java.io.PrintStream; 27 | 28 | /** 29 | * Used to log running estimate of MAP MultiTypeTree. 30 | * 31 | * @author Tim Vaughan (tgvaughan@gmail.com) 32 | */ 33 | public class MAPTreeLogger extends Tree { 34 | 35 | public Input multiTypeTreeInput = new Input<>( 36 | "multiTypeTree", 37 | "Multi-type tree state to maximize posterior wrt.", 38 | Validate.REQUIRED); 39 | 40 | public Input posteriorInput = new Input<>( 41 | "posterior", 42 | "Posterior used to identify MAP tree", 43 | Validate.REQUIRED); 44 | 45 | MultiTypeTree currentMAPTree; 46 | double maxPosterior; 47 | 48 | @Override 49 | public void initAndValidate() { 50 | super.initAndValidate(); 51 | 52 | currentMAPTree = multiTypeTreeInput.get().copy(); 53 | currentMAPTree.setTypeTrait(multiTypeTreeInput.get().getTypeTrait()); 54 | currentMAPTree.initAndValidate(); 55 | maxPosterior = Double.NEGATIVE_INFINITY; 56 | } 57 | 58 | @Override 59 | public void init(PrintStream out) { 60 | currentMAPTree.init(out); 61 | } 62 | 63 | @Override 64 | public void log(long nSample, PrintStream out) { 65 | if (posteriorInput.get().getCurrentLogP()>maxPosterior) { 66 | maxPosterior = posteriorInput.get().getCurrentLogP(); 67 | currentMAPTree.assignFrom(multiTypeTreeInput.get()); 68 | } 69 | currentMAPTree.log(nSample, out); 70 | } 71 | 72 | @Override 73 | public void close(PrintStream out) { 74 | currentMAPTree.close(out); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/multitypetree/util/MigrationModelLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Tim Vaughan (tgvaughan@gmail.com) 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.core.BEASTObject; 20 | import beast.base.core.Input; 21 | import beast.base.core.Input.Validate; 22 | import multitypetree.evolution.tree.MultiTypeTree; 23 | import multitypetree.evolution.tree.SCMigrationModel; 24 | import multitypetree.evolution.tree.TypeSet; 25 | import beast.base.core.Loggable; 26 | 27 | import java.io.PrintStream; 28 | 29 | /** 30 | * 31 | * @author Tim Vaughan (tgvaughan@gmail.com) 32 | */ 33 | public class MigrationModelLogger extends BEASTObject implements Loggable { 34 | 35 | public Input migModelInput = new Input<>("migrationModel", 36 | "Migration model to log.", Validate.REQUIRED); 37 | 38 | public Input multiTypeTreeInput = new Input<>( 39 | "multiTypeTree", "Tree from which to acquire type names."); 40 | 41 | private SCMigrationModel migModel; 42 | private MultiTypeTree mtTree; 43 | 44 | @Override 45 | public void initAndValidate() { 46 | migModel = migModelInput.get(); 47 | mtTree = multiTypeTreeInput.get(); 48 | } 49 | 50 | @Override 51 | public void init(PrintStream out) { 52 | String outName; 53 | TypeSet typeSet = migModel.getTypeSet(); 54 | 55 | if (migModel.getID() == null || migModel.getID().matches("\\s*")) 56 | outName = "migModel"; 57 | else 58 | outName = migModel.getID(); 59 | 60 | out.print(outName + ".popSizeScaleFactor\t"); 61 | 62 | for (int i=0; i 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.core.Input; 20 | import beast.base.core.Input.Validate; 21 | import beast.base.inference.Logger; 22 | import beast.base.inference.util.ESS; 23 | import beast.base.util.DiscreteStatistics; 24 | import multitypetree.evolution.tree.MultiTypeTree; 25 | 26 | import java.io.PrintStream; 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | 30 | /** 31 | * Special logger for constructing unit tests on multi type tree operator 32 | * combinations. 33 | * 34 | * @author Tim Vaughan 35 | */ 36 | public class MultiTypeTreeStatLogger extends Logger { 37 | 38 | public Input multiTypeTreeInput = new Input( 39 | "multiTypeTree", 40 | "Multi-type tree whose stats to log.", 41 | Validate.REQUIRED); 42 | 43 | public Input burninFracInput = new Input("burninFrac", 44 | "Fraction of trace to discard. Default 0.1.", 0.1); 45 | 46 | MultiTypeTree multiTypeTree; 47 | double burninFrac, logEvery; 48 | 49 | List heights = new ArrayList(); 50 | double [] heightsArray; 51 | double heightMean, heightVar, heightESS; 52 | 53 | public MultiTypeTreeStatLogger () { 54 | loggersInput.setRule(Validate.OPTIONAL); 55 | } 56 | 57 | @Override 58 | public void initAndValidate() { 59 | multiTypeTree = multiTypeTreeInput.get(); 60 | burninFrac = burninFracInput.get(); 61 | logEvery = everyInput.get(); 62 | }; 63 | 64 | @Override 65 | public void init() { 66 | heights.clear(); 67 | } 68 | 69 | @Override 70 | public void log(long nSample) { 71 | 72 | if ((nSample < 0) || (nSample % logEvery > 0)) 73 | return; 74 | 75 | heights.add(multiTypeTree.getRoot().getHeight()); 76 | } 77 | 78 | @Override 79 | public void close() { 80 | computeStatistics(); 81 | } 82 | 83 | /** 84 | * Compute statistics from completed traces. 85 | */ 86 | public void computeStatistics() { 87 | 88 | // Truncate burnin 89 | heights = heights.subList((int)(burninFrac*heights.size()), 90 | heights.size()-1); 91 | 92 | // Transfer to array for DiscreteStatistics methods: 93 | heightsArray = new double[heights.size()]; 94 | for (int i=0; i 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Function; 21 | import beast.base.core.Input; 22 | import beast.base.core.Input.Validate; 23 | import beast.base.core.Loggable; 24 | import beast.base.evolution.tree.Node; 25 | import beast.base.inference.CalculationNode; 26 | import multitypetree.evolution.tree.MultiTypeNode; 27 | import multitypetree.evolution.tree.MultiTypeTree; 28 | import multitypetree.evolution.tree.SCMigrationModel; 29 | 30 | import java.io.PrintStream; 31 | 32 | /** 33 | * @author Tim Vaughan 34 | */ 35 | @Description("Allows logging and defining distributions over number of" 36 | + " each node type on a multi-type tree.") 37 | public class NodeTypeCounts extends CalculationNode implements Function, Loggable { 38 | 39 | public Input multiTypeTreeInput = new Input<>( 40 | "multiTypeTree", 41 | "Multi-type tree whose changes will be counted.", 42 | Validate.REQUIRED); 43 | 44 | public Input migrationModelInput = new Input<>( 45 | "migrationModel", 46 | "Migration model needed to specify number of demes.", 47 | Validate.REQUIRED); 48 | 49 | public Input internalOnlyInput = new Input<>( 50 | "internalNodesOnly", 51 | "Only count types of internal nodes.", 52 | true); 53 | 54 | public Input useCacheInput = new Input<>( 55 | "useCache", "Cache counts, updating only when tree changes. " 56 | + "Warning: this will cause problems if this TypeChangeCounts " 57 | + "is not used in the target distribution.", false); 58 | 59 | private MultiTypeTree mtTree; 60 | 61 | private int nTypes; 62 | 63 | private int[] nodeTypeCounts; 64 | private boolean useCache, dirty; 65 | 66 | public NodeTypeCounts() { }; 67 | 68 | @Override 69 | public void initAndValidate() { 70 | mtTree = multiTypeTreeInput.get(); 71 | nTypes = migrationModelInput.get().getNTypes(); 72 | 73 | nodeTypeCounts = new int[nTypes]; 74 | 75 | useCache = useCacheInput.get(); 76 | dirty = true; 77 | update(); 78 | } 79 | 80 | /** 81 | * Update type change count array as necessary. 82 | */ 83 | private void update() { 84 | if (!dirty) 85 | return; 86 | 87 | // Zero count array 88 | for (int i=0; i 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | 21 | import java.io.PrintStream; 22 | 23 | import beast.base.inference.CalculationNode; 24 | import beast.base.core.Description; 25 | import beast.base.core.Function; 26 | import beast.base.core.Input; 27 | import beast.base.core.Loggable; 28 | import beast.base.core.Input.Validate; 29 | import beast.base.evolution.tree.Node; 30 | import beast.base.evolution.tree.Node; 31 | import beast.base.evolution.tree.Tree; 32 | import beast.base.evolution.tree.Tree; 33 | 34 | 35 | /** 36 | * @author Tim Vaughan 37 | */ 38 | @Description("Logger to report total length of all branches on tree.") 39 | public class TreeLengthLogger extends CalculationNode implements Loggable, Function { 40 | 41 | public Input treeInput = new Input<>("tree", 42 | "Tree to report total branch length of.", 43 | Validate.REQUIRED); 44 | 45 | @Override 46 | public void initAndValidate() { } 47 | 48 | @Override 49 | public void init(PrintStream out) { 50 | Tree tree = treeInput.get(); 51 | if (getID() == null || getID().matches("\\s*")) { 52 | out.print(tree.getID() + ".length\t"); 53 | } else { 54 | out.print(getID() + "\t"); 55 | } 56 | } 57 | 58 | @Override 59 | public void log(long nSample, PrintStream out) { 60 | Tree tree = treeInput.get(); 61 | out.print(getSubTreeLength(tree.getRoot()) + "\t"); 62 | } 63 | 64 | /** 65 | * Calculate total length of branches in sub-tree of node. 66 | * 67 | * @param node 68 | * @return total branch length 69 | */ 70 | private double getSubTreeLength(Node node) { 71 | double length = 0; 72 | for (Node child : node.getChildren()) { 73 | length += node.getHeight()-child.getHeight(); 74 | length += getSubTreeLength(child); 75 | } 76 | 77 | return length; 78 | } 79 | 80 | @Override 81 | public void close(PrintStream out) { 82 | } 83 | 84 | @Override 85 | public int getDimension() { 86 | return 1; 87 | } 88 | 89 | @Override 90 | public double getArrayValue() { 91 | return getSubTreeLength(treeInput.get().getRoot()); 92 | } 93 | 94 | @Override 95 | public double getArrayValue(int iDim) { 96 | return getSubTreeLength(treeInput.get().getRoot()); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/multitypetree/util/TreeRootTypeLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.inference.CalculationNode; 20 | import beast.base.core.Description; 21 | import beast.base.core.Input; 22 | import beast.base.core.Function; 23 | import beast.base.core.Input.Validate; 24 | import multitypetree.evolution.tree.MultiTypeNode; 25 | import multitypetree.evolution.tree.MultiTypeTree; 26 | import beast.base.core.Loggable; 27 | 28 | import java.io.PrintStream; 29 | 30 | 31 | /** 32 | * @author Denise Kuehnert 33 | * Date: Feb 12, 2013 34 | * Time: 2:09:02 PM 35 | */ 36 | @Description("Logger to report root type of a multi-type tree.") 37 | public class TreeRootTypeLogger extends CalculationNode implements Loggable, Function { 38 | 39 | public Input multiTypeTreeInput = new Input<>( 40 | "multiTypeTree", "MultiTypeTree to report root type of.", Validate.REQUIRED); 41 | 42 | public Input logStringTypesInput = new Input<>("logStringTypes", 43 | "Use string type labels in log. Warning: breaks tracer.", false); 44 | 45 | MultiTypeTree mtTree; 46 | 47 | @Override 48 | public void initAndValidate() { 49 | mtTree = multiTypeTreeInput.get(); 50 | } 51 | 52 | @Override 53 | public void init(PrintStream out) { 54 | 55 | if (getID() == null || getID().matches("\\s*")) { 56 | out.print(mtTree.getID() + ".rootColor\t"); 57 | } else { 58 | out.print(getID() + "\t"); 59 | } 60 | } 61 | 62 | @Override 63 | public void log(long nSample, PrintStream out) { 64 | if (logStringTypesInput.get()) 65 | out.print(mtTree.getTypeSet().getTypeName(((MultiTypeNode)mtTree.getRoot()).getNodeType()) + "\t"); 66 | else 67 | out.print(((MultiTypeNode)mtTree.getRoot()).getNodeType() + "\t"); 68 | } 69 | 70 | @Override 71 | public void close(PrintStream out) { }; 72 | 73 | @Override 74 | public int getDimension() { 75 | return 1; 76 | } 77 | 78 | @Override 79 | public double getArrayValue() { 80 | return ((MultiTypeNode)mtTree.getRoot()).getNodeType(); 81 | } 82 | 83 | @Override 84 | public double getArrayValue(int iDim) { 85 | return ((MultiTypeNode)mtTree.getRoot()).getNodeType(); 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/multitypetree/util/TypeChangeCounts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.inference.CalculationNode; 20 | import beast.base.core.Description; 21 | import beast.base.core.Input; 22 | import beast.base.core.Input.Validate; 23 | import beast.base.core.Loggable; 24 | import beast.base.core.Function; 25 | import beast.base.evolution.tree.Node; 26 | import multitypetree.evolution.tree.MultiTypeNode; 27 | import multitypetree.evolution.tree.MultiTypeTree; 28 | import multitypetree.evolution.tree.SCMigrationModel; 29 | 30 | import java.io.PrintStream; 31 | 32 | /** 33 | * @author Tim Vaughan 34 | */ 35 | @Description("Allows logging and defining distributions over number of" 36 | + " type changes on a multi-type tree.") 37 | public class TypeChangeCounts extends CalculationNode implements Function, Loggable { 38 | 39 | public Input multiTypeTreeInput = new Input<>( 40 | "multiTypeTree", 41 | "Multi-type tree whose changes will be counted.", 42 | Validate.REQUIRED); 43 | 44 | public Input migrationModelInput = new Input<>( 45 | "migrationModel", 46 | "Migration model needed to specify number of demes.", 47 | Validate.REQUIRED); 48 | 49 | public Input useCacheInput = new Input<>( 50 | "useCache", "Cache counts, updating only when tree changes. " 51 | + "Warning: this will cause problems if this TypeChangeCounts " 52 | + "is not used in the target distribution.", false); 53 | 54 | private MultiTypeTree mtTree; 55 | 56 | private int nTypes; 57 | 58 | private int[] typeChanges; 59 | private boolean useCache, dirty; 60 | 61 | public TypeChangeCounts() { }; 62 | 63 | @Override 64 | public void initAndValidate() { 65 | mtTree = multiTypeTreeInput.get(); 66 | nTypes = migrationModelInput.get().getNTypes(); 67 | 68 | typeChanges = new int[nTypes*(nTypes-1)]; 69 | 70 | useCache = useCacheInput.get(); 71 | dirty = true; 72 | update(); 73 | } 74 | 75 | /** 76 | * Update type change count array as necessary. 77 | */ 78 | private void update() { 79 | if (!dirty) 80 | return; 81 | 82 | // Zero type change count array 83 | for (int i=0; ii) 119 | j -= 1; 120 | return i*(nTypes-1)+j; 121 | } 122 | 123 | @Override 124 | public int getDimension() { 125 | return nTypes*(nTypes-1); 126 | } 127 | 128 | @Override 129 | public double getArrayValue() { 130 | update(); 131 | return typeChanges[0]; 132 | } 133 | 134 | @Override 135 | public double getArrayValue(int iDim) { 136 | if (iDim 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.util; 18 | 19 | import beast.base.core.Description; 20 | import beast.base.core.Function; 21 | import beast.base.core.Input; 22 | import beast.base.core.Input.Validate; 23 | import beast.base.core.Loggable; 24 | import beast.base.evolution.tree.Node; 25 | import beast.base.inference.CalculationNode; 26 | import multitypetree.evolution.tree.MigrationModel; 27 | import multitypetree.evolution.tree.MultiTypeNode; 28 | import multitypetree.evolution.tree.MultiTypeTree; 29 | import multitypetree.evolution.tree.SCMigrationModel; 30 | 31 | import java.io.PrintStream; 32 | 33 | /** 34 | * @author Tim Vaughan 35 | */ 36 | @Description("Allows logging and defining distributions over the lengths of" 37 | + " time lineages spend in each type on a multi-type tree.") 38 | public class TypeLengths extends CalculationNode implements Function, Loggable { 39 | 40 | public Input multiTypeTreeInput = new Input<>( 41 | "multiTypeTree", 42 | "Multi-type tree whose type-associated lengths will be recorded.", 43 | Validate.REQUIRED); 44 | 45 | public Input migrationModelInput = new Input<>( 46 | "migrationModel", 47 | "Migration model needed to specify number of demes.", 48 | Validate.REQUIRED); 49 | 50 | private MultiTypeTree mtTree; 51 | private MigrationModel migModel; 52 | 53 | private double[] typeLengths; 54 | 55 | public TypeLengths() { } 56 | 57 | @Override 58 | public void initAndValidate() { 59 | mtTree = multiTypeTreeInput.get(); 60 | migModel = migrationModelInput.get(); 61 | 62 | typeLengths = new double[migModel.getNTypes()]; 63 | 64 | update(); 65 | } 66 | 67 | /** 68 | * Update type change count array as necessary. 69 | */ 70 | private void update() { 71 | 72 | // Zero type change count array 73 | for (int i=0; i 15 | */ 16 | public class TypedNodeTreeLogger extends BEASTObject implements Loggable { 17 | 18 | public Input multiTypeTreeInput = new Input<>( 19 | "multiTypeTree", 20 | "Multi-type tree to log.", 21 | Input.Validate.REQUIRED); 22 | 23 | MultiTypeTree mtTree; 24 | 25 | @Override 26 | public void initAndValidate() { 27 | mtTree = multiTypeTreeInput.get(); 28 | } 29 | 30 | @Override 31 | public void init(PrintStream out) { 32 | mtTree.init(out); 33 | } 34 | 35 | @Override 36 | public void log(long nSample, PrintStream out) { 37 | 38 | // Set up metadata string 39 | for (Node node : mtTree.getNodesAsArray()) { 40 | MultiTypeNode mtNode = (MultiTypeNode)node; 41 | mtNode.metaDataString = mtTree.getTypeLabel() 42 | + "=\"" 43 | + mtTree.getTypeSet().getTypeName(mtNode.getNodeType()) 44 | + "\""; 45 | } 46 | 47 | out.print("tree STATE_" + nSample + " = "); 48 | out.print(mtTree.getRoot().toSortedNewick(new int[1], true)); 49 | out.print(";"); 50 | } 51 | 52 | @Override 53 | public void close(PrintStream out) { 54 | mtTree.close(out); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/multitypetree/util/UtilMethods.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package multitypetree.util; 19 | 20 | import beast.base.inference.parameter.IntegerParameter; 21 | import multitypetree.evolution.tree.SCMigrationModel; 22 | 23 | /** 24 | * 25 | * @author Tim Vaughan 26 | */ 27 | public class UtilMethods { 28 | 29 | public static double [] getSimulatedHeights(SCMigrationModel migrationModel, 30 | IntegerParameter leafTypes) throws Exception { 31 | 32 | // Generate ensemble: 33 | int reps = 100000; 34 | double[] heights = new double[reps]; 35 | 36 | for (int i = 0; i < reps; i++) { 37 | multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree sctree; 38 | sctree = new multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree(); 39 | sctree.initByName( 40 | "migrationModel", migrationModel, 41 | "leafTypes", leafTypes); 42 | 43 | heights[i] = sctree.getRoot().getHeight(); 44 | } 45 | 46 | return heights; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/multitypetree/coalescent/SCLikelihoodTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.coalescent; 18 | 19 | import org.junit.Test; 20 | 21 | import beast.base.inference.parameter.RealParameter; 22 | import junit.framework.TestCase; 23 | import multitypetree.distributions.StructuredCoalescentTreeDensity; 24 | import multitypetree.evolution.tree.MultiTypeTreeFromNewick; 25 | import multitypetree.evolution.tree.SCMigrationModel; 26 | import multitypetree.evolution.tree.TypeSet; 27 | 28 | /** 29 | * Tests for StructuredCoalescentLikelihood class methods. 30 | * 31 | * @author Tim Vaughan 32 | */ 33 | public class SCLikelihoodTest extends TestCase { 34 | 35 | /** 36 | * Test of calculateLogP method, of class StructuredCoalescentLikelihood. 37 | */ 38 | @Test 39 | public void testCalculateLogP() throws Exception { 40 | System.out.println("SCLikelihoodTest"); 41 | 42 | // Assemble test MultiTypeTree: 43 | String newickStr = 44 | "(((A[&state=1]:0.25)[&state=0]:0.25,B[&state=0]:0.5)[&state=0]:1.5," 45 | + "(C[&state=0]:1.0,D[&state=0]:1.0)[&state=0]:1.0)[&state=0]:0.0;"; 46 | 47 | MultiTypeTreeFromNewick mtTree = new MultiTypeTreeFromNewick(); 48 | mtTree.initByName( 49 | "value", newickStr, 50 | "typeLabel", "state"); 51 | 52 | // Assemble migration model: 53 | RealParameter rateMatrix = new RealParameter(); 54 | rateMatrix.initByName("value","2.0 1.0"); 55 | RealParameter popSizes = new RealParameter(); 56 | popSizes.initByName("value","5.0 10.0"); 57 | SCMigrationModel migrationModel = new SCMigrationModel(); 58 | migrationModel.initByName( 59 | "rateMatrix", rateMatrix, 60 | "popSizes", popSizes, 61 | "typeSet", new TypeSet("A", "B")); 62 | 63 | // Set up likelihood instance: 64 | StructuredCoalescentTreeDensity likelihood = new StructuredCoalescentTreeDensity(); 65 | likelihood.initByName( 66 | "migrationModel", migrationModel, 67 | "multiTypeTree", mtTree); 68 | 69 | double expResult = -16.52831; // Calculated by hand 70 | double result = likelihood.calculateLogP(); 71 | 72 | System.out.println(result); 73 | assertEquals(expResult, result, 1e-5); 74 | 75 | 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /test/multitypetree/operators/NSR_Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import multitypetree.distributions.StructuredCoalescentTreeDensity; 20 | import multitypetree.evolution.tree.MultiTypeTree; 21 | import multitypetree.evolution.tree.SCMigrationModel; 22 | import multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree; 23 | import multitypetree.evolution.tree.TypeSet; 24 | import multitypetree.operators.NodeShiftRetype; 25 | import junit.framework.TestCase; 26 | import multitypetree.util.MultiTypeTreeStatLogger; 27 | import org.junit.Assert; 28 | import org.junit.Test; 29 | 30 | import beast.base.inference.MCMC; 31 | import beast.base.inference.Operator; 32 | import beast.base.inference.State; 33 | import beast.base.inference.parameter.RealParameter; 34 | import beast.base.util.Randomizer; 35 | 36 | /** 37 | * 38 | * @author Tim Vaughan 39 | */ 40 | public class NSR_Test extends TestCase { 41 | 42 | @Test 43 | public void test() throws Exception { 44 | System.out.println("NSR test"); 45 | 46 | // Fix seed. 47 | Randomizer.setSeed(42); 48 | 49 | // Assemble migration model: 50 | RealParameter rateMatrix = new RealParameter("0.1 0.1"); 51 | RealParameter popSizes = new RealParameter("7.0 7.0"); 52 | SCMigrationModel migModel = new SCMigrationModel(); 53 | migModel.initByName( 54 | "rateMatrix", rateMatrix, 55 | "popSizes", popSizes, 56 | "typeSet", new TypeSet("A", "B")); 57 | 58 | // Assemble initial MultiTypeTree 59 | MultiTypeTree mtTree = new StructuredCoalescentMultiTypeTree(); 60 | mtTree.initByName( 61 | "typeLabel", "deme", 62 | "migrationModel", migModel, 63 | "leafTypes","1 0"); 64 | 65 | // Set up state: 66 | State state = new State(); 67 | state.initByName("stateNode", mtTree); 68 | 69 | // Assemble distribution: 70 | StructuredCoalescentTreeDensity distribution = 71 | new StructuredCoalescentTreeDensity(); 72 | distribution.initByName( 73 | "migrationModel", migModel, 74 | "multiTypeTree", mtTree); 75 | 76 | // Set up operators: 77 | Operator operatorNSR = new NodeShiftRetype(); 78 | operatorNSR.initByName( 79 | "weight", 1.0, 80 | "multiTypeTree", mtTree, 81 | "migrationModel", migModel); 82 | 83 | // Set up stat analysis logger: 84 | MultiTypeTreeStatLogger logger = new MultiTypeTreeStatLogger(); 85 | logger.initByName( 86 | "multiTypeTree", mtTree, 87 | "burninFrac", 0.1, 88 | "logEvery", 100); 89 | 90 | // Set up MCMC: 91 | MCMC mcmc = new MCMC(); 92 | mcmc.initByName( 93 | "chainLength", "1000000", 94 | "state", state, 95 | "distribution", distribution, 96 | "operator", operatorNSR, 97 | "logger", logger); 98 | 99 | // Run MCMC: 100 | mcmc.run(); 101 | 102 | System.out.format("height mean = %s\n", logger.getHeightMean()); 103 | System.out.format("height var = %s\n", logger.getHeightVar()); 104 | System.out.format("height ESS = %s\n", logger.getHeightESS()); 105 | 106 | // Compare analysis results with truth: 107 | boolean withinTol = (logger.getHeightESS()>2000) 108 | && (Math.abs(logger.getHeightMean()-19.0)<0.5) 109 | && (Math.abs(logger.getHeightVar()-291)<30); 110 | 111 | Assert.assertTrue(withinTol); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/multitypetree/operators/STXR_NRR_MTU_TS_Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import multitypetree.distributions.StructuredCoalescentTreeDensity; 20 | import multitypetree.evolution.tree.MultiTypeTree; 21 | import multitypetree.evolution.tree.SCMigrationModel; 22 | import multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree; 23 | import multitypetree.evolution.tree.TypeSet; 24 | import multitypetree.operators.MultiTypeTreeScale; 25 | import multitypetree.operators.MultiTypeUniform; 26 | import multitypetree.operators.NodeRetypeRandom; 27 | import multitypetree.operators.TypedSubtreeExchangeRandom; 28 | import junit.framework.TestCase; 29 | import multitypetree.util.MultiTypeTreeStatLogger; 30 | import org.junit.Assert; 31 | import org.junit.Test; 32 | 33 | import beast.base.inference.MCMC; 34 | import beast.base.inference.Operator; 35 | import beast.base.inference.State; 36 | import beast.base.inference.parameter.RealParameter; 37 | import beast.base.util.Randomizer; 38 | 39 | /** 40 | * 41 | * @author Tim Vaughan 42 | */ 43 | public class STXR_NRR_MTU_TS_Test extends TestCase { 44 | 45 | @Test 46 | public void test() throws Exception { 47 | System.out.println("STXR_NRR_MTU_TS test"); 48 | 49 | // Fix seed. 50 | Randomizer.setSeed(42); 51 | 52 | // Assemble migration model: 53 | RealParameter rateMatrix = new RealParameter("0.1 0.1"); 54 | RealParameter popSizes = new RealParameter("7.0 7.0"); 55 | SCMigrationModel migModel = new SCMigrationModel(); 56 | migModel.initByName( 57 | "rateMatrix", rateMatrix, 58 | "popSizes", popSizes, 59 | "typeSet", new TypeSet("A", "B")); 60 | 61 | // Assemble initial MultiTypeTree 62 | MultiTypeTree mtTree = new StructuredCoalescentMultiTypeTree(); 63 | mtTree.initByName( 64 | "typeLabel", "deme", 65 | "migrationModel", migModel, 66 | "leafTypes","1 1 0 0"); 67 | 68 | // Set up state: 69 | State state = new State(); 70 | state.initByName("stateNode", mtTree); 71 | 72 | // Assemble distribution: 73 | StructuredCoalescentTreeDensity distribution = 74 | new StructuredCoalescentTreeDensity(); 75 | distribution.initByName( 76 | "migrationModel", migModel, 77 | "multiTypeTree", mtTree); 78 | 79 | 80 | // Set up operators: 81 | Operator operatorSTXR = new TypedSubtreeExchangeRandom(); 82 | operatorSTXR.initByName( 83 | "weight", 1.0, 84 | "multiTypeTree", mtTree, 85 | "migrationModel", migModel, 86 | "mu", 0.2); 87 | 88 | Operator operatorNRR = new NodeRetypeRandom(); 89 | operatorNRR.initByName( 90 | "weight", 1.0, 91 | "multiTypeTree", mtTree, 92 | "migrationModel", migModel, 93 | "mu", 0.2); 94 | 95 | Operator operatorMTU = new MultiTypeUniform(); 96 | operatorMTU.initByName( 97 | "weight", 1.0, 98 | "multiTypeTree", mtTree, 99 | "migrationModel", migModel); 100 | 101 | Operator operatorMTTS = new MultiTypeTreeScale(); 102 | operatorMTTS.initByName( 103 | "weight", 1.0, 104 | "multiTypeTree", mtTree, 105 | "migrationModel", migModel, 106 | "scaleFactor", 1.5, 107 | "useOldTreeScaler", false); 108 | 109 | // Set up stat analysis logger: 110 | MultiTypeTreeStatLogger logger = new MultiTypeTreeStatLogger(); 111 | logger.initByName( 112 | "multiTypeTree", mtTree, 113 | "burninFrac", 0.1, 114 | "logEvery", 1000); 115 | 116 | // Set up MCMC: 117 | MCMC mcmc = new MCMC(); 118 | mcmc.initByName( 119 | "chainLength", "10000000", 120 | "state", state, 121 | "distribution", distribution, 122 | "operator", operatorSTXR, 123 | "operator", operatorNRR, 124 | "operator", operatorMTU, 125 | "operator", operatorMTTS, 126 | "logger", logger); 127 | 128 | // Run MCMC: 129 | mcmc.run(); 130 | 131 | System.out.format("height mean = %s\n", logger.getHeightMean()); 132 | System.out.format("height var = %s\n", logger.getHeightVar()); 133 | System.out.format("height ESS = %s\n", logger.getHeightESS()); 134 | 135 | // Compare analysis results with truth: 136 | boolean withinTol = (logger.getHeightESS()>2000) 137 | && (Math.abs(logger.getHeightMean()-25.8)<0.5) 138 | && (Math.abs(logger.getHeightVar()-320)<30); 139 | 140 | Assert.assertTrue(withinTol); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /test/multitypetree/operators/STX_NR_MTU_TS_Test.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Tim Vaughan 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package multitypetree.operators; 18 | 19 | import multitypetree.util.UtilMethods; 20 | import multitypetree.distributions.StructuredCoalescentTreeDensity; 21 | import multitypetree.evolution.tree.MultiTypeTree; 22 | import multitypetree.evolution.tree.SCMigrationModel; 23 | import multitypetree.evolution.tree.StructuredCoalescentMultiTypeTree; 24 | import multitypetree.evolution.tree.TypeSet; 25 | import multitypetree.operators.MultiTypeTreeScale; 26 | import multitypetree.operators.MultiTypeUniform; 27 | import multitypetree.operators.NodeRetype; 28 | import multitypetree.operators.TypedSubtreeExchange; 29 | import junit.framework.TestCase; 30 | import multitypetree.util.MultiTypeTreeStatLogger; 31 | import org.junit.Assert; 32 | import org.junit.Test; 33 | 34 | import beast.base.inference.MCMC; 35 | import beast.base.inference.Operator; 36 | import beast.base.inference.State; 37 | import beast.base.inference.parameter.IntegerParameter; 38 | import beast.base.inference.parameter.RealParameter; 39 | import beast.base.util.DiscreteStatistics; 40 | import beast.base.util.Randomizer; 41 | 42 | /** 43 | * 44 | * @author Tim Vaughan 45 | */ 46 | public class STX_NR_MTU_TS_Test extends TestCase { 47 | 48 | @Test 49 | public void test() throws Exception { 50 | System.out.println("STX_NR_MTU_TS test"); 51 | 52 | // Test passing locally, not on Travis. WHY!? 53 | 54 | // Fix seed. 55 | Randomizer.setSeed(53); 56 | 57 | // Assemble migration model: 58 | RealParameter rateMatrix = new RealParameter("0.1 0.1"); 59 | RealParameter popSizes = new RealParameter("7.0 7.0"); 60 | SCMigrationModel migModel = new SCMigrationModel(); 61 | migModel.initByName( 62 | "rateMatrix", rateMatrix, 63 | "popSizes", popSizes, 64 | "typeSet", new TypeSet("A", "B")); 65 | 66 | // Assemble initial MultiTypeTree 67 | MultiTypeTree mtTree = new StructuredCoalescentMultiTypeTree(); 68 | mtTree.initByName( 69 | "typeLabel", "deme", 70 | "migrationModel", migModel, 71 | "leafTypes","1 1 0 0"); 72 | 73 | // Set up state: 74 | State state = new State(); 75 | state.initByName("stateNode", mtTree); 76 | 77 | // Assemble distribution: 78 | StructuredCoalescentTreeDensity distribution = 79 | new StructuredCoalescentTreeDensity(); 80 | distribution.initByName( 81 | "migrationModel", migModel, 82 | "multiTypeTree", mtTree); 83 | 84 | 85 | // Set up operators: 86 | Operator operatorSTX = new TypedSubtreeExchange(); 87 | operatorSTX.initByName( 88 | "weight", 1.0, 89 | "multiTypeTree", mtTree, 90 | "migrationModel", migModel); 91 | 92 | Operator operatorNR = new NodeRetype(); 93 | operatorNR.initByName( 94 | "weight", 1.0, 95 | "multiTypeTree", mtTree, 96 | "migrationModel", migModel); 97 | 98 | Operator operatorMTU = new MultiTypeUniform(); 99 | operatorMTU.initByName( 100 | "weight", 1.0, 101 | "migrationModel", migModel, 102 | "multiTypeTree", mtTree); 103 | 104 | Operator operatorMTTS = new MultiTypeTreeScale(); 105 | operatorMTTS.initByName( 106 | "weight", 1.0, 107 | "multiTypeTree", mtTree, 108 | "migrationModel", migModel, 109 | "scaleFactor", 1.5, 110 | "useOldTreeScaler", false); 111 | 112 | // Set up stat analysis logger: 113 | MultiTypeTreeStatLogger logger = new MultiTypeTreeStatLogger(); 114 | logger.initByName( 115 | "multiTypeTree", mtTree, 116 | "burninFrac", 0.1, 117 | "logEvery", 1000); 118 | 119 | // Set up MCMC: 120 | MCMC mcmc = new MCMC(); 121 | mcmc.initByName( 122 | "chainLength", "1000000", 123 | "state", state, 124 | "distribution", distribution, 125 | "operator", operatorSTX, 126 | "operator", operatorNR, 127 | "operator", operatorMTU, 128 | "operator", operatorMTTS, 129 | "logger", logger); 130 | 131 | // Run MCMC: 132 | mcmc.run(); 133 | 134 | System.out.format("height mean = %s\n", logger.getHeightMean()); 135 | System.out.format("height var = %s\n", logger.getHeightVar()); 136 | System.out.format("height ESS = %s\n", logger.getHeightESS()); 137 | 138 | // Direct simulation: 139 | double [] heights = UtilMethods.getSimulatedHeights(migModel, 140 | new IntegerParameter("1 1 0 0")); 141 | double simHeightMean = DiscreteStatistics.mean(heights); 142 | double simHeightVar = DiscreteStatistics.variance(heights); 143 | 144 | System.out.format("sim height mean = %s\n", simHeightMean); 145 | System.out.format("sim height var = %s\n", simHeightVar); 146 | 147 | // Compare analysis results with truth: 148 | boolean withinTol = (logger.getHeightESS()>500) 149 | && (Math.abs(logger.getHeightMean()-simHeightMean)<2.0) 150 | && (Math.abs(logger.getHeightVar()-simHeightVar)<50); 151 | 152 | Assert.assertTrue(withinTol); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /version.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | --------------------------------------------------------------------------------