def _construct(self): # We create the database on the analysis path --- oooh! Clever. if not self.path.exists(): self._create_path(self.path) if self.path != self.analysis_path: if not self.analysis_path.exists(): self._create_path(self.analysis_path) self.database = Database(self.analysis_path) self.database.create() self._init_db()
class Experiment(object): def __init__(self, path, treatments, name=None, seed=1, analysis_path=None, full=True): logtools.set_logging() if name is None: name = _make_name_from_program() self.path = _construct_path(path, name) if analysis_path: self.analysis_path = _construct_path(analysis_path, name) else: self.analysis_path = self.path self.next_treatment_seq = 0 if full: self.lineage_class = FullLineage else: self.lineage_class = SnapshotLineage self.seed = seed self.rng = random.Random(seed) self.user_interrupt = False self.treatments = [] for t in treatments: assert isinstance(t, Treatment) self.treatments.append(t) t._bind(self) self._construct() def _construct(self): # We create the database on the analysis path --- oooh! Clever. if not self.path.exists(): self._create_path(self.path) if self.path != self.analysis_path: if not self.analysis_path.exists(): self._create_path(self.analysis_path) self.database = Database(self.analysis_path) self.database.create() self._init_db() def get_replicate(self, t_id, r_id): t_idx = t_id - 1 r_idx = r_id - 1 if t_id < 1 or t_id > len(self.treatments): raise ExperimentError("No treatment {}".format(str(t_id))) tr = self.treatments[t_idx] assert isinstance(tr, Treatment) assert tr.seq == t_id if r_id < 1 or r_id > len(tr.replicates): raise ExperimentError("No Replicate {}".format(str(r_id))) rep = tr.replicates[r_idx] assert isinstance(rep, Replicate) assert rep.seq == r_id return rep def get_next_treatment_seq(self): self.next_treatment_seq += 1 return self.next_treatment_seq def _create_path(self, path): if not path.parent.exists(): raise ExperimentError("No parent path: {}".format(str(path.parent))) log.info("Creating path {}".format(str(path))) path.mkdir(parents=True) def _remove_paths(self): if self.path.exists(): _remove_folder(self.path) if self.path is not self.analysis_path: if self.analysis_path.exists(): _remove_folder(self.analysis_path) self.analysis_path.mkdir(parents=True) def _init_db(self): # Use merge to allow for nice things to happen # TODO: We don't deal with the deletion of old records if the # replicate number are changed for t in self.treatments: self.database.session.merge(TreatmentRecord(t)) for r in t.replicates: self.database.session.merge(ReplicateRecord(r)) # TODO: we should really update the status of this stuff too? self.database.session.commit() def iter_lineages(self, readonly=False, skip_errors=True): for t in self.treatments: for r in t.replicates: with r.get_lineage(readonly=readonly) as lin: yield r, lin def run(self, overwrite=False, verbose=False, dry=False, only_treatment=None, only_replicate=None): """Run the experiment.""" if overwrite: self._remove_paths() self._construct() if dry: log.info("Dry run -- exiting without simulating") return # Now run log.info("Beginning experiment in {}".format(str(self.path))) for treat in self.treatments: if only_treatment is not None and treat != only_treatment: continue if not self.user_interrupt: treat.run(only_replicate) if self.user_interrupt: log.info("User interrupted --- quitting") def visit_lineages(self, visitor, only_treatment=None, only_replicate=None): for treat in self.treatments: if only_treatment is not None and treat != only_treatment: continue for rep in treat.replicates: if only_replicate is not None and rep != only_replicate: continue with rep.get_lineage() as lin: visitor.visit_lineage(rep, lin) def visit_generations(self, visitor, every=1, only=None, only_treatment=None, only_replicate=None): """Try and visit everything with the least amount of loading""" for treat in self.treatments: if only_treatment is not None and treat != only_treatment: continue for rep in treat.replicates: if only_replicate is not None and rep != only_replicate: continue with rep.get_lineage() as lin: visitor.visit_lineage(rep, lin) if only is not None: only = int(only) if visitor.wants_generation(only): pop = lin.get_generation(only) visitor.visit_generation(only, pop) else: # Now iterate through the generations gen_num = 0 while gen_num <= lin.generation: # Check if the visitor wants this generation (as loading is # expensive) if visitor.wants_generation(gen_num): pop = lin.get_generation(gen_num) visitor.visit_generation(gen_num, pop) gen_num += every if hasattr(visitor, 'leave_lineage'): visitor.leave_lineage(rep, lin) def find_matching(self, text, repnum): # Default to ALL matching_treatment = None matches = [] if text == "": # If they've selected a replicate too, then we need a treatment. if repnum >= 1: if len(self.treatments) > 1: raise ExperimentError("No treatment supplied and more than one is possible.") # If they've supplied a replicate... else: # Otherwise just match the only treament matches.append(self.treatments[0]) else: matches = [None] else: look = text.lower() len_look = len(text) for t in self.treatments: current = t.name.lower() if len_look <= len(current): if look == current[:len_look]: if len(current) == len_look: matches = [t] break else: matches.append(t) if not matches: raise ExperimentError("No match for {}.".format(text)) if len(matches) > 1: raise Experiment("More than one match for {}.".format(text)) # Ok. Grab the match. It may be None. matching_treatment = matches[0] if repnum >= 1: matching_replicate = matching_treatment.with_replicate_id(repnum) else: matching_replicate = None return matching_treatment, matching_replicate