/** * Creates an object to simulate data for a given set of models, a tree, parameters and unobserved * states. A different model can be given for each rate class. * * @param m Map from site class to model * @param t The tree * @param p The parameters * @param unobserved The unobserved states * @throws Models.RateCategory.RateException Thrown if there is an issue with a rate category in * the model (e.g. a badly formatted rate). * @throws Models.Model.ModelException Thrown if there is a problem with the model (e.g. the rate * categories differ in their states) * @throws TreeException Thrown if there is a problem with the tree. * @throws Parameters.Parameters.ParameterException Thrown if there is a problem with the * parameters (e.g. a required parameter is not present) */ public Simulate(Map<String, Model> m, Tree t, Parameters p, Alignment unobserved) throws RateException, ModelException, TreeException, ParameterException { P = new HashMap<>(); for (Entry<String, Model> e : m.entrySet()) { P.put(e.getKey(), new Probabilities(e.getValue(), t, p)); } this.missing = unobserved; this.t = new HashMap<>(); for (String s : m.keySet()) { this.t.put(s, t); } random = new Random(); // If the parameters setting doesn't include branch lengths parameters then // add them from the tree. The paramter / branch length interaction is a // bit counter-inutative and probably needs changing but in the mean time // this is here to make errors less likely. for (Branch b : t) { if (!p.hasParam(b.getChild())) { p.addParameter(Parameter.newFixedParameter(b.getChild(), b.getLength())); } } }
/** * Gets a simulated site. Recodes the simulated data before returning. For example if both "A" and * "B" are unobserved states representing an observed state of "0" then this can be used to change * "A" and "B" to zero. This is necessary as the simulator generates unobserved states by default. * The returned site will have ambiguous data set as appropriate. * * @param recode Map of recodings * @param internal Whether to return the state of the internal nodes * @param siteClass The site class to simulate for * @return The simulated site * @throws Simulations.Simulate.SimulationException Thrown if there is a problem with the * simulation (currently only if attempting to simulate for a site class we don't have a model * for) * @throws Models.RateCategory.RateException if a rate category uses the FitzJohn method at the * root as this method requires likelihoods to calculate the frequency and we don't have the * likelihoods when simulating. */ public Site getSite(boolean internal, Map<String, String> recode, String siteClass) throws SimulationException, RateException { if (!P.containsKey(siteClass)) { throw new SimulationException("No model defined for requested class"); } if (!t.containsKey(siteClass)) { throw new SimulationException("No tree defined for requested class"); } Site site, loSite; do { HashMap<String, String> assign = new HashMap<>(); RateCategory r = getRandomRate(P.get(siteClass).getRateCategory(), siteClass); // Assign the root assign.put(t.get(siteClass).getRoot(), getRandomStart(r, siteClass)); // Traverse the tree, assign values to nodes for (Branch b : t.get(siteClass).getBranchesReversed()) { assign.put(b.getChild(), getRandomChar(r, b, assign.get(b.getParent()), siteClass)); } // Done like this so things are in a sensible order if written out // Keeps a leaf only and all nodes copy. LinkedHashMap<String, String> all = new LinkedHashMap<>(); LinkedHashMap<String, String> lo = new LinkedHashMap<>(); for (String l : t.get(siteClass).getLeaves()) { all.put(l, assign.get(l)); lo.put(l, assign.get(l)); } for (String i : t.get(siteClass).getInternal()) { all.put(i, assign.get(i)); } // This deals with recoding as discussed in the javadoc. If there // is none simply ceate the site if (recode == null) { site = new Site(all, siteClass); loSite = new Site(lo, siteClass); } else { // Else make an ambiguous data structure Map<String, Set<String>> ambig = new HashMap<>(); // Step through the recodings and add the apropiate date to // ambig for (Entry<String, String> e : recode.entrySet()) { if (!ambig.containsKey(e.getValue())) { ambig.put(e.getValue(), new HashSet<String>()); } ambig.get(e.getValue()).add(e.getKey()); } // Create the sites site = new Site(all, new Ambiguous(ambig), siteClass); loSite = new Site(lo, new Ambiguous(ambig), siteClass); // Now recode them site = site.recode(recode); loSite = loSite.recode(recode); } } // While the site is missing generate another site while (isMissing(loSite)); if (internal) { return site; } else { return loSite; } }