From 33d74c61cf9d0c0e298279971347fcfe3f66d10b Mon Sep 17 00:00:00 2001 From: Jan Kwakkel Date: Sat, 27 Jan 2024 12:17:57 +0100 Subject: [PATCH 1/2] more pythonic implementation of wolf sheep --- benchmarks/WolfSheep/__init__.py | 14 --- benchmarks/WolfSheep/agents.py | 111 ------------------- benchmarks/WolfSheep/random_walk.py | 37 ------- benchmarks/WolfSheep/wolf_sheep.py | 159 +++++++++++++++++++++++----- 4 files changed, 133 insertions(+), 188 deletions(-) delete mode 100644 benchmarks/WolfSheep/agents.py delete mode 100644 benchmarks/WolfSheep/random_walk.py diff --git a/benchmarks/WolfSheep/__init__.py b/benchmarks/WolfSheep/__init__.py index 98b1e9fdfed..e69de29bb2d 100644 --- a/benchmarks/WolfSheep/__init__.py +++ b/benchmarks/WolfSheep/__init__.py @@ -1,14 +0,0 @@ -from .wolf_sheep import WolfSheep - -if __name__ == "__main__": - # for profiling this benchmark model - import time - - # model = WolfSheep(15, 25, 25, 60, 40, 0.2, 0.1, 20) - model = WolfSheep(15, 100, 100, 1000, 500, 0.4, 0.2, 20) - - start_time = time.perf_counter() - for _ in range(100): - model.step() - - print(time.perf_counter() - start_time) diff --git a/benchmarks/WolfSheep/agents.py b/benchmarks/WolfSheep/agents.py deleted file mode 100644 index 9ad9f7652cc..00000000000 --- a/benchmarks/WolfSheep/agents.py +++ /dev/null @@ -1,111 +0,0 @@ -from mesa import Agent - -from .random_walk import RandomWalker - - -class Sheep(RandomWalker): - """ - A sheep that walks around, reproduces (asexually) and gets eaten. - - The init is the same as the RandomWalker. - """ - - def __init__(self, unique_id, pos, model, moore, energy=None): - super().__init__(unique_id, pos, model, moore=moore) - self.energy = energy - - def step(self): - """ - A model step. Move, then eat grass and reproduce. - """ - self.random_move() - - # Reduce energy - self.energy -= 1 - - # If there is grass available, eat it - this_cell = self.model.grid.get_cell_list_contents(self.pos) - grass_patch = next(obj for obj in this_cell if isinstance(obj, GrassPatch)) - if grass_patch.fully_grown: - self.energy += self.model.sheep_gain_from_food - grass_patch.fully_grown = False - - # Death - if self.energy < 0: - self.model.grid.remove_agent(self) - self.model.schedule.remove(self) - elif self.random.random() < self.model.sheep_reproduce: - # Create a new sheep: - self.energy /= 2 - lamb = Sheep( - self.model.next_id(), self.pos, self.model, self.moore, self.energy - ) - self.model.grid.place_agent(lamb, self.pos) - self.model.schedule.add(lamb) - - -class Wolf(RandomWalker): - """ - A wolf that walks around, reproduces (asexually) and eats sheep. - """ - - def __init__(self, unique_id, pos, model, moore, energy=None): - super().__init__(unique_id, pos, model, moore=moore) - self.energy = energy - - def step(self): - self.random_move() - self.energy -= 1 - - # If there are sheep present, eat one - x, y = self.pos - this_cell = self.model.grid.get_cell_list_contents([self.pos]) - sheep = [obj for obj in this_cell if isinstance(obj, Sheep)] - if len(sheep) > 0: - sheep_to_eat = self.random.choice(sheep) - self.energy += self.model.wolf_gain_from_food - - # Kill the sheep - self.model.grid.remove_agent(sheep_to_eat) - self.model.schedule.remove(sheep_to_eat) - - # Death or reproduction - if self.energy < 0: - self.model.grid.remove_agent(self) - self.model.schedule.remove(self) - elif self.random.random() < self.model.wolf_reproduce: - # Create a new wolf cub - self.energy /= 2 - cub = Wolf( - self.model.next_id(), self.pos, self.model, self.moore, self.energy - ) - self.model.grid.place_agent(cub, cub.pos) - self.model.schedule.add(cub) - - -class GrassPatch(Agent): - """ - A patch of grass that grows at a fixed rate and it is eaten by sheep - """ - - def __init__(self, unique_id, pos, model, fully_grown, countdown): - """ - Creates a new patch of grass - - Args: - grown: (boolean) Whether the patch of grass is fully grown or not - countdown: Time for the patch of grass to be fully grown again - """ - super().__init__(unique_id, model) - self.fully_grown = fully_grown - self.countdown = countdown - self.pos = pos - - def step(self): - if not self.fully_grown: - if self.countdown <= 0: - # Set as fully grown - self.fully_grown = True - self.countdown = self.model.grass_regrowth_time - else: - self.countdown -= 1 diff --git a/benchmarks/WolfSheep/random_walk.py b/benchmarks/WolfSheep/random_walk.py deleted file mode 100644 index e55a1f7ed2b..00000000000 --- a/benchmarks/WolfSheep/random_walk.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Generalized behavior for random walking, one grid cell at a time. -""" - -from mesa import Agent - - -class RandomWalker(Agent): - """ - Class implementing random walker methods in a generalized manner. - - Not intended to be used on its own, but to inherit its methods to multiple - other agents. - - """ - - def __init__(self, unique_id, pos, model, moore=True): - """ - grid: The MultiGrid object in which the agent lives. - x: The agent's current x coordinate - y: The agent's current y coordinate - moore: If True, may move in all 8 directions. - Otherwise, only up, down, left, right. - """ - super().__init__(unique_id, model) - self.pos = pos - self.moore = moore - - def random_move(self): - """ - Step one cell in any allowable direction. - """ - # Pick the next cell from the adjacent cells. - next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True) - next_move = self.random.choice(next_moves) - # Now move: - self.model.grid.move_agent(self, next_move) diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index b08afbc57ba..384464be50f 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -8,15 +8,114 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ +import math -import mesa +from mesa import Model, Agent from mesa.space import MultiGrid from mesa.time import RandomActivationByType -from .agents import GrassPatch, Sheep, Wolf +class Animal(Agent): -class WolfSheep(mesa.Model): + def __init__(self, unique_id, model, moore, energy, p_reproduce, energy_from_food): + super().__init__(unique_id, model) + self.energy = energy + self.p_reproduce = p_reproduce + self.energy_from_food = energy_from_food + self.moore = moore + + def random_move(self): + next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True) + next_move = self.random.choice(next_moves) + # Now move: + self.model.grid.move_agent(self, next_move) + + def spawn_offspring(self): + self.energy /= 2 + offspring = self.__class__( + self.model.next_id(), self.model, self.moore, self.energy, self.p_reproduce, self.energy_from_food + ) + self.model.grid.place_agent(offspring, self.pos) + self.model.schedule.add(offspring) + + def feed(self): + ... + + def die(self): + self.model.grid.remove_agent(self) + self.remove() + + def step(self): + self.random_move() + self.energy -= 1 + + self.feed() + + if self.energy < 0: + self.die() + elif self.random.random() < self.p_reproduce: + self.spawn_offspring() + + +class Sheep(Animal): + """ + A sheep that walks around, reproduces (asexually) and gets eaten. + + The init is the same as the RandomWalker. + """ + + def feed(self): + # If there is grass available, eat it + agents = self.model.grid.get_cell_list_contents(self.pos) + grass_patch = next(obj for obj in agents if isinstance(obj, GrassPatch)) + if grass_patch.fully_grown: + self.energy += self.energy_from_food + grass_patch.fully_grown = False + +class Wolf(Animal): + """ + A wolf that walks around, reproduces (asexually) and eats sheep. + """ + + def feed(self): + agents = self.model.grid.get_cell_list_contents(self.pos) + sheep = [obj for obj in agents if isinstance(obj, Sheep)] + if len(sheep) > 0: + sheep_to_eat = self.random.choice(sheep) + self.energy += self.energy + + # Kill the sheep + sheep_to_eat.die() + + +class GrassPatch(Agent): + """ + A patch of grass that grows at a fixed rate and it is eaten by sheep + """ + + def __init__(self, unique_id, model, fully_grown, countdown): + """ + Creates a new patch of grass + + Args: + grown: (boolean) Whether the patch of grass is fully grown or not + countdown: Time for the patch of grass to be fully grown again + """ + super().__init__(unique_id, model) + self.fully_grown = fully_grown + self.countdown = countdown + + def step(self): + if not self.fully_grown: + if self.countdown <= 0: + # Set as fully grown + self.fully_grown = True + self.countdown = self.model.grass_regrowth_time + else: + self.countdown -= 1 + + +class WolfSheep(Model): """ Wolf-Sheep Predation Model @@ -24,17 +123,18 @@ class WolfSheep(mesa.Model): """ def __init__( - self, - seed, - height, - width, - initial_sheep, - initial_wolves, - sheep_reproduce, - wolf_reproduce, - grass_regrowth_time, - wolf_gain_from_food=13, - sheep_gain_from_food=5, + self, + seed, + height, + width, + initial_sheep, + initial_wolves, + sheep_reproduce, + wolf_reproduce, + grass_regrowth_time, + wolf_gain_from_food=13, + sheep_gain_from_food=5, + moore=False ): """ Create a new Wolf-Sheep model with the given parameters. @@ -49,6 +149,7 @@ def __init__( grass_regrowth_time: How long it takes for a grass patch to regrow once it is eaten sheep_gain_from_food: Energy sheep gain from grass, if enabled. + moore: """ super().__init__(seed=seed) # Set parameters @@ -56,34 +157,29 @@ def __init__( self.width = width self.initial_sheep = initial_sheep self.initial_wolves = initial_wolves - self.sheep_reproduce = sheep_reproduce - self.wolf_reproduce = wolf_reproduce - self.wolf_gain_from_food = wolf_gain_from_food self.grass_regrowth_time = grass_regrowth_time - self.sheep_gain_from_food = sheep_gain_from_food self.schedule = RandomActivationByType(self) self.grid = MultiGrid(self.height, self.width, torus=False) - # Create sheep: - for _i in range(self.initial_sheep): + for _ in range(self.initial_sheep): pos = ( self.random.randrange(self.width), self.random.randrange(self.height), ) - energy = self.random.randrange(2 * self.sheep_gain_from_food) - sheep = Sheep(self.next_id(), pos, self, True, energy) + energy = self.random.randrange(2 * sheep_gain_from_food) + sheep = Sheep(self.next_id(), self, moore, energy, sheep_reproduce, sheep_gain_from_food) self.grid.place_agent(sheep, pos) self.schedule.add(sheep) # Create wolves - for _i in range(self.initial_wolves): + for _ in range(self.initial_wolves): pos = ( self.random.randrange(self.width), self.random.randrange(self.height), ) - energy = self.random.randrange(2 * self.wolf_gain_from_food) - wolf = Wolf(self.next_id(), pos, self, True, energy) + energy = self.random.randrange(2 * wolf_gain_from_food) + wolf = Wolf(self.next_id(), self, moore, energy, wolf_reproduce, wolf_gain_from_food) self.grid.place_agent(wolf, pos) self.schedule.add(wolf) @@ -95,9 +191,20 @@ def __init__( countdown = self.grass_regrowth_time else: countdown = self.random.randrange(self.grass_regrowth_time) - patch = GrassPatch(self.next_id(), pos, self, fully_grown, countdown) + patch = GrassPatch(self.next_id(), self, fully_grown, countdown) self.grid.place_agent(patch, pos) self.schedule.add(patch) def step(self): self.schedule.step() + + +if __name__ == "__main__": + import time + + model = WolfSheep(15, 25, 25, 60, 40, 0.2, 0.1, 20) + + start_time = time.perf_counter() + for _ in range(100): + model.step() + print("Time:", time.perf_counter() - start_time) \ No newline at end of file From 6343f9d4c8011b5aa2c68034a7d5d8876c01156a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:18:52 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- benchmarks/WolfSheep/wolf_sheep.py | 51 +++++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/benchmarks/WolfSheep/wolf_sheep.py b/benchmarks/WolfSheep/wolf_sheep.py index 384464be50f..4d882a0ed48 100644 --- a/benchmarks/WolfSheep/wolf_sheep.py +++ b/benchmarks/WolfSheep/wolf_sheep.py @@ -8,15 +8,13 @@ Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. """ -import math -from mesa import Model, Agent +from mesa import Agent, Model from mesa.space import MultiGrid from mesa.time import RandomActivationByType class Animal(Agent): - def __init__(self, unique_id, model, moore, energy, p_reproduce, energy_from_food): super().__init__(unique_id, model) self.energy = energy @@ -33,7 +31,12 @@ def random_move(self): def spawn_offspring(self): self.energy /= 2 offspring = self.__class__( - self.model.next_id(), self.model, self.moore, self.energy, self.p_reproduce, self.energy_from_food + self.model.next_id(), + self.model, + self.moore, + self.energy, + self.p_reproduce, + self.energy_from_food, ) self.model.grid.place_agent(offspring, self.pos) self.model.schedule.add(offspring) @@ -72,6 +75,7 @@ def feed(self): self.energy += self.energy_from_food grass_patch.fully_grown = False + class Wolf(Animal): """ A wolf that walks around, reproduces (asexually) and eats sheep. @@ -123,18 +127,18 @@ class WolfSheep(Model): """ def __init__( - self, - seed, - height, - width, - initial_sheep, - initial_wolves, - sheep_reproduce, - wolf_reproduce, - grass_regrowth_time, - wolf_gain_from_food=13, - sheep_gain_from_food=5, - moore=False + self, + seed, + height, + width, + initial_sheep, + initial_wolves, + sheep_reproduce, + wolf_reproduce, + grass_regrowth_time, + wolf_gain_from_food=13, + sheep_gain_from_food=5, + moore=False, ): """ Create a new Wolf-Sheep model with the given parameters. @@ -168,7 +172,14 @@ def __init__( self.random.randrange(self.height), ) energy = self.random.randrange(2 * sheep_gain_from_food) - sheep = Sheep(self.next_id(), self, moore, energy, sheep_reproduce, sheep_gain_from_food) + sheep = Sheep( + self.next_id(), + self, + moore, + energy, + sheep_reproduce, + sheep_gain_from_food, + ) self.grid.place_agent(sheep, pos) self.schedule.add(sheep) @@ -179,7 +190,9 @@ def __init__( self.random.randrange(self.height), ) energy = self.random.randrange(2 * wolf_gain_from_food) - wolf = Wolf(self.next_id(), self, moore, energy, wolf_reproduce, wolf_gain_from_food) + wolf = Wolf( + self.next_id(), self, moore, energy, wolf_reproduce, wolf_gain_from_food + ) self.grid.place_agent(wolf, pos) self.schedule.add(wolf) @@ -207,4 +220,4 @@ def step(self): start_time = time.perf_counter() for _ in range(100): model.step() - print("Time:", time.perf_counter() - start_time) \ No newline at end of file + print("Time:", time.perf_counter() - start_time)