Пример #1
0
    def test_migration(self):
        """
        Test for migration

        Returns
        -------

        """
        i1 = Island(ISLE_MAP2)
        Herbivore.set_parameters({'mu': 1.0})
        Carnivore.set_parameters({'mu': 1.0})

        coordinate = (2, 3)
        i1.add_animal_island(coordinate, INI_HERB[0]['pop'])
        i1.add_animal_island(coordinate, INI_CARN[0]['pop'])

        for herbivore in i1.cells[2, 3].herbivores:
            herbivore.fitness = 1
        for carnivore in i1.cells[2, 3].carnivores:
            carnivore.fitness = 1

        i1.migration()

        # can only move left or down
        len_left_herb = len(i1.cells[2, 2].herbivores)
        len_down_herb = len(i1.cells[3, 3].herbivores)
        len_left_carn = len(i1.cells[2, 2].carnivores)
        len_down_carn = len(i1.cells[3, 3].carnivores)

        assert (len_down_herb + len_left_herb,
                len_left_carn + len_down_carn) == (20, 20)
Пример #2
0
    def test_get_pi_values_carnivores_with_herbs_and_carns(self):
        """
        Test for getting pi values

        We also test with extra herbivores and carnivores in adjacent cells

        Returns
        -------

        """
        i1 = Island(ISLE_MAP2)
        coordinate = (2, 3)
        i1.add_animal_island((2, 2), INI_HERB[0]['pop'])
        i1.add_animal_island((3, 3), INI_HERB[0]['pop'])
        i1.add_animal_island((3, 3), INI_CARN[0]['pop'])

        correct_weight_left = 400
        correct_weight_right = 400
        eleft = correct_weight_left / 50
        edown = correct_weight_right / ((20 + 1) * 50)

        correct_pi_left = math.exp(1 * eleft)
        correct_pi_up = 0
        correct_pi_right = 0
        correct_pi_down = math.exp(1 * edown)
        assert i1.get_pi_values_carnivores(coordinate) == pytest.approx(
            (correct_pi_right, correct_pi_up, correct_pi_left,
             correct_pi_down))
Пример #3
0
    def test_add_animal_island(self):
        """
        Test for adding animals to map.

        Returns
        -------

        """
        i1 = Island(ISLE_MAP)
        assert len(i1.cells[1, 1].herbivores) == 0
        i1.add_animal_island((1, 1), INI_HERB[0]['pop'])
        assert len(i1.cells[1, 1].herbivores) == 20

        for animal in i1.cells[1, 1].herbivores:
            assert isinstance(animal, Herbivore)

        assert len(i1.cells[1, 1].carnivores) == 0
        i1.add_animal_island((1, 1), INI_CARN[0]['pop'])
        assert len(i1.cells[1, 1].carnivores) == 20

        for animal in i1.cells[1, 1].carnivores:
            assert isinstance(animal, Carnivore)
Пример #4
0
    def test_cell_move_herbivore_and_carnivore(self):
        """
        Test for moving herbivore and carnivore

        Returns
        -------

        """
        i1 = Island(ISLE_MAP2)
        Herbivore.set_parameters({'mu': 1.0})
        Carnivore.set_parameters({'mu': 1.0})

        coordinate = (2, 3)
        i1.add_animal_island(coordinate, INI_HERB[0]['pop'])
        i1.add_animal_island(coordinate, INI_CARN[0]['pop'])
        for herbivore in i1.cells[2, 3].herbivores:
            herbivore.fitness = 1
        for carnivore in i1.cells[2, 3].carnivores:
            carnivore.fitness = 1

        assert len(i1.cells[2, 3].herbivores) == 20
        assert len(i1.cells[2, 3].carnivores) == 20
        i1.cell_move_herbivores(coordinate)
        i1.cell_move_carnivores(coordinate)

        assert len(i1.cells[2, 3].herbivores) == 0
        assert len(i1.cells[2, 3].carnivores) == 0

        # can only move left or down
        len_left_herb = len(i1.cells[2, 2].herbivores_new)
        len_down_herb = len(i1.cells[3, 3].herbivores_new)

        len_left_carn = len(i1.cells[2, 2].carnivores_new)
        len_down_carn = len(i1.cells[3, 3].carnivores_new)

        assert (len_down_herb + len_left_herb,
                len_left_carn + len_down_carn) == (20, 20)
Пример #5
0
class BioSim:
    """
    class for the simulation
    """

    def __init__(self, island_map, ini_pop=None, seed=12345, img_dir=None,
                 img_name=DEFAULT_GRAPHICS_NAME, img_format='png'):
        random.seed(seed)
        np.random.seed(seed)
        self.island_map = island_map
        self.island = Island(island_map)
        if ini_pop is not None:
            self.add_population(ini_pop)
        self.island.animals_on_island()
        self.fig = None
        self.ax1 = None
        self.ax2 = None
        self.ax3 = None
        self.ax4 = None
        self.line_herbivore = None
        self.line_carnivore = None
        self.herbivore_density = None
        self.carnivore_density = None
        self.year_ax = None
        self.year_txt = None
        self.vis_index = 0
        self.last_step = 0
        self.year = 0
        self.x_lim = (0, 100)  # line graph ordinate limits, default values
        self.y_lim = (0, 15000)  # line graph abscissa limits, default values
        self.user_limits = False
        self.herbivore_color_code = (0, 300)  # population map color code limits
        self.carnivore_color_code = (0, 300)  # population map color code limits

        self.img_counter = 0
        if img_dir is not None:
            self.img_base = os.path.join(img_dir, img_name)
        else:
            self.img_base = None
        self.img_format = img_format

    def add_population(self, population):
        """
        Method for adding a population to the island

        Parameters
        ----------
        population : list
            A list of animals, containing dictionaries for each animal

        Returns
        -------

        """

        island_shape = np.shape(self.island.cells)  # type: tuple
        points_on_island = []
        for i in range(1, island_shape[0] + 1):
            for j in range(1, island_shape[1] + 1):
                points_on_island.append((i, j))

        for dictionary in population:

            if type(dictionary['loc']) is tuple:
                if dictionary['loc'] in points_on_island:
                    coordinates = dictionary['loc']
                else:
                    raise IndexError('The coordinates must exist on the island'
                                     ': (x, y). (1,1) is the upper left corner')
            else:
                raise TypeError("'loc' must be tuple with coordinates: (x, y)")

            coordinates = (coordinates[0] - 1, coordinates[1] - 1)
            animals = dictionary['pop']
            self.island.add_animal_island(coordinates, animals)

    def status_year(self):
        """
        Method for getting the current year on screen

        Returns
        -------
        self.year : int
            the current year

        """
        print('Number of years simulated: ', self.year)
        return self.year

    def status_number_of_animals_total(self):
        """
        Method for getting the total amount of animals on screen

        Returns
        -------

        """
        total_animals = int(self.island.number_of_herbivores_island()
                            + self.island.number_of_carnivores_island())

        print('Total number of animals: ', total_animals)
        return total_animals

    def status_number_of_animals_by_species(self):
        """
        Method for getting number of herbivores and carnivores on screen

        Returns
        -------
        dictionary : dict
            A dictionary containing number herbivores and carnivores on island

        """
        return {'herbivores': int(self.island.number_of_herbivores_island()),
                'carnivores': int(self.island.number_of_carnivores_island())}

    def status_per_cell_animal_count(self):
        """
        Method for getting number of each species in each cell, using pandas

        Returns
        -------
        df : pandas.DataFrame
            A table containing each cell and the number of each species on
            each cell

        """

        shape = np.shape(self.island.herbivores_on_island)  # type: tuple
        coordinates = []
        for i in range(1, shape[0] + 1):
            for j in range(1, shape[1] + 1):
                coordinates.append((i, j))
        coordinates = np.array(coordinates)

        col1 = coordinates[:, 0]
        col2 = coordinates[:, 1]
        col3 = self.island.herbivores_on_island.ravel().astype(np.int)
        col4 = self.island.carnivores_on_island.ravel().astype(np.int)

        df = pd.DataFrame({'x': col1, 'y': col2, 'herbivores': col3,
                           'carnivores': col4}, index=zip(col1, col2))
        df = df[['x', 'y', 'herbivores', 'carnivores']]
        return df

    def set_axis_limits(self, x_limits=None, y_limits=None):
        """
        Method for setting the x and y-limits on the line graph

        Parameters
        ----------
        x_limits : tuple, list
            tuple or list containing the lower and upper boundaries for the
            x axis

        y_limits : tuple, list
            tuple or list containing the lower and upper boundaries for the
            y axis

        Returns
        -------

        """

        if x_limits is not None:
            if type(x_limits) is tuple or type(x_limits) is list:
                self.x_lim = x_limits
                self.user_limits = True
            else:
                raise TypeError('x_limits must be a tuple or list of 2 numbers')

        if y_limits is not None:
            if type(y_limits) is tuple or type(y_limits) is list:
                self.y_lim = y_limits
            else:
                raise TypeError('y_limits must be a tuple or list of 2 numbers')

    def reset_axis_limits(self):
        """
        Method for resetting the axis limits for both x and y axis

        Returns
        -------

        """
        self.user_limits = False
        self.y_lim = (0, 15000)  # default values
        # Dynamic x-axis will be used

    def set_color_code_limits(self, herbivore_colors, carnivore_colors):
        """
        Method for setting the color code limits for each species

        Parameters
        ----------
        herbivore_colors : tuple
            boundaries for the color representation for number of herbivores
        carnivore_colors : tuple
            boundaries for the color representation for number of herbivores


        Returns
        -------

        """
        if type(herbivore_colors) is tuple or type(herbivore_colors) is list:
            self.herbivore_color_code = herbivore_colors
        else:
            raise TypeError('herbivore_colors must be a tuple or list of 2 int')
        if type(carnivore_colors) is tuple or type(carnivore_colors) is list:
            self.carnivore_color_code = carnivore_colors
        else:
            raise TypeError('carnivore_colors must be a tuple or list of 2 int')

    def reset_color_code_limits(self):
        """
        Method for resetting the color code to default values

        Returns
        -------

        """
        self.herbivore_color_code = (0, 300)  # default values
        self.carnivore_color_code = (0, 300)  # default values

    def year_counter(self):
        """
        Method for updating the counter on screen

        Returns
        -------

        """
        """
        Source: Plesser's Repository: (18.01.2018)
        NMBU_INF200_H17 / Lectures / J05 / Plotting / time_counter.py"""

        if self.year_ax is None:
            self.year_ax = self.fig.add_axes([0.4, 0.83, 0.2, 0.2])
            self.year_ax.axis('off')

            self.year_txt = \
                self.year_ax.text(0.5, 0.5, 'Year: {:5}'.format(self.year),
                                  horizontalalignment='center',
                                  verticalalignment='center',
                                  transform=self.year_ax.transAxes, fontsize=16)

        self.year_txt.set_text('Year: {:5}'.format(self.year))

    def make_rgb_map(self):
        """
        Function to make RGB map from island-string

        Returns
        -------

        """
        """
        Source: Plesser's Repository:
        NMBU_INF200_H17 / Lectures / J05 / Plotting / mapping.py (18.01.2018)"""

        rgb_value = {'O': (0.0, 0.0, 1.0),  # blue
                     'M': (0.5, 0.5, 0.5),  # grey
                     'J': (0.0, 0.6, 0.0),  # dark green
                     'S': (0.5, 1.0, 0.5),  # light green
                     'D': (1.0, 1.0, 0.5)}  # light yellow

        map_rgb = [[rgb_value[column] for column in row]
                   for row in self.island_map.splitlines()]

        self.ax1.imshow(map_rgb, interpolation='nearest')
        self.ax1.set_xticks(range(len(map_rgb[0])))
        self.ax1.set_xticklabels(range(1, 1 + len(map_rgb[0])))
        self.ax1.set_yticks(range(len(map_rgb)))
        self.ax1.set_yticklabels(range(1, 1 + len(map_rgb)))
        self.ax1.set_title('Map of Rossumøya')

        axlg = self.fig.add_axes([0.44, 0.525, 0.1, 0.4])  # llx, lly, w, h
        axlg.axis('off')
        for ix, name in enumerate(('Ocean', 'Mountain', 'Jungle',
                                   'Savannah', 'Desert')):
            axlg.add_patch(plt.Rectangle((0., ix * 0.2), 0.3, 0.1,
                                         edgecolor='none',
                                         facecolor=rgb_value[name[0]]))
            axlg.text(0.35, ix * 0.2, name, transform=axlg.transAxes)

    def make_line_plot(self, vis_steps):
        """
        Method for making the line plot

        Parameters
        ----------
        vis_steps
            Variable entered in BioSim.simulation.
            How often the graphics are updated.
            Examples: 1 = updated each year. 2 = every second year

        Returns
        -------

        """

        if self.user_limits:
            self.ax2.set_xlim(self.x_lim[0], self.x_lim[1])
        else:
            self.ax2.set_xlim(0, self.last_step + 1)

        self.ax2.set_ylim(self.y_lim[0], self.y_lim[1])
        self.ax2.set_title('Populations')

        if self.line_herbivore is None:
            self.line_herbivore = \
                self.ax2.plot(np.arange(0, self.last_step + 1, vis_steps),
                              np.nan * np.ones(
                              len(np.arange(0, self.last_step + 1, vis_steps))),
                              'b-')[0]
            self.line_carnivore = \
                self.ax2.plot(np.arange(0, self.last_step + 1, vis_steps),
                              np.nan * np.ones(
                              len(np.arange(0, self.last_step + 1, vis_steps))),
                              'r-')[0]
            self.ax2.legend(['Herbivores', 'Carnivores'])
        else:
            xdata, ydata = self.line_herbivore.get_data()
            xnew = np.arange(xdata[-1] + 1, self.last_step + 1, vis_steps)
            if len(xnew) > 0:
                ynew = np.nan * np.ones_like(xnew)
                self.line_herbivore.set_data(np.hstack((xdata, xnew)),
                                             np.hstack((ydata, ynew)))
            xdata, ydata = self.line_carnivore.get_data()
            xnew = np.arange(xdata[-1] + 1, self.last_step + 1, vis_steps)
            if len(xnew) > 0:
                ynew = np.nan * np.ones_like(xnew)
                self.line_carnivore.set_data(np.hstack((xdata, xnew)),
                                             np.hstack((ydata, ynew)))

    def update_line_plot(self):
        """
        Method for updating the line plot

        Returns
        -------

        """
        ydata = self.line_herbivore.get_ydata()
        ydata[self.vis_index] = self.island.number_of_herbivores_island()
        self.line_herbivore.set_ydata(ydata)

        ydata = self.line_carnivore.get_ydata()
        ydata[self.vis_index] = self.island.number_of_carnivores_island()
        self.line_carnivore.set_ydata(ydata)
        plt.pause(1e-6)
        self.vis_index += 1

    def make_herbivore_density_map(self):
        """
        Method for making the hebrivore density map

        Returns
        -------

        """
        """
        Source: Plesser's Repository:
        NMBU_INF200_H17 / Lectures / J05 / Plotting / mapping.py (18.01.2018)"""

        animals = self.island.herbivores_on_island

        self.herbivore_density = \
            self.ax3.imshow(animals, interpolation='nearest',
                            vmin=self.herbivore_color_code[0],
                            vmax=self.herbivore_color_code[1])
        self.ax3.set_xticks(range(len(animals[0])))
        self.ax3.set_xticklabels(range(1, 1 + len(animals[0])))
        self.ax3.set_yticks(range(len(animals)))
        self.ax3.set_yticklabels(range(1, 1 + len(animals)))
        self.ax3.set_title('Herbivore population density')

    def update_herbivore_density_map(self):
        """
        Method for updating the herbivore density map

        Returns
        -------

        """
        self.herbivore_density.set_data(self.island.herbivores_on_island)

    def make_carnivore_density_map(self):
        """
        Method for making the carnivore density map

        Returns
        -------

        """
        """
        Source: Plesser's Repository:
        NMBU_INF200_H17 / Lectures / J05 / Plotting / mapping.py (18.01.2018)"""

        animals = self.island.carnivores_on_island

        self.carnivore_density = \
            self.ax4.imshow(animals, interpolation='nearest',
                            vmin=self.carnivore_color_code[0],
                            vmax=self.carnivore_color_code[1])
        self.ax4.set_xticks(range(len(animals[0])))
        self.ax4.set_xticklabels(range(1, 1 + len(animals[0])))
        self.ax4.set_yticks(range(len(animals)))
        self.ax4.set_yticklabels(range(1, 1 + len(animals)))
        self.ax4.set_title('Carnivore population density')

    def update_carnivore_density_map(self):
        """
        Method for updating the carnivore density map

        Returns
        -------

        """
        self.carnivore_density.set_data(self.island.carnivores_on_island)

    def make_visualization(self, vis_steps):
        """
        Method for making the visualization

        Parameters
        ----------
        vis_steps : int
            How often the graphics should be updated, in years

        Returns
        -------

        """
        if self.fig is None:
            self.fig = plt.figure()

            self.ax1 = self.fig.add_subplot(2, 2, 1)
            self.ax2 = self.fig.add_subplot(2, 2, 2)
            self.ax3 = self.fig.add_subplot(2, 2, 3)
            self.ax4 = self.fig.add_subplot(2, 2, 4)

            self.make_rgb_map()
            self.make_herbivore_density_map()
            self.make_carnivore_density_map()
            self.year_counter()

        self.make_line_plot(vis_steps)

    def update_visualization(self):
        """
        Method for updating the visualization

        Returns
        -------

        """
        self.update_line_plot()
        self.update_herbivore_density_map()
        self.update_carnivore_density_map()
        self.year_counter()

    def save_graphics(self):
        """
        Saves graphics to file if file name given.

        Returns
        -------

        """

        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_format))
        self.img_counter += 1

    def make_movie(self, movie_format=DEFAULT_MOVIE_FORMAT):
        """
        Creates MPEG4 movie from visualization images saved.

        .. :note:
            Requires ffmpeg

        The movie is stored as img_base + movie_format
        """

        if self.img_base is None:
            raise RuntimeError("No filename defined.")

        if movie_format == 'mp4':
            try:
                # Parameters chosen according to
                # http://trac.ffmpeg.org/wiki/Encode/H.264,
                # section "Compatibility"
                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_format)])
            except subprocess.CalledProcessError as err:
                raise RuntimeError('ERROR: ffmpeg failed with: {}'.format(err))
            """
        elif movie_format == 'gif':
            try:
                subprocess.check_call([CONVERT_BINARY,
                                       '-delay', '1',
                                       '-loop', '0',
                                       '{}_*.png'.format(self.img_base),
                                       '{}.{}'.format(self.img_base,
                                                      movie_format)])
            except subprocess.CalledProcessError as err:
                raise RuntimeError('ERROR: convert failed with: {}'.format(err))
        else:
            raise ValueError('Unknown movie format: ' + movie_format)
            """

    def simulate(self, num_steps, vis_steps=1, img_steps=None):
        """
        Method for simulating the entire island

        Parameters
        ----------
        num_steps : int
            Numbers of years to be simulated
        vis_steps : int
            How often the graphics should be updated, in years
        img_steps : How often the graphic should be saved

        Returns
        -------

        """
        plt.ion()

        if img_steps is not None:
            if img_steps % vis_steps != 0:
                raise ValueError("'img_steps' must be multiple of 'vis_steps'")

        self.last_step += num_steps

        self.make_visualization(vis_steps)

        # Run through num_steps years
        while self.year <= self.last_step:
            self.island.cycle()

            if (self.year % vis_steps) == 0:
                self.update_visualization()
            if img_steps is not None:
                if self.year % img_steps == 0:
                    self.save_graphics()

            self.year += 1
            if self.island.number_of_herbivores_island() + \
               self.island.number_of_carnivores_island() == 0:
                print('All the animals on the island died')
                break