From a8cb347e4ad2d19f8181bf702e960f401708cd0b Mon Sep 17 00:00:00 2001 From: AlexBocken Date: Wed, 17 May 2023 19:32:08 +0200 Subject: [PATCH] add test ant pheromone decay, rename chemicals to pheromone --- agent.py | 48 ++++++++++++++++++++++++------------------------ main.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ model.py | 2 +- 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/agent.py b/agent.py index 4a04919..784c891 100644 --- a/agent.py +++ b/agent.py @@ -12,11 +12,11 @@ from mesa.agent import Agent from mesa.space import Coordinate class RandomWalkerAnt(Agent): - def __init__(self, unique_id, model, look_for_chemical=None, + def __init__(self, unique_id, model, look_for_pheromone=None, energy_0=1, - chemical_drop_rate_0 : dict[str, float]={"A": 80, "B": 80}, + pheromone_drop_rate_0 : dict[str, float]={"A": 80, "B": 80}, sensitivity_0=0.99, - alpha=0.6, drop_chemical=None, + alpha=0.6, drop_pheromone=None, betas : dict[str, float]={"A": 0.0512, "B": 0.0512}, sensitivity_decay_rate=0.01, sensitivity_max = 1 @@ -27,12 +27,12 @@ class RandomWalkerAnt(Agent): self._next_pos : None | Coordinate = None self.prev_pos : None | Coordinate = None - self.look_for_chemical = look_for_chemical - self.drop_chemical = drop_chemical + self.look_for_pheromone = look_for_pheromone + self.drop_pheromone = drop_pheromone self.energy = energy_0 #TODO: use self.sensitivity_0 = sensitivity_0 self.sensitivity = self.sensitivity_0 - self.chemical_drop_rate = chemical_drop_rate_0 + self.pheromone_drop_rate = pheromone_drop_rate_0 self.alpha = alpha self.sensitivity_max = sensitivity_max self.sensitivity_decay_rate = sensitivity_decay_rate @@ -87,7 +87,7 @@ class RandomWalkerAnt(Agent): if self.searching_food: for neighbor in self.front_neighbors: if self.model.grid.is_food(neighbor): - self.drop_chemical = "B" + self.drop_pheromone = "B" self.sensitivity = self.sensitivity_0 self.prev_pos = neighbor @@ -96,8 +96,8 @@ class RandomWalkerAnt(Agent): elif self.searching_nest: for neighbor in self.front_neighbors: if self.model.grid.is_nest(neighbor): - self.look_for_chemical = "A" # Is this a correct interpretation? - self.drop_chemical = "A" + self.look_for_pheromone = "A" # Is this a correct interpretation? + self.drop_pheromone = "A" self.sensitivity = self.sensitivity_0 #TODO: Do we flip the ant here or reset prev pos? @@ -107,16 +107,16 @@ class RandomWalkerAnt(Agent): # recruit new ants for agent_id in self.model.get_unique_ids(self.model.num_new_recruits): - agent = RandomWalkerAnt(unique_id=agent_id, model=self.model, look_for_chemical="B", drop_chemical="A") + agent = RandomWalkerAnt(unique_id=agent_id, model=self.model, look_for_pheromone="B", drop_pheromone="A") agent._next_pos = self.pos self.model.schedule.add(agent) self.model.grid.place_agent(agent, pos=neighbor) # follow positive gradient - if self.look_for_chemical is not None: - front_concentration = [self.model.grid.fields[self.look_for_chemical][cell] for cell in self.front_neighbors ] - front_concentration = self.sens_adj(front_concentration, self.look_for_chemical) - current_pos_concentration = self.sens_adj(self.model.grid.fields[self.look_for_chemical][self.pos], self.look_for_chemical) + if self.look_for_pheromone is not None: + front_concentration = [self.model.grid.fields[self.look_for_pheromone][cell] for cell in self.front_neighbors ] + front_concentration = self.sens_adj(front_concentration, self.look_for_pheromone) + current_pos_concentration = self.sens_adj(self.model.grid.fields[self.look_for_pheromone][self.pos], self.look_for_pheromone) gradient = front_concentration - np.repeat(current_pos_concentration, 3) index = np.argmax(gradient) if gradient[index] > 0: @@ -138,19 +138,19 @@ class RandomWalkerAnt(Agent): def step(self): self.sensitivity -= self.sensitivity_decay_rate self._choose_next_pos() - self._adjust_chemical_drop_rate() + self._adjust_pheromone_drop_rate() - def _adjust_chemical_drop_rate(self): - if(self.drop_chemical is not None): - self.chemical_drop_rate[self.drop_chemical] -= self.chemical_drop_rate[self.drop_chemical] * self.betas[self.drop_chemical] + def _adjust_pheromone_drop_rate(self): + if(self.drop_pheromone is not None): + self.pheromone_drop_rate[self.drop_pheromone] -= self.pheromone_drop_rate[self.drop_pheromone] * self.betas[self.drop_pheromone] - def drop_chemicals(self) -> None: + def drop_pheromones(self) -> None: # should only be called in advance() as we do not use hidden fields - if self.drop_chemical is not None: - self.model.grid.fields[self.drop_chemical][self.pos] += self.chemical_drop_rate[self.drop_chemical] + if self.drop_pheromone is not None: + self.model.grid.fields[self.drop_pheromone][self.pos] += self.pheromone_drop_rate[self.drop_pheromone] def advance(self) -> None: - self.drop_chemicals() + self.drop_pheromones() self.prev_pos = self.pos self.model.grid.move_agent(self, self._next_pos) @@ -162,11 +162,11 @@ class RandomWalkerAnt(Agent): @property def searching_nest(self) -> bool: - return self.drop_chemical == "B" + return self.drop_pheromone == "B" @property def searching_food(self) -> bool: - return self.drop_chemical == "A" + return self.drop_pheromone == "A" @property def front_neighbors(self): diff --git a/main.py b/main.py index 4e30f8e..a4d19ab 100755 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ from mesa.space import Coordinate def main(): check_pheromone_exponential_decay() check_ant_sensitivity_linear_decay() + check_ant_pheromone_exponential_decay() def check_pheromone_exponential_decay(): """ @@ -103,6 +104,51 @@ def check_ant_sensitivity_linear_decay(): plt.show() +def check_ant_pheromone_exponential_decay(): + """ + Check whether wanted exponential decay of pheromone drop rate for ants is correctly modeled + shows plot of pheromone placed on grid vs. equivalent exponential decay function + """ + + from mesa.datacollection import DataCollector + + width = 50 + height = width + num_initial_roamers = 1 + num_max_agents = 100 + nest_position : Coordinate = (width //2, height //2) + max_steps = 1000 + + model = ActiveWalkerModel(width=width, height=height, + num_initial_roamers=num_initial_roamers, + nest_position=nest_position, + num_max_agents=num_max_agents, + max_steps=max_steps) + + + model.datacollector = DataCollector( + model_reporters={}, + agent_reporters={"pheromone_drop_rate": lambda a: a.pheromone_drop_rate["A"]} + ) + start = model.schedule.agents[0].pheromone_drop_rate["A"] + model.run_model() + a_test = model.datacollector.get_agent_vars_dataframe().reset_index()["pheromone_drop_rate"] + + import matplotlib.pyplot as plt + import numpy as np + + + plt.figure() + xx = np.linspace(0,1000, 10000) + yy = a_test[0]*np.exp(-model.schedule.agents[0].betas["A"]*xx) + plt.plot(xx, yy, label="correct exponential function") + plt.scatter(range(len(a_test)), a_test, label="modeled decay", marker='o') + plt.title("Exponential pheromone drop rate decay test") + plt.legend(loc='best') + + plt.show() + + if __name__ == "__main__": main() diff --git a/model.py b/model.py index a79c2a8..5145bfd 100644 --- a/model.py +++ b/model.py @@ -39,7 +39,7 @@ class ActiveWalkerModel(Model): } for agent_id in self.get_unique_ids(num_initial_roamers): - agent = RandomWalkerAnt(unique_id=agent_id, model=self, look_for_chemical="A", drop_chemical="A") + agent = RandomWalkerAnt(unique_id=agent_id, model=self, look_for_pheromone="A", drop_pheromone="A") self.schedule.add(agent) self.grid.place_agent(agent, pos=nest_position)