def _generate_individual(self): depots = self.settings.depots random.shuffle(depots) result = Individual() for depot in depots: result.add_depot(depot, self.settings.max_capacity) return result
def test_offspring_number_increases_with_fertility(self): self.indLowFertility = Ind(m=3) self.indHighFertility = Ind(m=3) self.indLowFertility.storage = 1 self.indLowFertility.reproduce(fecundity=0.5) self.indHighFertility.storage = 10 self.indHighFertility.reproduce(fecundity=2) assert self.indLowFertility.offspring < self.indHighFertility.offspring, "high fertility should lead to higher offspring number"
def test_normalize(self): individual = Individual([Route(0, self.depots)]) individual.normalize(100) self.assertGreater(len(individual.routes), 1) for route in individual.routes: self.assertTrue(route.sum_of_demands() <= 100) depots_in_individual = [depot for route in individual.routes for depot in route.depots] self.assertItemsEqual(depots_in_individual, self.depots)
def populate(self, size): for _ in range(size): ind = Individual() age1, age2 = AgeDist.draw_age(), DeathDist.draw_age_at_death() ind.current_age, ind.age_at_death = sorted([age1, age2]) ind.endowment = IncomeDist.draw_income() ind.schooling = SchoolingDist.draw_schooling() self.population.append(ind) self.size = size
def test_steps_are_bounded(self): gridSide = 6 self.ind = Ind(m=gridSide) self.ind.coordinates = [0, 0] storeCoordH = [] storeCoordV = [] for i in range(10): self.ind.explore() storeCoordH.append(self.ind.coordinates[0]) storeCoordV.append(self.ind.coordinates[1]) assert all([0 <= x < gridSide for x in storeCoordH ]), "stepped out of grid: {0}".format(storeCoordH) assert all([0 <= x < gridSide for x in storeCoordV ]), "stepped out of grid: {0}".format(storeCoordV) # reset for upper boundary self.ind.coordinates = [gridSide - 1, gridSide - 1] storeCoordH = [] storeCoordV = [] for i in range(10): self.ind.explore() storeCoordH.append(self.ind.coordinates[0]) storeCoordV.append(self.ind.coordinates[1]) assert all([0 <= x < gridSide for x in storeCoordH ]), "stepped out of grid: {0}".format(storeCoordH) assert all([0 <= x < gridSide for x in storeCoordV ]), "stepped out of grid: {0}".format(storeCoordV)
def test_death_depends_on_predation_risk(self): self.ind = Ind(m=3) self.ind.vigilance = 0 self.ind.survive(p=0) assert self.ind.alive == True self.ind.survive(p=1) assert self.ind.alive == False
def test_death_depends_on_vigilance(self): self.ind = Ind(m=3) self.ind.vigilance = 0 self.ind.survive(p=1) assert self.ind.alive == False self.ind.vigilance = 1 self.ind.survive(p=1) assert self.ind.alive == True
def test_reproduction_gives_offspring_number(self): self.indiv = Ind(m=3) self.indiv.reproduce(fecundity=2) assert self.indiv.offspring != None, "No offspring number generated" assert type( self.indiv.offspring ) is int, "Offspring number of wrong format: {0} instead of integer".format( type(self.indiv.offspring)) assert self.indiv.offspring >= 0, "Offspring number cannot be negative"
def test_gathering_depends_on_neighbours_vigilance(self): self.ind = Ind(m=3) self.ind.gather(resources=6, share=(1 - self.ind.vigilance + 0.9) / 2, efficiency=0.9) firstGathering = self.ind.storage self.ind.gather(resources=6, share=(1 - self.ind.vigilance + 0.7 * 2) / 3, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) < firstGathering
def test_gathering_depends_on_resources_no_neighbours(self): self.ind = Ind(m=3) self.ind.gather(resources=5, share=1 - self.ind.vigilance, efficiency=0.9) firstGathering = self.ind.storage self.ind.gather(resources=10, share=1 - self.ind.vigilance, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) > firstGathering
def update_population(self): deaths = [0 for _ in BirthData.rates] incomes = [0. for _ in BirthData.rates] thresholds = [quantile(self.get_overall_incomes(),q)\ for q in BirthData.quantiles] for ind in self.population: ind.grow_older() if not ind.is_alive(): level = sum([ind.get_overall_income()> threshold \ for threshold in thresholds]) deaths[level] += 1 incomes[level] += ind.get_overall_income() self.population.remove(ind) births = [int(d * (1. + r)) for d, r in zip(deaths, BirthData.rates)] for birth, income in zip(births, incomes): if birth > 0: for _ in range(birth): ind = Individual() ind.endowment = income / birth ind.current_age = 0 ind.age_at_death = DeathDist.draw_age_at_death() ind.schooling = SchoolingDist.draw_schooling() self.population.append(ind) self.size = len(self.population)
def update(self): """ Update population Return nothing Create new individual instances for the new generation, who inherit their parent's vigilance level and coordinates on the grid. Mutate individual vigilance phenotype. Calculate mean vigilance in the population and store it in self.vigilance. """ tmpIndividuals = [] self.totalVigilance = 0 for offspring in range(self.nIndiv): ind = Ind(m=self.gridSize) parent = self.individuals[self.nextGeneration[offspring]] setattr(ind, "vigilance", parent.vigilance) setattr(ind, "coordinates", parent.coordinates) ind.mutate(mutRate=self.mutRate, mutStep=self.mutStep) tmpIndividuals.append(ind) self.totalVigilance += ind.vigilance self.individuals = tmpIndividuals self.vigilance = self.totalVigilance / self.nIndiv
def test_individuals_make_steps(self): gridSide = 6 self.ind = Ind(m=gridSide) storeStepsH = [99] * 10 storeStepsV = [99] * 10 for i in range(10): before = self.ind.coordinates self.ind.explore() after = self.ind.coordinates storeStepsH[i] = before[0] - after[0] storeStepsV[i] = before[1] - after[1] assert any([x != 0 for x in storeStepsH]) or any( [x != 0 for x in storeStepsV]), "no individual changed position!"
def test_steps_are_between_minus_one_and_one(self): gridSide = 6 self.ind = Ind(m=gridSide) storeStepsH = [] storeStepsV = [] for i in range(10): before = self.ind.coordinates self.ind.explore() after = self.ind.coordinates storeStepsH.append(before[0] - after[0]) storeStepsV.append(before[1] - after[1]) assert all([x in [-1, 0, 1] for x in storeStepsH ]), "wrong horizontal step size in {0}".format(storeStepsH) assert all([x in [-1, 0, 1] for x in storeStepsV]), "wrong vertical step size"
def test_gathering_depends_on_vigilance_no_neighbours(self): self.ind = Ind(m=3) assert self.ind.storage == 0 self.ind.vigilance = 0.1 self.ind.gather(resources=6, share=1 - self.ind.vigilance, efficiency=0.9) firstGathering = self.ind.storage expect = (1 - 0.1) * 0.9 * 6 assert firstGathering == expect, "gathered {0} instead of {1}".format( firstGathering, expect) self.ind.vigilance = 0.8 self.ind.gather(resources=6, share=1 - self.ind.vigilance, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) < firstGathering
def test_average_survival_for_intermediate_vigilance(self): self.ind = Ind(m=3) self.ind.vigilance = random.random() deathCount = 0 sampleSize = 1000 for i in range(sampleSize): self.ind.survive(p=1) if not self.ind.alive: deathCount += 1 stat1, pval1 = scistats.ttest_1samp([1] * deathCount + [0] * (sampleSize - deathCount), 1 - self.ind.vigilance) assert pval1 > 0.05, "T-test mean failed. Observed: {0}, Expected: {1}".format( deathCount / sampleSize, 1 - self.ind.vigilance) self.test = scistats.binom_test(deathCount, sampleSize, 1 - self.ind.vigilance, alternative="two-sided") assert self.test > 0.05, "Success rate = {0} when predation rate = {1}".format( self.mutantCount / self.nIndividuals, 1 - self.ind.vigilance)
class TestConsumptionFunction(object): def test_individuals_can_gather_resources(self): assert hasattr(Ind(m=3), "gather"), "individual has no consumption method" assert callable(getattr(Ind(m=3), "gather")) def test_individual_has_access_to_storage(self): assert hasattr(Ind(m=3), "storage"), "individual cannot store resources" self.ind = Ind(m=3) assert self.ind.storage == 0, "individual initial resource storage should be empty" def test_gathering_depends_on_resources_no_neighbours(self): self.ind = Ind(m=3) self.ind.gather(resources=5, share=1 - self.ind.vigilance, efficiency=0.9) firstGathering = self.ind.storage self.ind.gather(resources=10, share=1 - self.ind.vigilance, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) > firstGathering def test_gathering_depends_on_vigilance_no_neighbours(self): self.ind = Ind(m=3) assert self.ind.storage == 0 self.ind.vigilance = 0.1 self.ind.gather(resources=6, share=1 - self.ind.vigilance, efficiency=0.9) firstGathering = self.ind.storage expect = (1 - 0.1) * 0.9 * 6 assert firstGathering == expect, "gathered {0} instead of {1}".format( firstGathering, expect) self.ind.vigilance = 0.8 self.ind.gather(resources=6, share=1 - self.ind.vigilance, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) < firstGathering def test_gathering_depends_on_neighbours_vigilance(self): self.ind = Ind(m=3) self.ind.gather(resources=6, share=(1 - self.ind.vigilance + 0.9) / 2, efficiency=0.9) firstGathering = self.ind.storage self.ind.gather(resources=6, share=(1 - self.ind.vigilance + 0.7 * 2) / 3, efficiency=0.9) assert self.ind.storage > firstGathering assert (self.ind.storage - firstGathering) < firstGathering
def test_individual_has_access_to_storage(self): assert hasattr(Ind(m=3), "storage"), "individual cannot store resources" self.ind = Ind(m=3) assert self.ind.storage == 0, "individual initial resource storage should be empty"
class TestReproductionFunction(object): def test_individual_can_reproduce(self): assert hasattr(Ind(m=3), "reproduce"), "ind cannot reproduce" def test_fertility_returns_positive_float(self): self.fakepop = Pop("test/test/parameters.txt") self.fakepop.create() for ind in range(len(self.fakepop.individuals)): indiv = self.fakepop.individuals[ind] setattr(indiv, "storage", 1 + 9 * ind) indiv.reproduce(fecundity=2) assert type(indiv.fertility) is float assert indiv.fertility >= 0 def test_fertility_increases_with_storage(self): self.fakepop = Pop("test/test/parameters.txt") self.fakepop.create() fertility = 0 for ind in range(len(self.fakepop.individuals)): indiv = self.fakepop.individuals[ind] setattr(indiv, "storage", 1 + 9 * ind) indiv.reproduce(fecundity=2) assert indiv.fertility > fertility fertility = indiv.fertility def test_reproduction_gives_offspring_number(self): self.indiv = Ind(m=3) self.indiv.reproduce(fecundity=2) assert self.indiv.offspring != None, "No offspring number generated" assert type( self.indiv.offspring ) is int, "Offspring number of wrong format: {0} instead of integer".format( type(self.indiv.offspring)) assert self.indiv.offspring >= 0, "Offspring number cannot be negative" def test_offspring_number_increases_with_fertility(self): self.indLowFertility = Ind(m=3) self.indHighFertility = Ind(m=3) self.indLowFertility.storage = 1 self.indLowFertility.reproduce(fecundity=0.5) self.indHighFertility.storage = 10 self.indHighFertility.reproduce(fecundity=2) assert self.indLowFertility.offspring < self.indHighFertility.offspring, "high fertility should lead to higher offspring number" def test_reproduction_is_seed_dependent(self, pseudorandom): self.fakepop = Pop("test/test/parameters.txt") self.fakepop.nIndiv = 1000 self.fakepop.create() offspring = [] for ind in self.fakepop.individuals: setattr(ind, "storage", 4) pseudorandom(0) ind.reproduce(fecundity=2) offspring.append(ind.offspring) assert all([ x == offspring[0] for x in offspring ]), "number of offspring differs with same seed, {0}".format( set(offspring)) def test_reproduction_follows_a_poisson_distribution(self, pseudorandom): #http://www2.stat-athens.aueb.gr/~exek/papers/Xekalaki-Statistician2000(355-382)ft.pdf pseudorandom(0) self.fakepop = Pop("test/test/parameters.txt") self.fakepop.nIndiv = 1000 self.fakepop.create() self.explambda = 8 offspringPerInd = [] for ind in self.fakepop.individuals: setattr(ind, "storage", 4) ind.reproduce(fecundity=2) offspringPerInd.append(ind.offspring) d = Counter(offspringPerInd) a, b = list(d.keys()), list(d.values()) maxCount = max(a) observedCount = [] expectedCount = [] for k in range(maxCount): if k in a: observedCount.append(d[k]) else: observedCount.append(0) expProbability = m.pow(m.e, (-self.explambda)) * (m.pow( self.explambda, k)) / m.factorial(k) expectedCount.append(self.fakepop.nIndiv * expProbability) chisq, pval = scistats.chisquare(observedCount, expectedCount) assert len(expectedCount) == len( observedCount), "len obs = {0}, len exp = {1}".format( len(observedCount), len(expectedCount)) #assert sum(expectedCount) == sum(observedCount), "n obs = {0}, n exp = {1}".format(sum(observedCount), sum(expectedCount)) assert pval > 0.05, "Test for goodness of fit failed: obs = {0}, exp = {1}".format( observedCount, expectedCount)
def crossover(self, first_individual, second_individual): descendant = Individual.of(first_individual) subroute = self._get_random_subroute(second_individual) self._insert_subroute(subroute, descendant) descendant.normalize(self.settings) return descendant
class TestSurvivalFeature(object): def test_individuals_have_survival_function(self): assert hasattr(Ind(m=3), "survive"), "individuals cannot survive" assert callable(getattr(Ind(m=3), "survive")) def test_individuals_can_die(self): self.ind = Ind(m=3) self.ind.survive(p=0.5) assert hasattr(self.ind, "alive") assert self.ind.alive is not None assert type(self.ind.alive) is bool def test_death_depends_on_predation_risk(self): self.ind = Ind(m=3) self.ind.vigilance = 0 self.ind.survive(p=0) assert self.ind.alive == True self.ind.survive(p=1) assert self.ind.alive == False def test_death_depends_on_vigilance(self): self.ind = Ind(m=3) self.ind.vigilance = 0 self.ind.survive(p=1) assert self.ind.alive == False self.ind.vigilance = 1 self.ind.survive(p=1) assert self.ind.alive == True def test_average_survival_for_intermediate_vigilance(self): self.ind = Ind(m=3) self.ind.vigilance = random.random() deathCount = 0 sampleSize = 1000 for i in range(sampleSize): self.ind.survive(p=1) if not self.ind.alive: deathCount += 1 stat1, pval1 = scistats.ttest_1samp([1] * deathCount + [0] * (sampleSize - deathCount), 1 - self.ind.vigilance) assert pval1 > 0.05, "T-test mean failed. Observed: {0}, Expected: {1}".format( deathCount / sampleSize, 1 - self.ind.vigilance) self.test = scistats.binom_test(deathCount, sampleSize, 1 - self.ind.vigilance, alternative="two-sided") assert self.test > 0.05, "Success rate = {0} when predation rate = {1}".format( self.mutantCount / self.nIndividuals, 1 - self.ind.vigilance)
class TestExplorationFunction(object): def test_individual_can_explore_grid(self): assert hasattr(Ind(m=3), "explore"), "ind cannot explore" assert callable(getattr(Ind(m=3), "explore")), "explore not a method" def test_individuals_make_steps(self): gridSide = 6 self.ind = Ind(m=gridSide) storeStepsH = [99] * 10 storeStepsV = [99] * 10 for i in range(10): before = self.ind.coordinates self.ind.explore() after = self.ind.coordinates storeStepsH[i] = before[0] - after[0] storeStepsV[i] = before[1] - after[1] assert any([x != 0 for x in storeStepsH]) or any( [x != 0 for x in storeStepsV]), "no individual changed position!" def test_steps_are_between_minus_one_and_one(self): gridSide = 6 self.ind = Ind(m=gridSide) storeStepsH = [] storeStepsV = [] for i in range(10): before = self.ind.coordinates self.ind.explore() after = self.ind.coordinates storeStepsH.append(before[0] - after[0]) storeStepsV.append(before[1] - after[1]) assert all([x in [-1, 0, 1] for x in storeStepsH ]), "wrong horizontal step size in {0}".format(storeStepsH) assert all([x in [-1, 0, 1] for x in storeStepsV]), "wrong vertical step size" def test_steps_are_bounded(self): gridSide = 6 self.ind = Ind(m=gridSide) self.ind.coordinates = [0, 0] storeCoordH = [] storeCoordV = [] for i in range(10): self.ind.explore() storeCoordH.append(self.ind.coordinates[0]) storeCoordV.append(self.ind.coordinates[1]) assert all([0 <= x < gridSide for x in storeCoordH ]), "stepped out of grid: {0}".format(storeCoordH) assert all([0 <= x < gridSide for x in storeCoordV ]), "stepped out of grid: {0}".format(storeCoordV) # reset for upper boundary self.ind.coordinates = [gridSide - 1, gridSide - 1] storeCoordH = [] storeCoordV = [] for i in range(10): self.ind.explore() storeCoordH.append(self.ind.coordinates[0]) storeCoordV.append(self.ind.coordinates[1]) assert all([0 <= x < gridSide for x in storeCoordH ]), "stepped out of grid: {0}".format(storeCoordH) assert all([0 <= x < gridSide for x in storeCoordV ]), "stepped out of grid: {0}".format(storeCoordV)
def test_individuals_can_die(self): self.ind = Ind(m=3) self.ind.survive(p=0.5) assert hasattr(self.ind, "alive") assert self.ind.alive is not None assert type(self.ind.alive) is bool
def test_insertion(self): depots = self.depots route = Route(0, list(depots)) individual = Individual([route]) individual.normalize(self.operators.settings.max_demand) self.operators.insertion(individual)