def test_not_enough_herbs_to_eat(self): """Tests that the animal eats what is left and nothing more when they cannot fill their F""" animal_list = [{ 'loc': (1, 1), 'pop': [{ 'species': 'Carnivore', 'age': 5, 'weight': 53.3 }, { 'species': 'Herbivore', 'age': 500, 'weight': 20 }, { 'species': 'Herbivore', 'age': 500, 'weight': 20 }] }] a = Island('OOO\nOJO\nOOO') b = Carnivores() c = Herbivores() a.add_animals(animal_list) b.set_new_params({'beta': 1, 'F': 50, 'DeltaPhiMax': 0.0001}) c.calculate_fitness((1, 1), a.herbs) b.calculate_fitness((1, 1), a.carns) b.carnivores_eat((1, 1), a, a.carns) assert len(a.herbs[(1, 1)]) == 0 assert a.carns[(1, 1)][0]['weight'] == 93.3
def test_more_migration(self): """Testing that the animals moves into every tile and in every direction""" animals = [{ 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }] }] a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO") b = Herbivores() a.add_animals(animals) for i in range(a.rader): for j in range(a.col): a.set_food((i, j)) b.calculate_fitness((2, 2), a.herbs) for _ in range(20): b.migration_calculations(a, a.herbs) b.migration_execution(a, a.herbs) for i in range(1, 3): for j in range(1, 3): assert (i, j) in a.herbs.keys()
def test_invalid_moving(self): """Tests that the animals does not move to tiles that they are not allowed to enter""" population = [{ 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }] }] a = Island("OOOOO\nOOOOO\nOOJOO\nOOMOO\nOOOOO") b = Herbivores() for i in range(a.rader): for j in range(a.col): a.set_food((i, j)) a.add_animals(population) b.calculate_fitness((2, 2), a.herbs) for _ in range(30): b.migration_calculations(a, a.herbs) b.migration_execution(a, a.herbs) assert (2, 1) not in a.herbs.keys() assert len(a.herbs[(2, 2)]) == 9
def test_carnivores_eat_sometimes_when_they_have_somewhat_higher_fitness( self): """ Testing that the carnivores do eat with some kind of probability when their fitness is somewhat higher than the herbivores """ animal_list = [{ 'loc': (1, 1), 'pop': [{ 'species': 'Carnivore', 'age': 1, 'weight': 53.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }] }] a = Island('OOO\nOJO\nOOO') b = Carnivores() c = Herbivores() a.add_animals(animal_list) b.set_new_params({'beta': 1, 'F': 50}) c.calculate_fitness((1, 1), a.herbs) b.calculate_fitness((1, 1), a.carns) for _ in range(40): b.carnivores_eat((1, 1), a, a.carns) assert len(a.herbs[(1, 1)]) < 4 assert len(a.herbs[(1, 1)]) != 0
def test_carnivore_leaves_food(self): """Testing that the carnivores stops eating when it has received f amount of food""" animal_list = [{ 'loc': (1, 1), 'pop': [{ 'species': 'Carnivore', 'age': 5, 'weight': 53.3 }, { 'species': 'Carnivore', 'age': 5, 'weight': 53.3 }, { 'species': 'Herbivore', 'age': 500, 'weight': 13.3 }, { 'species': 'Herbivore', 'age': 500, 'weight': 13.3 }, { 'species': 'Herbivore', 'age': 500, 'weight': 13.3 }, { 'species': 'Herbivore', 'age': 500, 'weight': 13.3 }] }] a = Island('OOO\nOJO\nOOO') b = Carnivores() c = Herbivores() a.add_animals(animal_list) b.set_new_params({'beta': 1, 'F': 5, 'DeltaPhiMax': 0.0001}) c.calculate_fitness((1, 1), a.herbs) b.calculate_fitness((1, 1), a.carns) b.carnivores_eat((1, 1), a, a.carns) assert len(a.herbs[(1, 1)]) == 2 assert a.carns[(1, 1)][0]['weight'] == 58.3 assert a.carns[(1, 1)][1]['weight'] == 58.3
def test_carnivores_does_not_eat_with_lower_fitness(self): """Testing that the carnivores do not eat when they have lower fitness than the herbivores""" animal_list = [{ 'loc': (1, 1), 'pop': [{ 'species': 'Carnivore', 'age': 500, 'weight': 53.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 23.3 }] }] a = Island('OOO\nOJO\nOOO') b = Carnivores() c = Herbivores() a.add_animals(animal_list) b.set_new_params({'beta': 1, 'F': 50}) c.calculate_fitness((1, 1), a.herbs) b.calculate_fitness((1, 1), a.carns) for _ in range(40): b.carnivores_eat((1, 1), a, a.carns) assert len(a.herbs[(1, 1)]) == 4
def test_migration(self): """ Tests that two of the animals gets moved with the given inputs and seed """ population = [{ 'loc': (3, 1), 'pop': [{ 'species': 'Herbivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (3, 1), 'pop': [{ 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }] }] a = Island("OOOOO\nOOOOO\nOJOOO\nOJOOO\nOOOOO\nOOOOO") b = Herbivores() a.add_animals(population) a.set_food((3, 1)) a.set_food((2, 1)) a.set_food((3, 2)) a.set_food((3, 0)) a.set_food((4, 1)) b.calculate_fitness((3, 1), a.herbs) b.migration_calculations(a, a.herbs) assert len(a.herbs[(3, 1)]) == 9 assert len(b.idx_for_animals_to_remove) > 0 b.migration_execution(a, a.herbs) assert len(a.herbs[(3, 1)]) == 7 assert len(a.herbs[(2, 1)]) == 2
def test_more_migration(self): """Tests that the carnivores migrates to all cells""" population = [{ 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (2, 2), 'pop': [{ 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }, { 'species': 'Herbivore', 'age': 4, 'weight': 10 }] }] population1 = [{ 'loc': (2, 2), 'pop': [{ 'species': 'Carnivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (2, 2), 'pop': [{ 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }, { 'species': 'Carnivore', 'age': 4, 'weight': 10 }] }] a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOJJJO\nOOOOO") b = Carnivores() c = Herbivores() a.add_animals(population) a.add_animals(population1) for i in range(a.rader): for j in range(a.col): a.set_food((i, j)) b.calculate_fitness((2, 2), a.carns) c.calculate_fitness((2, 2), a.herbs) for _ in range(20): c.migration_calculations(a, a.herbs) c.migration_execution(a, a.herbs) for _ in range(20): b.migration_calculations(a, c, a.carns) b.migration_execution(a, a.carns) for i in range(1, 3): for j in range(1, 3): assert (i, j) in a.carns.keys()
class BioSim: def __init__(self, island_map, ini_pop, seed, ymax_animals=None, cmax_animals=None, img_base=None, img_fmt='png'): """ :param island_map: Multi-line string specifying island geography :param ini_pop: List of dictionaries specifying initial population :param seed: Integer used as random number seed :param ymax_animals: Number specifying y-axis limit for graph showing animal numbers :param cmax_animals: Dict specifying color-code limits for animal densities :param img_base: String with beginning of file name for figures, including path :param img_fmt: String with file type for figures, e.g. 'png' If ymax_animals is None, the y-axis limit should be adjusted automatically. If cmax_animals is None, sensible, fixed default values should be used. cmax_animals is a dict mapping species names to numbers, e.g., {'Herbivore': 50, 'Carnivore': 20} If img_base is None, no figures are written to file. Filenames are formed as '{}_{:05d}.{}'.format(img_base, img_no, img_fmt) where img_no are consecutive image numbers starting from 0. img_base should contain a path and beginning of a file name. """ # variables for code/setup/classes self.ini_pop = ini_pop self.island_map = island_map self.tot_num_years = 0 # variables for movie/save if img_base is not None: self._img_base = img_base else: self._img_base = None self._img_fmt = img_fmt self._img_ctr = 0 # Variables for visualization/plot self.ax1 = None self.ax2 = None self.ax3 = None self.ax4 = None self.line = None self.line2 = None if ymax_animals is None: self.ymax_animals = 20000 else: self.ymax_animals = ymax_animals if cmax_animals is None: self.cmax_animals = {'Herbivore': 100, 'Carnivore': 100} else: self.cmax_animals = cmax_animals # Initialization, setting classes and seed self.island = Island(island_map) self.island.limit_map_vals() self.herbivores = Herbivores() self.carnivores = Carnivores() self.animal = Animal() self.setup_simulation() np.random.seed(seed) random.seed(seed) def set_animal_parameters(self, species, params): """ Set parameters for animal species. :param species: String, name of animal species :param params: Dict with valid parameter specification for species """ if species == 'Herbivore': self.herbivores.set_new_params(params) else: self.carnivores.set_new_params(params) def set_landscape_parameters(self, terrain, params): """ Set parameters for landscape types. :param terrain: Tells which terrain shall get new parameters :param params: Dict with valid parameter specification for landscape """ self.island.set_new_params(terrain, params) def setup_simulation(self): """ Function to set up the simulation, sets up the food and inserts the initial population. """ for i in range(self.island.rader): for j in range(self.island.col): self.island.set_food((i, j)) self.island.add_animals(self.ini_pop) def simulate_one_year(self): """ Function used to simulate one year """ for i in range(self.island.rader): for j in range(self.island.col): pos = (i, j) self.island.grow_food(pos) self.herbivores.calculate_fitness(pos, self.island.herbs) self.herbivores.sort_by_fitness(pos, self.island.herbs) self.herbivores.animals_eat(pos, self.island, self.island.herbs) self.herbivores.sort_before_getting_hunted( pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.carnivores.sort_by_fitness(pos, self.island.carns) self.carnivores.carnivores_eat(pos, self.island, self.island.carns) self.herbivores.breeding(pos, self.island, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.carnivores.breeding(pos, self.island, self.island.carns) self.herbivores.calculate_fitness(pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.herbivores.migration_calculations(self.island, self.island.herbs) self.carnivores.migration_calculations(self.island, self.herbivores, self.island.carns) self.herbivores.migration_execution(self.island, self.island.herbs) self.carnivores.migration_execution(self.island, self.island.carns) for i in range(self.island.rader): for j in range(self.island.col): pos = (i, j) self.herbivores.aging(pos, self.island.herbs) self.carnivores.aging(pos, self.island.carns) self.herbivores.loss_of_weight(pos, self.island.herbs) self.carnivores.loss_of_weight(pos, self.island.carns) self.herbivores.calculate_fitness(pos, self.island.herbs) self.carnivores.calculate_fitness(pos, self.island.carns) self.herbivores.death(pos, self.island.herbs) self.carnivores.death(pos, self.island.carns) def simulate(self, num_years, vis_years=1, img_years=None): """ Function used to simulate num_years, visualizing every vis_years and taking a picture every img_years. :param num_years: Number of years to simulate :param vis_years: Number that indicates how many years between each visualization :param img_years: Number that indicates how many years between taking images, if None a picture will be taken for each visualization """ self._setup_graphics(num_years) for year in range(num_years): if year % vis_years == 0: self._update_graphics() if year % img_years == 0: self._save_graphics() self.simulate_one_year() self.tot_num_years += 1 def add_population(self, population): """ Add a population to the island :param population: List of dictionaries specifying population """ self.island.add_animals(population) def _setup_graphics(self, num_years): """ This function is used to setup the figure of the grapichs, it creates a figure with 4 subplots and initializes the animal lines which are plotted in the upper right subplot. :param num_years: :return: """ fig = plt.figure() axt = fig.add_axes([0.4, 0.8, 0.2, 0.2]) axt.axis('off') self.ax1 = plt.subplot2grid((2, 2), (0, 0)) self.ax2 = plt.subplot2grid((2, 2), (0, 1)) self.ax3 = plt.subplot2grid((2, 2), (1, 0)) self.ax4 = plt.subplot2grid((2, 2), (1, 1)) self.ax2.set_xlim(self.year + 1, self.year + num_years) self.ax2.set_ylim(0, self.ymax_animals) self.line = self.ax2.plot(np.arange(num_years + self.year + 1), np.full(num_years + self.year + 1, np.nan), 'b-')[0] self.line2 = self.ax2.plot(np.arange(num_years + self.year + 1), np.full(num_years + self.year + 1, np.nan), 'r-')[0] def _update_graphics(self): """ Updates graphics with current data. """ plt.suptitle('Years since the simulation started: ' + str(self.year) + ' years') rgb_value = { "O": mcolors.to_rgba("navy"), "J": mcolors.to_rgba("forestgreen"), "S": mcolors.to_rgba("#e1ab62"), "D": mcolors.to_rgba("yellow"), "M": mcolors.to_rgba("lightslategrey") } kart_rgb = [[rgb_value[column] for column in row] for row in self.island_map.split()] self.ax1.imshow(kart_rgb) self.ax1.set_xticks(range(len(kart_rgb[0]))) self.ax1.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax1.set_yticks(range(len(kart_rgb))) self.ax1.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax1.axis('scaled') self.ax1.set_title('Map') # Upper right subplot ydata = self.line.get_ydata() ydata2 = self.line2.get_ydata() ydata[self.year] = self.num_animals_per_species['Herbivore'] ydata2[self.year] = self.num_animals_per_species['Carnivore'] self.line.set_ydata(ydata) self.line2.set_ydata(ydata2) self.ax2.set_title('Total number of animals') self.ax3.imshow(self.animal_distribution_for_plot[:, :, 0], 'Greens', vmax=self.cmax_animals['Herbivore']) self.ax3.set_xticks(range(len(kart_rgb[0]))) self.ax3.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax3.set_yticks(range(len(kart_rgb))) self.ax3.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax3.axis('scaled') self.ax3.set_title('Herbivore distribution: ' + str(self.num_animals_per_species['Herbivore'])) self.ax4.imshow(self.animal_distribution_for_plot[:, :, 1], 'Reds', vmax=self.cmax_animals['Carnivore']) self.ax4.set_xticks(range(len(kart_rgb[0]))) self.ax4.set_xticklabels(range(1, 1 + len(kart_rgb[0]))) self.ax4.set_yticks(range(len(kart_rgb))) self.ax4.set_yticklabels(range(1, 1 + len(kart_rgb))) self.ax4.axis('scaled') self.ax4.set_title('Carnivore distribution: ' + str(self.num_animals_per_species['Carnivore'])) plt.pause(1e-9) def _save_graphics(self): """Saves graphics to file if file name given.""" if self._img_base is None: return plt.savefig('{base}_{num:05d}.{type}'.format(base=self._img_base, num=self._img_ctr, type=self._img_fmt)) self._img_ctr += 1 def make_movie(self): import glob img_array = [] for filename in glob.glob(self._img_base + '*' + self._img_fmt): img = cv2.imread(filename) height, width, layers = img.shape size = (width, height) img_array.append(img) out = cv2.VideoWriter('project.avi', cv2.VideoWriter_fourcc(*'DIVX'), 6, size) for i in range(len(img_array)): out.write(img_array[i]) out.release() @property def year(self): """Last year simulated.""" return self.tot_num_years @property def num_animals(self): """Total number of animals on island.""" return sum(self.island.num_animals()) @property def num_animals_per_species(self): """Number of animals per species in island, as dictionary.""" herb, carn = self.island.num_animals() return {'Herbivore': herb, 'Carnivore': carn} @property def animal_distribution_for_plot(self): """Pandas DataFrame with animal count per species for each cell on island.""" animal_list = np.zeros((self.island.rader, self.island.col, 2)) for i in range(self.island.rader): for j in range(self.island.col): if (i, j) in self.island.herbs.keys(): animal_list[i, j, 0] = len(self.island.herbs[(i, j)]) if (i, j) in self.island.carns.keys(): animal_list[i, j, 1] = len(self.island.carns[(i, j)]) return animal_list @property def animal_distribution(self): a = np.zeros((self.island.rader * self.island.col, 4)) for i in range(self.island.rader): a[i * self.island.col:(i + 1) * self.island.col, 0] = i c = np.arange(self.island.col) for i in range(self.island.rader): a[i * self.island.col:(i + 1) * self.island.col, 1] = c for pos in self.island.herbs.keys(): a[pos[0] * self.island.col + pos[1], 2] += len(self.island.herbs[pos]) for pos in self.island.carns.keys(): a[pos[0] * self.island.col + pos[1], 3] += len(self.island.carns[pos]) return pd.DataFrame(a, columns=['Row', 'Col', 'Herbivore', 'Carnivore'])