def add_state(self, state): # Add mean clock rate xml.parameter(state, text=self.initial_mean, id=self.mean_rate_id, upper="1000.0", name="stateNode")
def _add_prior(self, prior, name): alpha_prior = xml.prior(prior, id="covarion_alpha_prior.s:%s" % name, name="distribution", x="@covarion_alpha.s:%s" % name) xml.Uniform(alpha_prior, id="CovAlphaUniform:%s" % name, name="distr", upper="Infinity") switch_prior = xml.prior(prior, id="covarion_s_prior.s:%s" % name, name="distribution", x="@covarion_s.s:%s" % name) gamma = xml.Gamma(switch_prior, id="Gamma.0:%s" % name, name="distr") xml.parameter(gamma, text="0.05", id="covarion_switch_gamma_param1:%s" % name, name="alpha", lower="0.0", upper="0.0") xml.parameter(gamma, text="10.0", id="covarion_switch_gamma_param2:%s" % name, name="beta", lower="0.0", upper="0.0")
def add_operators(self, beastxml): if beastxml.config.languages.sample_branch_lengths: updown = xml.operator( beastxml.run, attrib={ "id": "UpDown", "spec": "UpDownOperator", "scaleFactor": "0.5", "weight": "3.0"}) xml.tree(updown, idref="Tree.t:beastlingTree", name="up") xml.parameter(updown, idref="birthRate.t:beastlingTree", name="down") ### Include clock rates in up/down only if calibrations are given if beastxml.config.calibrations: for clock in beastxml.config.clocks: if clock.estimate_rate: xml.parameter(updown, idref=clock.mean_rate_id, name="down") # Birth rate scaler # Birth rate is *always* scaled. xml.operator( beastxml.run, attrib={"id": "YuleBirthRateScaler.t:beastlingTree", "spec": "ScaleOperator", "parameter": "@birthRate.t:beastlingTree", "scaleFactor": "0.5", "weight": "3.0"})
def add_state(self, state): BaseModel.add_state(self, state) if self.gamma_categories > 0: xml.parameter(state, text="1.0", id="gammaShape.s:%s" % self.name, name="stateNode")
def add_state(self, state): RelaxedClock.add_state(self, state) # Standard deviation for lognormal dist xml.parameter(state, text=self.initial_variance, id="ucldSdev.c:%s" % self.name, lower="0.0", upper="10.0", name="stateNode")
def add_frequency_state(self, state): for fname in self.parameter_identifiers(): xml.parameter( state, text="0.94 0.05 0.01", id="%s:visiblefrequencies.s" % fname, name="stateNode", dimension="3", lower="0.0", upper="1.0")
def add_state(self, state): RelaxedClock.add_state(self, state) xml.parameter(state, text="2.0", id="clockRateGammaShape:%s" % self.name, lower="0.0", name="stateNode") xml.parameter(state, text="0.5", id="clockRateGammaScale:%s" % self.name, lower="0.0", name="stateNode")
def add_state(self, state): xml.parameter(state, text="100.0", id="sphericalPrecision", lower="0.0", name="stateNode") xml.stateNode(state, id="location.geo", spec="sphericalGeo.LocationParameter", minordimension="2", estimate="true", value="0.0 0.0", lower="0.0")
def add_prior(self, beastxml): """Add a Yule tree prior.""" coalescent = xml.distribution(beastxml.prior, id="Coalescent.t:beastlingTree", spec="Coalescent") popmod = xml.populationModel(coalescent, id="ConstantPopulation:beastlingTree", spec="ConstantPopulation") xml.parameter(popmod, idref="popSize.t:beastlingTree", name="popSize") xml.treeIntervals(coalescent, id="TreeIntervals", spec="TreeIntervals", tree="@Tree.t:beastlingTree")
def add_operators(self, run): RelaxedClock.add_operators(self, run) updown = xml.operator(run, id="relaxedClockGammaUpDown:%s" % self.name, spec="UpDownOperator", scaleFactor="0.5", weight="3.0") xml.parameter(updown, idref="clockRateGammaShape:%s" % self.name, name="up") xml.parameter(updown, idref="clockRateGammaScale:%s" % self.name, name="down")
def _add_substmodel(self, sitemodel, feature, name): if self.share_params: name = self.name self.subst_model_id = "%s:pdcovarion.s" % name subst_model_id = "%s:pdcovarion.s" % name substmodel = xml.substModel( sitemodel, id=subst_model_id, spec="BirthDeathCovarion2", deathprob="@{:}:pdcovarion_death.s".format(name), originLength="@{:s}:pdcovarion_origin.s".format(name), switchRate="@{:}:pdcovarion_s.s".format(name)) # Numerical instability is an issue with this model, so we give the # option of using a more robust method of computing eigenvectors. if self.use_robust_eigensystem: # pragma: no cover raise ValueError( "Currently, Beast's pseudo-Dollo covarion model does not " "support robust eigensystems.") substmodel.set("eigenSystem", "beast.evolution.substitutionmodel.RobustEigenSystem") # The "vfrequencies" parameter here is the frequencies # of the *visible* states (present/absent) and should # be based on the data (if we are doing an empirical # analysis) if self.frequencies == "estimate": substmodel.set("vfrequencies","@%s:visiblefrequencies.s" % name) else: vfreq = xml.vfrequencies( substmodel, id="%s:visiblefrequencies.s" % name, dimension="3", spec="parameter.RealParameter") if self.frequencies == "empirical": # pragma: no cover raise ValueError("Dollo model {:} cannot derive empirical " "frequencies from data".format(self.name)) else: vfreq.text="0.94 0.05 0.01" # These are the frequencies of the *hidden* states # (fast / slow), and are just set to 50: 50. They could be estimated, # in principle, but this seems to lead to serious instability problems # so we don't expose that possibility to the user. xml.parameter( substmodel, text="0.5 0.5", id="%s: hiddenfrequencies.s" % name, dimension="2", name="hfrequencies", lower="0.0", upper="1.0")
def add_state(self, state): BinaryModel.add_state(self, state) # Each feature gets a param for fname in self.parameter_identifiers(): xml.parameter(state, text="0.5", id="covarion_alpha.s:%s" % fname, lower="1.0E-4", name="stateNode", upper="1.0") xml.parameter(state, text="0.5", id="covarion_s.s:%s" % fname, lower="1.0E-4", name="stateNode", upper="Infinity")
def add_substmodel(self, sitemodel, feature, fname): # If we're sharing one substmodel across all features and have already # created it, just reference it and that's it if self.subst_model_id: sitemodel.set("substModel", "@%s" % self.subst_model_id) return # Otherwise, create a substmodel name = self.name if self.share_params else fname subst_model_id = "binaryCTMC.s:%s" % name if self.share_params: self.subst_model_id = subst_model_id substmodel = xml.substModel(sitemodel, id=subst_model_id, spec="GeneralSubstitutionModel") xml.parameter(substmodel, text="1.0 1.0", id="rates.s:%s" % name, dimension=2, estimate="false", name="rates") if self.frequencies == "estimate": xml.frequencies(substmodel, id="estimatedFrequencies.s:%s" % name, spec="Frequencies", frequencies="@freqs_param.s:%s" % name) elif self.frequencies == "empirical": attribs = { "id": "empiricalFrequencies.s:%s" % name, "spec": "Frequencies" } if self.share_params: if self.single_sitemodel: attribs["data"] = "@filtered_data_%s" % name else: attribs["frequencies"] = self.build_freq_str() else: attribs["data"] = "@feature_data_%s" % name xml.frequencies(substmodel, attrib=attribs) elif self.frequencies == "uniform": xml.frequencies(substmodel, text="0.5 0.5", id="frequencies.s:%s" % name, dimension="2", spec="parameter.RealParameter")
def add_substmodel(self, sitemodel, feature, fname): attribs = { "id": "svs.s:%s"%fname, "rateIndicator": "@rateIndicator.s:%s" % fname, "rates": "@relativeGeoRates.s:%s" % fname, "spec": "SVSGeneralSubstitutionModel"} if not self.symmetric: attribs['symmetric'] = 'false' if self.use_robust_eigensystem: attribs["eigenSystem"] = "beast.evolution.substitutionmodel.RobustEigenSystem" substmodel = xml.substModel(sitemodel, **attribs) attribs = { "id": "feature_freqs.s:%s"%fname, "spec": "Frequencies", } freq_string=None if self.frequencies == "estimate": attribs["frequencies"] = "@feature_freqs_param.s:%s"%fname elif self.frequencies == "uniform": freq_string = str(1.0/self.valuecounts[feature]) elif self.frequencies == "empirical": #TODO: Do this in the BEAStly way freqs = [ self.counts[feature].get( self.unique_values[feature][v], 0) for v in range(self.valuecounts[feature])] norm = float(sum(freqs)) freqs = [f/norm for f in freqs] # Sometimes, due to WALS oddities, there's a zero frequency, and that makes BEAST sad. So do some smoothing in these cases: if 0 in freqs: freqs = [0.1/self.valuecounts[feature] + 0.9*f for f in freqs] norm = float(sum(freqs)) freq_string = " ".join([str(c/norm) for c in freqs]) else: raise ValueError( "Model BSVS does not recognize frequencies %r, " "should be 'uniform' or 'empirical'." % self.frequencies) freq = xml.frequencies(substmodel, **attribs) if self.frequencies != "estimate": xml.parameter( freq, text=freq_string, dimension=self.valuecounts[feature], id="feature_frequencies.s:%s" % fname, name="frequencies")
def add_state_nodes(self, beastxml): """ Add tree-related <state> sub-elements. """ state = beastxml.state self.tree = xml.tree(state, id=self.tree_id, name="stateNode") xml.taxonset(self.tree, idref="taxa") if self.type in ["yule", "birthdeath"]: param = xml.parameter(state, id="birthRate.t:beastlingTree", name="stateNode") if self.birthrate_estimate is not None: param.text = str(self.birthrate_estimate) else: param.text = "1.0" if self.type in ["birthdeath"]: xml.parameter(beastxml.state, text="0.5", id="deathRate.t:beastlingTree", name="stateNode") xml.parameter(beastxml.state, text="0.2", id="sampling.t:beastlingTree", name="stateNode") elif self.type == "coalescent": xml.parameter(beastxml.state, text="1.0", id="popSize.t:beastlingTree", name="stateNode") if beastxml.config.tip_calibrations: self.add_tip_heights(beastxml.config.tip_calibrations)
def add_prior(self, prior): BaseClock.add_prior(self, prior) # Gamma prior over rates sub_prior = xml.prior(prior, id="RandomRatesPrior.c:%s" % self.name, name="distribution", x="@clockrates.c:%s" % self.name) xml.Gamma(sub_prior, id="RandomRatesPrior:%s" % self.name, name="distr", alpha="@%s" % self.shape_id, beta="@%s" % self.scale_id) # Exponential prior over Gamma scale parameter # (mean param copied from rate heterogeneity implementation in BaseModel) if self.estimate_variance: sub_prior = xml.prior(prior, id="randomClockGammaScalePrior.s:%s" % self.name, name="distribution", x="@%s" % self.shape_id) xml.Exponential(sub_prior, id="randomClockGammaScalePriorExponential.s:%s" % self.name, mean="0.23", name="distr") # Poisson prior over number of rate changes sub_prior = xml.prior(prior, id="RandomRateChangesPrior.c:%s" % self.name, name="distribution") xml.x(sub_prior, id="RandomRateChangesCount:%s" % self.name, spec="util.Sum", arg="@Indicators.c:%s" % self.name) poisson = xml.distr(sub_prior, id="RandomRatechangesPoisson.c:%s" % self.name, spec="beast.math.distributions.Poisson") xml.parameter(poisson, text="0.6931471805599453", id="RandomRateChangesPoissonLambda:%s" % self.name, estimate=False, name="lambda")
def add_operators(self): """ Add all <operator> elements. """ self.add_tree_operators() for clock in self.config.clocks: clock.add_operators(self.run) for model in self.config.all_models: model.add_operators(self.run) # Add one DeltaExchangeOperator for feature rates per clock for clock in self.config.clocks: clock_models = [ m for m in self.config.models if m.rate_variation and m.clock == clock ] if not clock_models: continue # Add one big DeltaExchangeOperator which operates on all # feature clock rates from all models delta = xml.operator(self.run, id="featureClockRateDeltaExchanger:%s" % clock.name, spec="DeltaExchangeOperator", weight="3.0") for model in clock_models: xml.parameter(xml.plate(delta, var="rate", range=model.all_rates), idref="featureClockRate:%s:$(rate)" % model.name) # Add weight vector if there has been any binarisation if any([ w != 1 for w in itertools.chain( *[m.weights for m in clock_models]) ]): xml.weightvector( delta, text=" ".join( itertools.chain( *[map(str, m.weights) for m in clock_models])), id="featureClockRateWeightParameter:%s" % clock.name, spec="parameter.IntegerParameter", dimension=str(sum([len(m.weights) for m in clock_models])), estimate="false")
def add_state_nodes(self, beastxml): """ Add tree-related <state> sub-elements. """ super().add_state_nodes(beastxml) state = beastxml.state param = xml.parameter(state, id="birthRate.t:beastlingTree", name="stateNode") if self.birthrate_estimate is not None: param.text=str(self.birthrate_estimate) else: param.text="1.0"
def _add_prior(self, prior, name): switch_prior = xml.prior( prior, id="%s:pdcovarion_s_prior.s" % name, name="distribution", x="@%s:pdcovarion_s.s" % name) gamma = xml.Gamma(switch_prior, id="%s: Gamma.0" % name, name="distr") xml.parameter( gamma, text="0.05", id="%s:pdcovarion_switch_gamma_param1" % name, name="alpha", lower="0.0", upper="0.0") xml.parameter( gamma, text="10.0", id="%s:pdcovarion_switch_gamma_param2" % name, name="beta", lower="0.0", upper="0.0") origin_prior = xml.prior( prior, id="%s:pdcovarion_origin_prior.s" % name, name="distribution", x="@%s:pdcovarion_origin.s" % name) xml.Uniform( origin_prior, id="%s:PDCovOriginUniform" % name, name="distr", upper="Infinity") death_prior = xml.prior( prior, id="%s:pdcovarion_death_prior.s" % name, name="distribution", x="@{:}:pdcovarion_death.s".format(name)) xml.Exponential( death_prior, id="%s:PDCovDeathExp" % name, name="distr", mean="1.0")
def add_prior(self, prior): RelaxedClock.add_prior(self, prior) if self.estimate_variance: # Gamma prior on the standard deviation for lognormal dist sub_prior = xml.prior(prior, id="ucldSdev:%s" % self.name, name="distribution", x="@ucldSdev.c:%s" % self.name) gamma = xml.Gamma(sub_prior, id="uclSdevPrior:%s" % self.name, name="distr") xml.parameter(gamma, text="0.5396", id="uclSdevPriorAlpha:%s" % self.name, estimate="false", name="alpha") xml.parameter(gamma, text="0.3819", id="uclSdevPriorBeta:%s" % self.name, estimate="false", name="beta")
def add_state(self, state): BinaryModel.add_state(self, state) for fname in self.parameter_identifiers(): # One param for all features xml.parameter( state, text="0.5 0.5", id="%s:pdcovarion_s.s" % fname, lower="1.0E-4", name="stateNode", dimension="2", upper="Infinity") xml.parameter( state, text="10", id="%s:pdcovarion_origin.s" % fname, lower="1", name="stateNode", upper="Infinity") xml.parameter( state, text="1.0 0.1", id="%s:pdcovarion_death.s" % fname, lower="1.0E-4", name="stateNode", dimension="2", upper="1.0")
def add_data(self, distribution): """ Add <data> element corresponding to the indicated feature, descending from the indicated likelihood distribution. """ data = xml.data(distribution, id="locationData", spec="sphericalGeo.AlignmentFromTraitMap") traitmap = xml.traitMap(data, id="geographyTraitmap", spec="sphericalGeo.TreeTraitMap", initByMean="true", randomizelower="-90 -180", randomizeupper="90 180", traitName="location", tree="@Tree.t:beastlingTree") xml.parameter(traitmap, text="0.0 0.0", id="locationParameter", spec="sphericalGeo.LocationParameter", dimension=2 * (2 * len(self.config.languages.languages) - 1), minordimension="2") loc_data_text_bits = [] for lang in self.config.languages.languages: lat, lon = self.config.locations.get(lang, ("?", "?")) if "?" in (lat, lon): if lang not in self.sampling_points: self.sampling_points.append(lang) log.info( "Location of language %s will be sampled. You may wish to add a prior." % lang, model=self) else: bit = "%s=%.2f %.2f" % (lang, lat, lon) loc_data_text_bits.append(bit) traitmap.text = ",\n".join(loc_data_text_bits) xml.userDataType(data, id="LocationDataType", spec="sphericalGeo.LocationDataType")
def add_operators(self, run): """ Add <operators> for individual feature substitution rates if rate variation is configured. """ if self.frequencies == "estimate": self.add_frequency_operators(run) if self.rate_variation: # UpDownOperator to scale the Gamma distribution for this model's # feature rates updown = xml.operator(run, id="featureClockRateGammaUpDown:%s" % self.name, spec="UpDownOperator", scaleFactor="0.5", weight="0.3") xml.parameter(updown, idref="featureClockRateGammaShape:%s" % self.name, name="up") xml.parameter(updown, idref="featureClockRateGammaScale:%s" % self.name, name="down")
def add_state(self, state): BaseClock.add_state(self, state) xml.stateNode(state, text=False, id="Indicators.c:%s" % self.name, spec="parameter.BooleanParameter", dimension=42) xml.stateNode(state, text="0.1", id="clockrates.c:%s" % self.name, spec="parameter.RealParameter", dimension=42) self.shape_id = "randomClockGammaShape:%s" % self.name # Domain and initial values for Gamma params copied from rate heterogeneity # implementation in BaseModel xml.parameter(state, text="5.0", id=self.shape_id, name="stateNode", lower="1.1", upper="1000.0") self.scale_id = "randomClockGammaScale:%s" % self.name xml.parameter(state, text="0.2", id=self.scale_id, name="stateNode")
def add_state(self, state): """Construct the model's state nodes. Add parameters for Gamma-distributed rate heterogenetiy, if configured. """ if self.frequencies == "estimate": self.add_frequency_state(state) if self.rate_variation: if self.feature_rates: # If user specified rates have been provided for either # features or partitions, we need to list each rate # individually for rate in self.all_rates: xml.parameter(state, text=self.feature_rates.get(rate, 1.0), id="featureClockRate:%s:%s" % (self.name, rate), name="stateNode") else: # If not, and everything is initialised to the same # value, we can just whack 'em all in a big plate plate = xml.plate(state, var="rate", range=self.all_rates) xml.parameter(plate, text="1.0", id="featureClockRate:%s:$(rate)" % self.name, name="stateNode") # Give Gamma shape parameter a finite domain # Must be > 1.0 for the distribution to be bell-shaped, # rather than L-shaped. The domain [1.1,1000] limits feature # rate variation to the realms of vague plausibity xml.parameter(state, text="5.0", id="featureClockRateGammaShape:%s" % self.name, lower="1.1", upper="100.0", name="stateNode") # Gamma scale parameter's domain is defined *implicilty* # by the fact that the operators maintain shape*scale = 1.0 xml.parameter(state, text="0.2", id="featureClockRateGammaScale:%s" % self.name, name="stateNode")
def add_operators(self, run): BaseClock.add_operators(self, run) # Operate on indicators xml.operator(run, id="IndicatorsBitFlip.c:%s" % self.name, spec="BitFlipOperator", parameter="@Indicators.c:%s" % self.name, weight="15.0") # Operate on branch rates xml.operator(run, id="ClockRateScaler.c:%s" % self.name, spec="ScaleOperator", parameter="@clockrates.c:%s" % self.name, scaleFactor="0.5", weight="15.0") # Up/down for Gamma params if self.estimate_variance: updown = xml.operator(run, id="randomClockGammaUpDown:%s" % self.name, spec="UpDownOperator", scaleFactor="0.5", weight="1.0") xml.parameter(updown, idref=self.shape_id, name="up") xml.parameter(updown, idref=self.scale_id, name="down")
def add_state(self, state): BaseModel.add_state(self, state) for f in self.features: fname = "%s:%s" % (self.name, f) N = self.valuecounts[f] dimension = N*(N-1) if self.symmetric: dimension = int(dimension/2) xml.stateNode( state, text="true", id="rateIndicator.s:%s" % fname, spec="parameter.BooleanParameter", dimension=dimension) xml.parameter( state, text="1.0", id="relativeGeoRates.s:%s" % fname, name="stateNode", dimension=dimension)
def add_substmodel(self, sitemodel, feature, fname): # If we're sharing one substmodel across all features and have already # created it, just reference it and that's it if self.share_params and self.subst_model_id: sitemodel.set("substModel", "@%s" % self.subst_model_id) return # Otherwise, create a substmodel name = self.name if self.share_params else fname subst_model_id = "covarion.s:%s" % name if self.share_params: self.subst_model_id = subst_model_id substmodel = xml.substModel(sitemodel, id=subst_model_id, spec="BinaryCovarion", alpha="@covarion_alpha.s:%s" % name, switchRate="@covarion_s.s:%s" % name) # Numerical instability is an issue with this model, so we give the # option of using a more robust method of computing eigenvectors. if self.use_robust_eigensystem: substmodel.set( "eigenSystem", "beast.evolution.substitutionmodel.RobustEigenSystem") # The "vfrequencies" parameter here is the frequencies # of the *visible* states (present/absent) and should # be based on the data (if we are doing an empirical # analysis) if self.frequencies == "estimate": substmodel.set("vfrequencies", "@freqs_param.s:%s" % name) else: vfreq = xml.vfrequencies(substmodel, id="%s:visiblefrequencies.s" % name, dimension="2", spec="parameter.RealParameter") if self.frequencies == "empirical": if self.share_params: vfreq.text = self.build_freq_str() else: vfreq.text = self.build_freq_str(feature) else: vfreq.text = "0.5 0.5" # These are the frequencies of the *hidden* states # (fast / slow), and are just set to 50:50. They could be estimated, # in principle, but this seems to lead to serious instability problems # so we don't expose that possibility to the user. xml.parameter(substmodel, text="0.5 0.5", id="%s:hiddenfrequencies.s" % name, dimension="2", lower="0.0", name="hfrequencies", upper="1.0") # Dummy frequencies - these do nothing and are required # to stop the BinaryCovarion model complaining that the # "frequencies" input is not specified, which is # inherited behaviour from GeneralSubstitutionModel # which probably should have been overridden... xml.frequencies(substmodel, id="%s:dummyfrequences.s" % name, spec="Frequencies", frequencies="0.5 0.5")
def add_state_nodes(self, beastxml): super().add_state_nodes(beastxml) xml.parameter(beastxml.state, text="1.0", id="popSize.t:beastlingTree", name="stateNode")
def add_operators(self, beastxml): """ Add all <operator>s which act on the tree topology and branch lengths. """ """ Add all <operator>s which act on the tree topology and branch lengths. """ # Tree operators # Operators which affect the tree must respect the sample_topology and # sample_branch_length options. if beastxml.config.languages.sample_topology: ## Tree topology operators xml.operator(beastxml.run, attrib={ "id": "SubtreeSlide.t:beastlingTree", "spec": "SubtreeSlide", "tree": "@Tree.t:beastlingTree", "markclades": "true", "weight": "15.0" }) xml.operator(beastxml.run, attrib={ "id": "narrow.t:beastlingTree", "spec": "Exchange", "tree": "@Tree.t:beastlingTree", "markclades": "true", "weight": "15.0" }) xml.operator(beastxml.run, attrib={ "id": "wide.t:beastlingTree", "isNarrow": "false", "spec": "Exchange", "tree": "@Tree.t:beastlingTree", "markclades": "true", "weight": "3.0" }) xml.operator(beastxml.run, attrib={ "id": "WilsonBalding.t:beastlingTree", "spec": "WilsonBalding", "tree": "@Tree.t:beastlingTree", "markclades": "true", "weight": "3.0" }) if beastxml.config.languages.sample_branch_lengths: ## Branch length operators xml.operator(beastxml.run, attrib={ "id": "UniformOperator.t:beastlingTree", "spec": "Uniform", "tree": "@Tree.t:beastlingTree", "weight": "30.0" }) xml.operator(beastxml.run, attrib={ "id": "treeScaler.t:beastlingTree", "scaleFactor": "0.5", "spec": "ScaleOperator", "tree": "@Tree.t:beastlingTree", "weight": "3.0" }) xml.operator(beastxml.run, attrib={ "id": "treeRootScaler.t:beastlingTree", "scaleFactor": "0.5", "spec": "ScaleOperator", "tree": "@Tree.t:beastlingTree", "rootOnly": "true", "weight": "3.0" }) ## Up/down operator which scales tree height if self.type in ["yule", "birthdeath"]: updown = xml.operator(beastxml.run, attrib={ "id": "UpDown", "spec": "UpDownOperator", "scaleFactor": "0.5", "weight": "3.0" }) xml.tree(updown, idref="Tree.t:beastlingTree", name="up") xml.parameter(updown, idref="birthRate.t:beastlingTree", name="down") ### Include clock rates in up/down only if calibrations are given if beastxml.config.calibrations: for clock in beastxml.config.clocks: if clock.estimate_rate: xml.parameter(updown, idref=clock.mean_rate_id, name="down") if self.type in ["yule", "birthdeath"]: # Birth rate scaler # Birth rate is *always* scaled. xml.operator(beastxml.run, attrib={ "id": "YuleBirthRateScaler.t:beastlingTree", "spec": "ScaleOperator", "parameter": "@birthRate.t:beastlingTree", "scaleFactor": "0.5", "weight": "3.0" }) elif self.type == "coalescent": xml.operator(beastxml.run, attrib={ "id": "PopulationSizeScaler.t:beastlingTree", "spec": "ScaleOperator", "parameter": "@popSize.t:beastlingTree", "scaleFactor": "0.5", "weight": "3.0" }) if self.type in ["birthdeath"]: xml.operator(beastxml.run, attrib={ "id": "SamplingScaler.t:beastlingTree", "spec": "ScaleOperator", "parameter": "@sampling.t:beastlingTree", "scaleFactor": "0.8", "weight": "1.0" }) xml.operator(beastxml.run, attrib={ "id": "DeathRateScaler.t:beastlingTree", "spec": "ScaleOperator", "parameter": "@deathRate.t:beastlingTree", "scaleFactor": "0.5", "weight": "3.0" }) # Add a Tip Date scaling operator if required if beastxml.config.tip_calibrations and beastxml.config.languages.sample_branch_lengths: # Get a list of taxa with non-point tip cals tip_taxa = [ next(cal.langs.__iter__()) for cal in beastxml.config.tip_calibrations.values() if cal.dist != "point" ] for taxon in tip_taxa: tiprandomwalker = xml.operator(beastxml.run, attrib={ "id": "TipDatesandomWalker:%s" % taxon, "spec": "TipDatesRandomWalker", "windowSize": "1", "tree": "@Tree.t:beastlingTree", "weight": "3.0", }) beastxml.add_taxon_set(tiprandomwalker, taxon, (taxon, ))