From 10279930e1b47dbb5a3870388d594afcac7fcdbf Mon Sep 17 00:00:00 2001 From: AlexBocken Date: Thu, 18 May 2023 16:00:43 +0200 Subject: [PATCH] implemented test ant_follow_gradient, fixed step() function --- agent.py | 49 ++++++++++++++++++++++++++++++++++--------------- main.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ model.py | 4 ++-- server.py | 6 +++--- 4 files changed, 87 insertions(+), 20 deletions(-) diff --git a/agent.py b/agent.py index 95c94cb..c0e7b1d 100644 --- a/agent.py +++ b/agent.py @@ -19,13 +19,13 @@ class RandomWalkerAnt(Agent): alpha=0.6, drop_pheromone=None, betas : dict[str, float]={"A": 0.0512, "B": 0.0512}, sensitivity_decay_rate=0.01, - sensitivity_max = 1 + sensitivity_max = 300 ) -> None: super().__init__(unique_id=unique_id, model=model) self._next_pos : None | Coordinate = None - self.prev_pos : None | Coordinate = None + self._prev_pos : None | Coordinate = None self.look_for_pheromone = look_for_pheromone self.drop_pheromone = drop_pheromone @@ -37,7 +37,7 @@ class RandomWalkerAnt(Agent): self.sensitivity_max = sensitivity_max self.sensitivity_decay_rate = sensitivity_decay_rate self.betas = betas - self.threshold : dict[str, float] = {"A": 1, "B": 1} + self.threshold : dict[str, float] = {"A": 0, "B": 0} def sens_adj(self, props, key) -> npt.NDArray[np.float_] | float: @@ -79,9 +79,11 @@ class RandomWalkerAnt(Agent): return np.array(arr) def _choose_next_pos(self): - if self.prev_pos is None: + if self._prev_pos is None: i = np.random.choice(range(6)) + assert(self.pos is not self.neighbors()[i]) self._next_pos = self.neighbors()[i] + self._prev_pos = self.pos return if self.searching_food: @@ -91,7 +93,7 @@ class RandomWalkerAnt(Agent): self.look_for_pheromone = "A" self.sensitivity = self.sensitivity_0 - self.prev_pos = neighbor + self._prev_pos = neighbor self._next_pos = self.pos elif self.searching_nest: @@ -101,7 +103,7 @@ class RandomWalkerAnt(Agent): self.drop_pheromone = "A" self.sensitivity = self.sensitivity_0 - self.prev_pos = neighbor + self._prev_pos = neighbor self._next_pos = self.pos # recruit new ants @@ -117,22 +119,26 @@ class RandomWalkerAnt(Agent): 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) + gradient = front_concentration - np.repeat(current_pos_concentration, 3).astype(np.float_) + # TODO: if two or more neighbors have same concentration randomize? Should be unlikely with floats though index = np.argmax(gradient) if gradient[index] > 0: self._next_pos = self.front_neighbors[index] + self._prev_pos = self.pos return # do biased random walk p = np.random.uniform() if p < self.alpha: self._next_pos = self.front_neighbor + self._prev_pos = self.pos else: # need copy() as we would otherwise remove the tuple from all possible lists (aka python "magic") other_neighbors = self.neighbors().copy() other_neighbors.remove(self.front_neighbor) random_index = np.random.choice(range(len(other_neighbors))) self._next_pos = other_neighbors[random_index] + self._prev_pos = self.pos def step(self): @@ -151,8 +157,8 @@ class RandomWalkerAnt(Agent): def advance(self) -> None: self.drop_pheromones() - self.prev_pos = self.pos self.model.grid.move_agent(self, self._next_pos) + self._next_pos = None # so that we rather crash than use wrong data # TODO: find out how to decorate with property properly def neighbors(self, pos=None, include_center=False): @@ -173,12 +179,25 @@ class RandomWalkerAnt(Agent): """ returns all three neighbors which the ant can see """ - assert(self.prev_pos is not None) all_neighbors = self.neighbors() - neighbors_at_the_back = self.neighbors(pos=self.prev_pos, include_center=True) + neighbors_at_the_back = self.neighbors(pos=self._prev_pos, include_center=True) front_neighbors = list(filter(lambda i: i not in neighbors_at_the_back, all_neighbors)) - assert(len(front_neighbors) == 3) # not sure whether always the case, used for debugging - return front_neighbors + + ########## DEBUG + try: + assert(self._prev_pos is not None) + assert(self._prev_pos is not self.pos) + assert(self._prev_pos in all_neighbors) + assert(len(front_neighbors) == 3) + except AssertionError: + print(f"{self._prev_pos=}") + print(f"{self.pos=}") + print(f"{all_neighbors=}") + print(f"{neighbors_at_the_back=}") + print(f"{front_neighbors=}") + raise AssertionError + else: + return front_neighbors @property def front_neighbor(self): @@ -186,11 +205,11 @@ class RandomWalkerAnt(Agent): returns neighbor of current pos which is towards the front of the ant """ - neighbors_prev_pos = self.neighbors(self.prev_pos) + neighbors__prev_pos = self.neighbors(self._prev_pos) for candidate in self.front_neighbors: - # neighbor in front direction only shares current pos as neighborhood with prev_pos + # neighbor in front direction only shares current pos as neighborhood with _prev_pos candidate_neighbors = self.model.grid.get_neighborhood(candidate) - overlap = [x for x in candidate_neighbors if x in neighbors_prev_pos] + overlap = [x for x in candidate_neighbors if x in neighbors__prev_pos] if len(overlap) == 1: return candidate diff --git a/main.py b/main.py index 0e0c596..c9c3c15 100755 --- a/main.py +++ b/main.py @@ -13,10 +13,13 @@ import matplotlib.pyplot as plt from mesa.space import Coordinate from mesa.datacollection import DataCollector +from multihex import MultiHexGrid + def main(): check_pheromone_exponential_decay() check_ant_sensitivity_linear_decay() check_ant_pheromone_exponential_decay() + check_ants_follow_gradient() def check_pheromone_exponential_decay(): """ @@ -131,6 +134,51 @@ def check_ant_pheromone_exponential_decay(): plt.show() +def check_ants_follow_gradient(): + """ + Create a path of neighbours with a static gradient. + Observe whether ant correctly follows gradient once found. via matrix printouts + 8 = ant + anything else: pheromone A density. + The ant does not drop any new pheromones for this test + """ + width, height = 20,20 + params = { + "width": width, "height": height, + "num_max_agents": 1, + "num_food_sources": 0, + "nest_position": (10,10), + "num_initial_roamers": 1, + } + model = ActiveWalkerModel(**params) + def place_line(grid : MultiHexGrid, start_pos=None): + strength = 5 + if start_pos is None: + start_pos = (9,9) + next_pos = start_pos + for _ in range(width): + grid.fields["A"][next_pos] = strength + strength += 0.01 + next_pos = grid.get_neighborhood(next_pos)[0] + + place_line(model.grid) + + ant = model.schedule._agents[0] + ant.looking_for_pheromone = "A" + ant.drop_pheromone = None + ant.threshold["A"] = 0 + ant.sensitivity_max = 100 + #model.grid.fields["A"] = np.diag(np.ones(width)) + model.decay_rates["A"] = 0 + + while model.schedule.steps < 100: + display_field = np.copy(model.grid.fields["A"]) + display_field[ant.pos] = 8 + print(display_field) + print(20*"#") + model.step() + + if __name__ == "__main__": main() diff --git a/model.py b/model.py index 8634e61..10cee4a 100644 --- a/model.py +++ b/model.py @@ -22,7 +22,8 @@ class ActiveWalkerModel(Model): nest_position : Coordinate, num_food_sources=5, food_size=10, - max_steps:int=1000) -> None: + max_steps:int=1000, + ) -> None: super().__init__() fields=["A", "B", "nests", "food"] self.schedule = SimultaneousActivation(self) @@ -68,7 +69,6 @@ class ActiveWalkerModel(Model): for key in ("A", "B"): field = self.grid.fields[key] self.grid.fields[key] = field - self.decay_rates[key]*field - # TODO: plot to check whether exponential self.datacollector.collect(self) diff --git a/server.py b/server.py index 4d14e63..2a2a159 100755 --- a/server.py +++ b/server.py @@ -23,9 +23,9 @@ def setup(params=None): if params is None: params = { "width": 50, "height": 50, - "num_max_agents" : 1000, + "num_max_agents" : 100, "nest_position" : (25,25), - "num_initial_roamers" : 20, + "num_initial_roamers" : 5, } @@ -127,7 +127,7 @@ def setup(params=None): [lambda m: "

Ant density

Nest: Red, Food: Green
", grid_ants, lambda m: f"
Normalization Value: {norm_ants(m)}
", - lambda m: "

Pheromone Density

Pheromone A: Cyan, Pheromone B: Pink
", + lambda m: "

Pheromone Density

Pheromone A: Cyan, Pheromone B: Magenta
", grid_pheromones, lambda m: f"
Normalization Value: {norm_pheromones(m)}
" ],