def launchSimulation(self, path, mutbound=True): folderName = path.split('/')[-1] fitfun = folderName.split('_')[0] populationInstance = Pop(fit_fun=fitfun, inst=path, mutationBoundaries=mutbound) populationInstance.runSimulation()
def test_deme_number_of_leaders_is_number_of_individuals_with_that_role( self, pseudorandom, getFitnessParameters): pseudorandom(0) self.pop = Pop(fit_fun='socialclass', inst='test/test') self.pop.fitnessParameters = getFitnessParameters('socialclass') self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 10 self.pop.initialPhenotypes = [0.1, 0.2, 0.3, 0.6] self.pop.migrationRate = 0 self.pop.mutationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() leaderCountPerDeme = [0] * self.pop.numberOfDemes for ind in self.pop.individuals: leaderCountPerDeme[ind.currentDeme] += ind.leader for deme in range(self.pop.numberOfDemes): nl = self.pop.demes[deme].numberOfLeaders assert nl != 10, "all individuals in deme have been elected leaders when proportion should be about: " + self.pop.initialPhenotypes[ 3] assert leaderCountPerDeme[ deme] == nl, "deme counts {0} leaders when there are {1}".format( nl, leaderCountPerDeme[deme])
def test_consensus_takes_time_to_reach(self): self.pop = Pop(inst='test/test') self.pop.fit_fun = 'debate' self.pop.initialPhenotypes = [0.1, 0.2, 0.3, 0.4] self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 2 self.pop.mutationRate = 0 self.pop.migrationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() for deme in self.pop.demes: assert deme.politicsValues[ "consensusTime"] is not None, "consensus reaching time has not been calculated" assert deme.politicsValues[ "consensusTime"] > 0, "consensus reaching should take a bare minimum of time" disagreement = self.pop.fitnessParameters[ "aconsensus"] * deme.demography * deme.varPhenotypes[2] assert deme.politicsValues[ "consensusTime"] == self.pop.fitnessParameters[ "epsilon"] + disagreement / ( self.pop.fitnessParameters["bconsensus"] + disagreement)
def test_fertility_depends_on_public_good(self, getFitnessParameters): fitfun = 'policing' self.fakepop = Pop(fit_fun=fitfun, inst='test/test') self.fakepop.fitnessParameters = getFitnessParameters(fitfun) self.fakepop.numberOfDemes = 2 self.fakepop.initialDemeSize = 1 self.fakepop.individualResources = 4 self.fakepop.initialPhenotypes = [0.2, 0.3] self.fakepop.numberOfPhenotypes = len(self.fakepop.initialPhenotypes) self.fakepop.migrationRate = 0 self.fakepop.mutationRate = 0 self.fakepop.createAndPopulateDemes() self.fakepop.clearDemeInfo() self.fakepop.populationMutationMigration() for ind in range(self.fakepop.demography): indiv = self.fakepop.individuals[ind] if indiv.currentDeme == 0: self.fakepop.fitnessParameters.update({"pg": 1}) indiv.fertility(self.fakepop.fit_fun, **self.fakepop.fitnessParameters) fertility0 = indiv.fertilityValue elif indiv.currentDeme == 1: self.fakepop.fitnessParameters.update({"pg": 3}) indiv.fertility(self.fakepop.fit_fun, **self.fakepop.fitnessParameters) fertility1 = indiv.fertilityValue assert fertility0 < fertility1 gc.collect()
def test_migrants_destinations_equally_likely_as_in_uniform_distribution( self, pseudorandom, instantiateSingleIndividualsDemes): pseudorandom(69) self.fakepop = Pop(inst='test/test') self.ds = 100 self.nd = 10 self.fakepop.createAndPopulateDemes(nDemes=self.nd, dSize=self.ds) destinations = [] for ind in range(self.fakepop.demography): indiv = self.fakepop.individuals[ind] indiv.migrate(nDemes=self.fakepop.numberOfDemes, migRate=1) destinations.append(indiv.destinationDeme) observedCountUnsorted = Counter(destinations) observedCount = [] for key, val in sorted(observedCountUnsorted.items()): observedCount.append(val) expectedCount = [self.ds for i in range(self.nd)] chisq, pval = scistats.chisquare(observedCount, expectedCount) assert len(expectedCount) == len( observedCount), "len obs = {0}, len exp = {1}".format( len(observedCount), len(expectedCount)) assert pval > 0.05, "Test for goodness of fit failed: obs = {0}, exp = {1}".format( observedCount, expectedCount) gc.collect()
def test_all_phenotype_means_in_output(self): self.ntraits = 4 with open('test/test/' + INITIAL_PHENOTYPES_FILE, "w") as f: for i in range(self.ntraits): f.write('{0}\n'.format(i / self.ntraits)) self.fakepop = Pop(inst='test/test') self.fakepop.numberOfDemes = 3 self.fakepop.initialDemeSize = 2 self.fakepop.numberOfGenerations = 5 self.fakepop.createAndPopulateDemes() assert self.fakepop.numberOfPhenotypes == self.ntraits, "uh-oh, the test did not change the number of phenotypes" self.fakepop.runSimulation() with open('test/test/out_phenotypes.txt') as fp: firstline = fp.readline() phenotypeMeans = firstline.split(',') ### only take half of the line, since the second half is the variance in phenotypes, not the mean assert len( phenotypeMeans ) / 2 == self.ntraits, "Simulation returns mean of {0} phenotypes instead of {1}".format( len(phenotypeMeans), self.ntraits) os.remove('test/test/out_phenotypes.txt') os.remove('test/test/out_demography.txt')
def test_demography_file_has_correct_output(self, pseudorandom, runSim, clearOutputFiles): pseudorandom(69) self.pop = Pop(inst='test/test') self.pop.mutationRate = 0.1 self.pop.numberOfDemes = 5 self.pop.initialDemeSize = 8 self.pop.fitnessParameters.clear() self.pop.fitnessParameters.update({ "fb": 10, "b": 0.5, "c": 0.05, "gamma": 0.01 }) self.pop.createAndPopulateDemes() expDemog = [] for gen in range(5): self.pop.lifecycle() assert self.pop.numberOfDemes == 5 expDemog.append(self.pop.demography / self.pop.numberOfDemes) pseudorandom(69) runSim() obsDemog = fman.extractColumnFromFile('test/test/out_demography.txt', 0, float) assert obsDemog == expDemog, "unexpected value of group size has been printed" clearOutputFiles('test/test/')
def test_group_labour_force_is_calculated_and_given_to_individual_instance( self): self.pop = Pop(fit_fun='technology', inst='test/test') self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 20 self.pop.createAndPopulateDemes() self.deme = self.pop.demes[0] assert hasattr(self.deme, "progressValues"), "make dict" assert type(self.deme.progressValues) is dict progressKeys = ["fine", "investmentReward"] for key in progressKeys: assert key in self.deme.progressValues self.pop.clearDemeInfo() for pheno in self.pop.demes[0].meanPhenotypes: assert pheno is not None, "none phenotypes before migration" self.pop.populationMutationMigration() for pheno in self.pop.demes[0].meanPhenotypes: assert pheno is not None, "none phenotypes before update" self.pop.updateDemeInfoPreProduction() self.pop.populationProduction() self.pop.updateDemeInfoPostProduction() self.demeAFTER = self.pop.demes[0] for key in progressKeys: assert self.demeAFTER.progressValues[key] is not None # deme labour force = total private time: (demography - nleaders)(1-T1) + nleaders(1-T2) # where T1 and T2 is effective time spent in debate by civilian and leader respectively gc.collect()
def test_technology_updates_with_correct_number(self): self.pop = Pop(fit_fun='technology', inst='test/test') self.pop.numberOfDemes = 2 self.pop.initialDemeSize = 10 self.pop.fit_fun = 'technology' self.pop.initialPhenotypes = [0.5] * 4 self.pop.createAndPopulateDemes() assert self.pop.demes[ 0].technologyLevel == self.pop.initialTechnologyLevel, "wrong technology level assigned to deme when created" self.pop.clearDemeInfo() assert self.pop.demes[ 0].technologyLevel == self.pop.initialTechnologyLevel, "wrong technology level after first clearing" self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() self.pop.populationProduction() self.pop.updateDemeInfoPostProduction() # calculate new technology level as it should be publicGood = self.pop.demes[0].publicGood tech = self.pop.demes[0].technologyLevel tech_new = tech * (self.pop.fitnessParameters['atech'] + ((1 - self.pop.fitnessParameters['p']) * publicGood) **(1 - self.pop.fitnessParameters['betaTech'])) / ( 1 + self.pop.fitnessParameters['btech'] * tech) self.pop.populationReproduction() self.pop.clearDemeInfo() assert self.pop.demes[ 0].technologyLevel == tech_new, "wrong value for new technology level." gc.collect()
def test_deme_gets_number_of_leaders(self, pseudorandom, getFitnessParameters): pseudorandom(10) self.pop = Pop(fit_fun='socialclass', inst='test/test') self.pop.fitnessParameters = getFitnessParameters('socialclass') self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 1000 self.pop.initialPhenotypes = [0.1, 0.2, 0.3, 0.6] self.pop.migrationRate = 0 self.pop.mutationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() plead = self.pop.initialPhenotypes[3] for deme in self.pop.demes: nlead = deme.numberOfLeaders assert nlead is not None assert type(nlead) is int assert nlead >= 0 assert pytest.approx(nlead / deme.demography, 0.1) == plead stat1, pval1 = scistats.ttest_1samp( [1] * nlead + [0] * (deme.demography - nlead), plead) #assert pval1 > 0.05, "T-test mean failed. Observed: {0}, Expected: {1}".format(nlead/deme.demography, plead) self.test = scistats.binom_test(nlead, deme.demography, plead, alternative="two-sided") assert self.test > 0.05, "Success rate = {0} when proportion of leaders = {1}".format( nlead / deme.demography, plead) gc.collect()
def test_consensus_value_defines_policing_amount(self): self.pop = Pop(inst='test/test') self.pop.fit_fun = 'debate' self.pop.initialPhenotypes = [0.1, 0.2, 0.3, 0.4] self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 2 self.pop.mutationRate = 0 self.pop.migrationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() self.pop.populationProduction() self.pop.updateDemeInfoPostProduction() allres = [0] * self.pop.numberOfDemes for ind in self.pop.individuals: allres[ind.currentDeme] += ind.resourcesAmount for deme in self.pop.demes: assert deme.progressValues["institutionQuality"] is not None assert deme.progressValues["institutionQuality"] == ( deme.politicsValues['consensus'] * deme.publicGood * self.pop.fitnessParameters['aquality'] / allres[deme.id])**self.pop.fitnessParameters['alphaquality'] assert deme.progressValues["fineBudget"] is not None assert deme.progressValues["fineBudget"] == deme.politicsValues[ 'consensus'] * deme.publicGood * ( 1 - self.pop.fitnessParameters['aquality'])
def test_demes_are_populated(self): self.pop = Pop(inst='test/test') self.pop.createAndPopulateDemes() for deme in self.pop.demes: assert deme.demography is not None, "Deme {0} is not populated".format( deme) gc.collect()
def test_population_contains_demes(self): self.pop = Pop(inst='test/test') self.pop.createAndPopulateDemes() assert hasattr(self.pop, "demes"), "This population has no deme yet!" for deme in self.pop.demes: assert type(deme) is Dem gc.collect()
def test_simulation_stops_if_population_extinct(self): self.fakepop = Pop(inst='test/test') self.fakepop.numberOfDemes = 10 self.fakepop.numberOfGenerations = 10 self.fakepop.fitnessParameters[ "fb"] = 0.0001 # to make the population die out self.fakepop.createAndPopulateDemes() assert self.fakepop.demography == self.fakepop.numberOfDemes * self.fakepop.initialDemeSize
def test_population_has_fitness_method_or_pgg_parameters(self): self.fakepop = Pop(inst='test/test') assert hasattr(self.fakepop, "fitnessParameters"), "Provide fitness parameters" if not hasattr(self.fakepop, "fitnessMethod"): for key in ["fb", "b", "c", "gamma"]: assert key in self.fakepop.fitnessParameters, "PGG parameter {0} not provided".format( key)
def test_migration_is_ran_at_the_population_level(self): self.fakepop = Pop(inst='test/test') self.nd = self.fakepop.numberOfDemes self.fakepop.createAndPopulateDemes(self.nd, 10) assert hasattr(self.fakepop, "populationMutationMigration" ), "Migration cannot be ran at the population level" assert callable(self.fakepop.populationMutationMigration) gc.collect()
def test_initial_deme_technology_is_not_null(self): self.pop = Pop(inst='test/test') self.pop.createAndPopulateDemes() assert type(self.pop.demes[0].technologyLevel ) is float, "initial technology level info missing" assert self.pop.demes[ 0].technologyLevel > 0, "technology level cannot be null or negative" gc.collect()
def test_population_mutation_updates_individual_phenotypes(self): self.fakepop = Pop(inst='test/test') self.fakepop.numberOfDemes = 2 self.fakepop.initialDemeSize = 50 self.fakepop.migrationRate = 0 self.fakepop.mutationRate = 1 # the two following lines are very important so that the test does not fail at the boundaries, # e.g. if phen = 0 and dev < 0, mutated phenotype will still be 0 self.fakepop.initialPhenotypes = [0.5] self.fakepop.numberOfPhenotypes = 1 self.fakepop.createAndPopulateDemes(nDemes=self.fakepop.numberOfDemes, dSize=self.fakepop.initialDemeSize) origPhenDeme0 = [] origPhenDeme1 = [] for ind in self.fakepop.individuals: if ind.currentDeme == 0: origPhenDeme0.append(ind.phenotypicValues[0]) elif ind.currentDeme == 1: origPhenDeme1.append(ind.phenotypicValues[0]) self.fakepop.clearDemeInfo() self.fakepop.populationMutationMigration() phenDeme0 = [] devDeme0 = [] phenDeme1 = [] devDeme1 = [] for ind in self.fakepop.individuals: if ind.currentDeme == 0: phenDeme0.append(ind.phenotypicValues[0]) devDeme0.append(ind.mutationDeviation[0]) elif ind.currentDeme == 1: phenDeme1.append(ind.phenotypicValues[0]) devDeme1.append(ind.mutationDeviation[0]) assert len(origPhenDeme0) == len( phenDeme0 ), "Number of individuals in deme 0 have changed from {0} to {1} after mutation".format( len(origPhenDeme0), len(phenDeme0)) assert len(origPhenDeme1) == len( phenDeme1 ), "Number of individuals in deme 1 have changed from {0} to {1} after mutation".format( len(origPhenDeme1), len(phenDeme1)) for i in range(self.fakepop.initialDemeSize): assert origPhenDeme0[i] != phenDeme0[ i], "Individual {0} in deme 0 mutated from {1} to {2} when deviation was supposed to be {3}".format( i, origPhenDeme0[i], phenDeme0[i], devDeme0[i]) assert origPhenDeme1[i] != phenDeme1[ i], "Individual {0} in deme 1 mutated from {1} to {2} when deviation was supposed to be {3}".format( i, origPhenDeme1[i], phenDeme1[i], devDeme1[i])
def test_fitness_function_runs_at_population_level(self): self.pop = Pop(fit_fun='socialclass', inst='test/test') self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 5 self.pop.initialPhenotypes = [0.9, 0.7, 0.5, 0.3] self.pop.createAndPopulateDemes() try: self.pop.lifecycle() except ValueError as e: assert False, str(e)
def test_individual_attributes_are_non_empty(self, objectAttributesAreNotNone): self.pop = Pop(inst='test/test') self.pop.createAndPopulateDemes() for ind in self.pop.individuals: testObj, attrObj = objectAttributesAreNotNone( ind, ["phenotypicValues", "currentDeme"]) assert testObj, "Individual {0} has attribute(s) {1} set to None".format( ind, attrObj) gc.collect()
def test_population_has_the_right_size(self): self.howManyDemes = 10 self.howManyIndividualsPerDeme = 10 self.pop = Pop(inst='test/test') self.pop.createAndPopulateDemes(nDemes=self.howManyDemes, dSize=self.howManyIndividualsPerDeme) assert len( self.pop.individuals ) == self.howManyIndividualsPerDeme * self.howManyDemes, "You created a population of {0} individuals instead of {1}!".format( len(self.pop.individuals), self.howManyIndividualsPerDeme * self.howManyDemes)
def test_program_requires_valid_fitness_function(self, clearOutputFiles): self.population = Pop(fit_fun="gibberish", inst='test/test') setattr(self.population, "numberOfGenerations", 4) try: self.population.runSimulation() except KeyError as e: assert str(e).replace( "'", "" ) == 'Fitness function "gibberish" unknown. Add it to the functions in fitness.py', "Explain why the program fails!, not '{0}'".format( e) else: assert False, "The program should return an error message when trying to run simulations with unknown fitness function".format( self.population.numberOfDemes) clearOutputFiles('test/test/')
def test_single_sim_reads_and_writes_from_same_folder(self): dirpath = Path('simulations') if dirpath.exists() and dirpath.is_dir(): shutil.rmtree(dirpath) shutil.copytree('test/test', 'simulations') self.pop = Pop(inst='simulations') self.pop.numberOfGenerations = 3 self.pop.runSimulation() self.outputfiles = ["out_consensus.txt", "out_demography.txt", "out_phenotypes.txt", "out_resources.txt", "out_technology.txt"] for file in self.outputfiles: assert file in os.listdir('simulations') shutil.rmtree('simulations')
def test_elections_take_place_in_demes(self, pseudorandom): pseudorandom(23) self.pop = Pop(fit_fun='socialclass', inst='test/test') self.pop.numberOfDemes = 2 self.pop.initialDemeSize = 500 self.pop.migrationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() for deme in self.pop.demes: assert deme.numberOfLeaders is not None assert deme.numberOfLeaders / deme.demography == pytest.approx( deme.meanPhenotypes[3], 1.4)
def test_simulations_only_run_on_structured_populations( self, clearOutputFiles): self.population = Pop(inst='test/test') setattr(self.population, "numberOfDemes", 1) setattr(self.population, "numberOfGenerations", 4) try: self.population.runSimulation() except ValueError as e: assert str( e ) == 'This program runs simulations on well-mixed populations only. "numberOfDemes" in initialisation.txt must be > 1', "Explain why the program fails!, not '{0}'".format( e) else: assert False, "You cannot let people run simulations on well-mixed populations (only {0} deme)!".format( self.population.numberOfDemes) clearOutputFiles('test/test/')
def test_policing_function_in_population_reproduction(self): self.fakepop = Pop(inst='test/test', fit_fun='policing') self.fakepop.numberOfDemes = 3 self.fakepop.initialDemeSize = 3 self.fakepop.createAndPopulateDemes() self.fakepop.clearDemeInfo() self.fakepop.populationMutationMigration() self.fakepop.updateDemeInfoPreProduction() self.fakepop.populationProduction() self.fakepop.updateDemeInfoPostProduction() try: self.fakepop.populationReproduction() except Exception as e: assert False, "something went wrong, raised {0}: {1}".format( e.__class__.__name__, str(e))
def test_consensus_is_aggregate_of_opinions(self): self.pop = Pop(inst='test/test') self.pop.fit_fun = 'debate' self.pop.initialPhenotypes = [0.1, 0.2, 0.3, 0.4] self.pop.numberOfDemes = 3 self.pop.initialDemeSize = 2 self.pop.mutationRate = 0 self.pop.migrationRate = 0 self.pop.createAndPopulateDemes() self.pop.clearDemeInfo() self.pop.populationMutationMigration() self.pop.updateDemeInfoPreProduction() for deme in self.pop.demes: assert deme.politicsValues[ "consensus"] == 0.3, "wrong consensus value"
def test_simulation_cycle(self): fakepop = Pop(fit_fun='policingdemog', inst='test/test') fakepop.numberOfDemes = 3 fakepop.initialDemeSize = 10 fakepop.numberOfGenerations = 10 try: fakepop.runSimulation() for f in glob.glob('test/test/out_*.txt'): os.remove(f) except ValueError as e: assert False, str(e)
def test_deme_technology_is_right_format(self): self.pop = Pop(fit_fun='technology', inst='test/test') self.pop.numberOfDemes = 2 self.pop.initialDemeSize = 3 #self.fakeDeme.publicGood = 20 self.pop.initialPhenotypes = [0.5] * 4 self.pop.createAndPopulateDemes() assert self.pop.demes[ 0].technologyLevel is self.pop.initialTechnologyLevel self.pop.clearDemeInfo() assert self.pop.demes[0].technologyLevel is not None assert type(self.pop.demes[0].technologyLevel) is float assert self.pop.demes[0].technologyLevel >= 0 gc.collect()
def test_identify_deme_neighbours(self): self.fakepop = Pop(inst='test/test') self.nd = self.fakepop.numberOfDemes for deme in range(self.nd): newDemeInstance = Dem() newDemeInstance.id = deme assert deme in range(self.nd) newDemeInstance.neighbours = self.fakepop.identifyNeighbours( self.nd, deme) assert deme not in newDemeInstance.neighbours, "Deme {0} counts itself as a neighbour".format( deme) assert all( x in range(self.nd) for x in newDemeInstance.neighbours ), "Neighbour(s) of deme {0} are missing: takes into account {1} out of its {2} neighbours".format( deme, newDemeInstance.neighbours, self.nd - 1) gc.collect()