def jungle_island_animals(self): """Creats an island that consists of a 3X3 jungle square in tha middle, that contains 4 animals(3 Herbivores and one carnivore) for later convenience """ added_list = [{ 'loc': (3, 3), 'pop': [{ 'species': 'Herbivore', 'age': 21, 'weight': 17.3 }, { 'species': 'Herbivore', 'age': 30, 'weight': 19.3 }, { 'species': 'Herbivore', 'age': 10, 'weight': 107.3 }, { 'species': 'Carnivore', 'age': 1, 'weight': 20.3 }] }] a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO") a.add_animals(added_list) b = Herbivores() return a, b
def jungle_island_animals(self): """ Function to create an island that can be used for testing the Herbivore-class :return: Returns the Island-class with a map and animals and the Herbivore-class """ added_list = [{ 'loc': (3, 3), 'pop': [{ 'species': 'Herbivore', 'age': 21, 'weight': 17.3 }, { 'species': 'Herbivore', 'age': 30, 'weight': 19.3 }, { 'species': 'Herbivore', 'age': 10, 'weight': 107.3 }, { 'species': 'Herbivore', 'age': 1, 'weight': 20.3 }] }] a = Island("OOOOO\nOJJJO\nOJJJO\nOJJJO\nOOOOO") a.add_animals(added_list) b = Herbivores() return a, b
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_invalid_moving_carns(self): """ Tests invalid moving is not happening""" 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\nOOMOO\nOOJOO\nOOMOO\nOOJOO") b = Carnivores() c = Herbivores() a.add_animals(population1) b.calculate_fitness((2, 2), a.carns) b.migration_calculations(a, c, a.carns) assert len(a.carns[(2, 2)]) == 9 assert (3, 2) not in a.carns.keys() assert len(b.idx_for_animals_to_remove) == 0 b.migration_execution(a, a.carns) assert len(a.carns[(2, 2)]) == 9
def test_add_animals(self): """ Testing add_animals and total_animals_per_species methods """ map_str = """ OOOOOOOOOOOO OMSOOOOOSMMO OOOOOOOOOOOO""" island = Island(map_str) animals = [ { "loc": (1, 1), "pop": [ { "species": "Herbivore", "age": 10, "weight": 10.0 }, { "species": "Carnivore", "age": 11, "weight": 11.0 }, ], }, { "loc": (1, 2), "pop": [ { "species": "Herbivore", "age": 10, "weight": 10.0 }, { "species": "Herbivore", "age": 11, "weight": 11.0 }, { "species": "Carnivore", "age": 12, "weight": 12.0 }, ], }, ] island.add_animals(animals) assert island.total_animals_per_species('Herbivore') == 3 assert island.total_animals_per_species('Carnivore') == 2
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
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. """ self.landscapes = {'O': Ocean, 'S': Savannah, 'M': Mountain, 'J': Jungle, 'D': Desert} self.landscapes_with_parameters = [Savannah, Jungle] self.animal_species = {'Carnivore': Carnivore, 'Herbivore': Herbivore} for char in island_map.replace('\n', ''): if char not in self.landscapes: raise ValueError('This given string contains unknown ' 'geographies') lengths = [len(line) for line in island_map.splitlines()] if len(set(lengths)) > 1: raise ValueError('This given string is not uniform') self.island_map = island_map self._map = Island(island_map) random.seed(seed) self.add_population(ini_pop) if ymax_animals is None: self.ymax_animals = 20000 else: self.ymax_animals = ymax_animals if cmax_animals is None: self.cmax_animals = {'Herbivore': 5, 'Carnivore': 5} else: self.cmax_animals = cmax_animals if img_base is None: self.img_base = DEFAULT_GRAPHICS_DIR + DEFAULT_GRAPHICS_NAME else: self.img_base = img_base self.img_fmt = img_fmt self.img_counter = 0 self.vis = None self._year = 0 self.final_year = None 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 in self.animal_species: species_type = self.animal_species[species] animal = species_type() animal.set_parameters(params) else: raise TypeError(species + ' parameters can\'t be assigned, ' 'there is no such data type') def set_landscape_parameters(self, landscape, params): """ Set parameters for landscape type. :param landscape: String, code letter for landscape :param params: Dict with valid parameter specification for landscape """ if landscape in self.landscapes: landscape_type = self.landscapes[landscape] if landscape_type in \ self.landscapes_with_parameters: landscape_type.set_parameters(params) else: raise ValueError(landscape + ' parameter is not valid') else: raise TypeError(landscape + ' parameters can\'t be assigned, ' 'there is no such data type') def simulate(self, num_years, vis_years=200, img_years=None): """ Run simulation while visualizing the result. :param num_years: number of years to simulate :param vis_years: years between visualization updates :param img_years: years between visualizations saved to files (default: vis_years) Image files will be numbered consecutively. """ if img_years is None: img_years = vis_years self.final_year = self._year + num_years self.setup_graphics() if self._year > 1: self.vis.generate_animal_graphs(self.final_year, self.ymax_animals, recreate=True) while self._year < self.final_year: if self._year % vis_years == 0: self.update_graphics() if (self._year + 1) % img_years == 0: self.save_graphics() self._map.life_cycle() self._year += 1 df = self.animal_distribution df.to_csv('results/data.csv', sep='\t', encoding='utf-8') def setup_graphics(self): """ Setup the graphics """ map_dims = self._map.map_dims if self.vis is None: fig = plt.figure() self.vis = Graphics(self.island_map, fig, map_dims) self.vis.generate_island_graph() self.vis.generate_animal_graphs(self.final_year, self.ymax_animals) self.vis.animal_dist_graphs() def update_graphics(self): """ Updates graphics with current data. """ df = self.animal_distribution rows, cols = self._map.map_dims dist_matrix_carnivore = np.array(df[['Carnivore']]).reshape(rows, cols) dist_matrix_herbivore = np.array(df[['Herbivore']]).reshape(rows, cols) # updates the line graphs herb_count, carn_count = list(self.num_animals_per_species.values()) self.vis.update_graphs(self._year, herb_count, carn_count) self.vis.update_herbivore_dist(dist_matrix_herbivore) self.vis.update_carnivore_dist(dist_matrix_carnivore) plt.pause(1e-6) self.vis.set_year(self._year) def save_graphics(self): """ Save the graphics """ if self.img_base is None: return plt.savefig('{base}_{num:05d}.{type}'.format(base=self.img_base, num=self.img_counter, type=self.img_fmt)) self.img_counter += 1 def add_population(self, population): """ Add a population to the island :param population: List of dictionaries specifying population """ self._map.add_animals(population) def make_movie(self, movie_fmt=DEFAULT_MOVIE_FORMAT): """Create MPEG4 movie from visualization images saved.""" if self.img_base is None: raise RuntimeError('No filename defines') if movie_fmt == 'mp4': try: subprocess.check_call([FFMPEG_BINARY, '-i', '{}_%05d.png'.format(self.img_base), '-y', '-profile:v', 'baseline', '-level', '3.0', '-pix_fmt', 'yuv420p', '{}.{}'.format(self.img_base, movie_fmt)]) except subprocess.CalledProcessError as err: raise RuntimeError('Error: ffmpeg failed with: {}'.format(err)) elif movie_fmt == 'gif': try: subprocess.check_call([CONVERT_BINARY, '-delay', '1', '-loop', '0', '{}_*.png'.format(self.img_base), '{}.{}'.format(self.img_base, movie_fmt)]) except subprocess.CalledProcessError as err: raise RuntimeError( 'Error: convert failed with: {}'.format(err)) else: raise ValueError('Unknown movie format') @property def year(self): """Last year simulated.""" return self._year @property def num_animals(self): """Total number of animals on island.""" animal_count = 0 for species in self.animal_species: animal_count += self._map.total_animals_per_species(species) return animal_count @property def num_animals_per_species(self): """Number of animals per species in island, as dictionary.""" num_fauna_per_species = {} for species in self.animal_species: num_fauna_per_species[species] = self._map.\ total_animals_per_species(species) return num_fauna_per_species @property def animal_distribution(self): """Pandas DataFrame with animal count per species for each cell on island.""" animal_df = [] rows, cols = self._map.map_dims for row in range(rows): for col in range(cols): cell = self._map.cells[row, col] animal_count = cell.cell_fauna_count animal_df.append({'Row': row, 'Col': col, 'Herbivore': animal_count['Herbivore'], 'Carnivore': animal_count['Carnivore']}) return pd.DataFrame(animal_df)
class BioSim: def __init__(self, island_map, ini_pop, seed, ymax_animals=None, cmax_animals=None, img_base=None, img_fmt="png", hist_specs=None): """ :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 hist_specs: Specifications for histograms, see below :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} hist_specs is a dictionary with one entry per property for which a histogram shall be shown. \n For each property, a dictionary providing the maximum value and the bin width must be given, e.g., \n {'weight': {'max': 80, 'delta': 2}, 'fitness': {'max': 1.0, 'delta': 0.05}} \n Permitted properties are 'weight', 'age', 'fitness'. \n 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. \n img_base should contain a path and beginning of a file name. \n """ self.landscapes = { 'W': Water, 'L': Lowland, 'H': Highland, 'D': Desert } self.landscapes_with_parameters = [Highland, Lowland] self.animal_species = {'Carnivore': Carnivore, 'Herbivore': Herbivore} for char in island_map.replace('\n', ''): if char not in self.landscapes: raise ValueError( 'This given string contains unknown geographies') lengths = [len(line) for line in island_map.splitlines()] if len(set(lengths)) > 1: raise ValueError('This given string is not uniform') self.island_map = island_map self._map = Island(island_map) np.random.seed(seed) self.add_population(ini_pop) if ymax_animals is None: self.ymax_animals = 20000 else: self.ymax_animals = ymax_animals if cmax_animals is None: self.cmax_animals = {'Herbivore': 50, 'Carnivore': 20} else: self.cmax_animals = cmax_animals if img_base is None: self.img_base = DEFAULT_GRAPHICS_DIR + DEFAULT_GRAPHICS_NAME else: self.img_base = img_base if hist_specs is None: self._hist_specs = { 'weight': { 'max': 80, 'delta': 2 }, 'fitness': { 'max': 1.0, 'delta': 0.05 }, 'age': { 'max': 80, 'delta': 2 } } self.img_fmt = img_fmt self.img_counter = 0 self.vis = None self._year = 0 self.final_year = None def set_animal_parameters(self, species, params): """ Set parameters for animal species. \n :param species: String, name of animal species \n :param params: Dict with valid parameter specification for species \n """ if species in self.animal_species: species_type = self.animal_species[species] animal = species_type() animal.set_parameters(params) else: raise TypeError( species + ' parameters cant be assigned,there is no such data type') def set_landscape_parameters(self, landscape, params): """ Set parameters for landscape type. \n :param landscape: String, code letter for landscape \n :param params: Dict with valid parameter specification for landscape \n """ if landscape in self.landscapes: landscape_type = self.landscapes[landscape] if landscape_type in self.landscapes_with_parameters: landscape_type.set_parameters(params) else: raise ValueError(landscape + ' parameter is not valid') else: raise TypeError(landscape + ' parameters cannot be assigned, there is no such ' 'data type') def simulate(self, num_years, vis_years=200, img_years=None): """ Run simulation while visualizing the result. \n :param num_years: number of years to simulate \n :param vis_years: years between visualization updates \n :param img_years: years between visualizations saved to files \n (default: vis_years) \n Image files will be numbered consecutively. \n """ if img_years is None: img_years = vis_years self.final_year = self._year + num_years self.setup_graphics() if self._year > 1: self.vis.create_animal_graphs(self.final_year, self.ymax_animals, recreate=True) while self._year < self.final_year: if self._year % vis_years == 0: self.update_graphics() if (self._year + 1) % img_years == 0: self.save_graphics() self._map.life_cycle_in_rossumoya() self._year += 1 df = self.animal_distribution df.to_csv('data.csv', sep='\t', encoding='utf-8') def setup_graphics(self): """ Setup the graphics \n """ map_dims = self._map.map_dims if self.vis is None: fig = plt.figure(figsize=(16, 9)) self.vis = Graphics(self.island_map, fig, map_dims) self.vis.create_island_graph() self.vis.create_animal_graphs(self.final_year, self.ymax_animals) self.vis.animal_distribution_graphs() self.vis.create_histograms_setup() def update_graphics(self): """ Updates graphics with current data. \n """ df = self.animal_distribution rows, cols = self._map.map_dims dist_matrix_carnivore = np.array(df[['Carnivore']]).reshape(rows, cols) dist_matrix_herbivore = np.array(df[['Herbivore']]).reshape(rows, cols) # updates the line graphs herb_count, carn_count = list(self.num_animals_per_species.values()) self.vis.update_graphs(self._year, herb_count, carn_count) self.vis.update_herbivore_distribution(dist_matrix_herbivore) self.vis.update_carnivore_distribution(dist_matrix_carnivore) # histogram self.vis.update_histogram(fit_list=self.animals_fitness, age_list=self.animal_ages, wt_list=self.animal_weights) # plt.pause(1) plt.pause(1e-6) self.vis.set_year def save_graphics(self): """ Save the graphics \n """ if self.img_base is None: return plt.savefig('{base}_{num:05d}.{type}'.format(base=self.img_base, num=self.img_counter, type=self.img_fmt)) self.img_counter += 1 def add_population(self, population): """ Add a population to the island \n :param population: List of dictionaries specifying population \n """ self._map.add_animals(population) def make_movie(self, movie_fmt=DEFAULT_MOVIE_FORMAT): """ Source: Hans Ekkehard Plesser, Randviz project \n Create MPEG4 movie from visualization images saved. \n """ if self.img_base is None: raise RuntimeError('No filename defined') if movie_fmt == 'mp4': try: subprocess.check_call([ FFMPEG_BINARY, '-i', '{}_%05d.png'.format(self.img_base), '-y', '-profile:v', 'baseline', '-level', '3.0', '-pix_fmt', 'yuv420p', '{}.{}'.format(self.img_base, movie_fmt) ]) except subprocess.CalledProcessError as err: raise RuntimeError('Error: ffmpeg failed with: {}'.format(err)) elif movie_fmt == 'gif': try: subprocess.check_call([ CONVERT_BINARY, '-delay', '1', '-loop', '0', '{}_*.png'.format(self.img_base), '{}.{}'.format(self.img_base, movie_fmt) ]) except subprocess.CalledProcessError as err: raise RuntimeError( 'Error: convert failed with: {}'.format(err)) else: raise ValueError('Unknown movie format') @property def year(self): """ Last year simulated. \n """ return self._year @property def num_animals(self): """ Total number of animals on island. \n """ animal_count = 0 for species in self.animal_species: animal_count += self._map.number_of_animals_per_species(species) return animal_count @property def num_animals_per_species(self): """ Number of animals per species in island, as dictionary. \n """ num_fauna_per_species = {} for species in self.animal_species: num_fauna_per_species[ species] = self._map.number_of_animals_per_species(species) return num_fauna_per_species @property def animal_distribution(self): """ Pandas DataFrame with animal count per species for each cell on the island. \n """ animal_df = [] rows, cols = self._map.map_dims for row in range(rows): for col in range(cols): cell = self._map.cells[row, col] animal_count = cell.cell_fauna_count animal_df.append({ 'Row': row, 'Col': col, 'Herbivore': animal_count['Herbivore'], 'Carnivore': animal_count['Carnivore'] }) return pd.DataFrame(animal_df) @property def animal_weights(self): """ Returns a dictionary with weights of each animal according to the species /n """ weights = {"Herbivore": [], "Carnivore": []} rows, cols = self._map.map_dims for row in range(rows): for col in range(cols): cell = self._map.cells[row][col] for animal in cell.fauna_dict["Herbivore"]: weights['Herbivore'].append(animal.weight) for animal in cell.fauna_dict["Carnivore"]: weights['Carnivore'].append(animal.weight) return weights @property def animals_fitness(self): """ Returns a dictionary with weights of each animal according to the species /n """ fitness = {"Herbivore": [], "Carnivore": []} rows, cols = self._map.map_dims for row in range(rows): for col in range(cols): cell = self._map.cells[row][col] for animal in cell.fauna_dict["Herbivore"]: fitness['Herbivore'].append(animal.animal_fitness) for animal in cell.fauna_dict["Carnivore"]: fitness['Carnivore'].append(animal.animal_fitness) return fitness @property def animal_ages(self): """ Returns a dictionary with weights of each animal according to the species /n """ age = {"Herbivore": [], "Carnivore": []} rows, cols = self._map.map_dims for row in range(rows): for col in range(cols): cell = self._map.cells[row][col] for animal in cell.fauna_dict["Herbivore"]: age['Herbivore'].append(animal.age) for animal in cell.fauna_dict["Carnivore"]: age['Carnivore'].append(animal.age) return age
def test_migration_carnivores(self): """Tests that the carnivores migrate and that they do not move before the execution-function is called""" population = [{ 'loc': (3, 2), 'pop': [{ 'species': 'Herbivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (3, 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': (3, 1), 'pop': [{ 'species': 'Carnivore', 'age': 5, 'weight': 13.3 }] }, { 'loc': (3, 1), '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) b.calculate_fitness((3, 1), a.carns) b.migration_calculations(a, c, a.carns) assert len(a.carns[(3, 1)]) == 9 assert (3, 2) not in a.carns.keys() assert len(b.idx_for_animals_to_remove) > 0 b.migration_execution(a, a.carns) assert len(a.carns[(3, 2)]) > 0 assert len(a.carns[(3, 1)]) < 9
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'])