add initial project files
This commit is contained in:
parent
9a11387436
commit
808dc6f78c
@ -1,2 +1,10 @@
|
||||
# ants
|
||||
A reimplementation of Schweitzer et al. 1996 as well as additional improvemnts for the Course Agent Based Modelling for Social Systems FS2023 ETH Zürich
|
||||
|
||||
|
||||
For the course [Agent Based Modelling for Social Systems FS2023](https://www.vorlesungen.ethz.ch/Vorlesungsverzeichnis/lerneinheit.view?lerneinheitId=167292&semkez=2023S&ansicht=LEHRVERANSTALTUNGEN&lang=de) we were tasked to implement a model of our own (in groups).
|
||||
For this, we decided to implement an enhanced version of [Active random walkers simulate trunk trail formation by ants (Schweitzer et al. 1996)](https://www.sciencedirect.com/science/article/pii/S030326479601670X?casa_token=fv82ToWDN3cAAAAA:wB5hHIlxnYBBvyuHb98YUFpXqWqGt50xDRnmAZ_UaMS5khR9IiH8K6m1b5gdqkAe1ACXx_lEy2U) using Python and Mesa.
|
||||
|
||||
|
||||
For now, wanted features can be found in our [shortlist](shortlist.md).
|
||||
For everything else start at [main py](main.py)
|
||||
|
59
agent.py
Normal file
59
agent.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
agent.py - Part of ants project
|
||||
|
||||
This model implements the actual agents on the grid (a.k.a. the ants)
|
||||
|
||||
License: AGPL 3 (see end of file)
|
||||
(C) Alexander Bocken, Viviane Fahrni, Grace Kragho
|
||||
"""
|
||||
import numpy as np
|
||||
from mesa.agent import Agent
|
||||
from mesa.space import Coordinate
|
||||
|
||||
|
||||
class RandomWalkerAnt(Agent):
|
||||
def __init__(self, unique_id, model, do_follow_chemical_A=True,
|
||||
energy_0=1, chemical_drop_rate_0=1, sensitvity_0=1, alpha=0.5)-> None:
|
||||
super().__init__(unique_id=unique_id, model=model)
|
||||
|
||||
self._next_pos : None | Coordinate = None
|
||||
|
||||
self.prev_pos = None
|
||||
self.do_follow_chemical_A : bool = True # False -> follow_chemical_B = True
|
||||
self.energy : float = energy_0
|
||||
self.sensitvity : float = sensitvity_0
|
||||
self.chemical_drop_rate : float = chemical_drop_rate_0 #TODO: check whether needs to be separated into A and B
|
||||
self.alpha = alpha
|
||||
|
||||
def step(self):
|
||||
pass
|
||||
|
||||
def advance(self) -> None:
|
||||
self.pos = self._next_pos
|
||||
|
||||
@property
|
||||
def front_neighbors(self):
|
||||
if self.prev_pos is not None:
|
||||
x, y = self.pos
|
||||
x_prev, y_prev = self.prev_pos
|
||||
dx, dy = x - x_prev, y - y_prev
|
||||
front = [
|
||||
(x, y + dy),
|
||||
(x + dx, y),
|
||||
(x + dx, y + dy),
|
||||
]
|
||||
return front #TODO: verify (do we need to sperate into even/odd?)
|
||||
else:
|
||||
# TODO: return all neighbors or raise Exception?
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
"""
|
49
main.py
Executable file
49
main.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/python
|
||||
"""
|
||||
main.py - Part of ants project
|
||||
execute via `python main.py` in terminal or only UNIX: `./main.py`
|
||||
|
||||
License: AGPL 3 (see end of file)
|
||||
(C) Alexander Bocken, Viviane Fahrni, Grace Kragho
|
||||
"""
|
||||
from model import ActiveWalkerModel
|
||||
from agent import RandomWalkerAnt
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mesa.space import Coordinate
|
||||
|
||||
def main():
|
||||
width = 21
|
||||
height = width
|
||||
num_initial_roamers = 5
|
||||
num_max_agents = 100
|
||||
nest_position : Coordinate = (width //2, height //2)
|
||||
max_steps = 100
|
||||
|
||||
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)
|
||||
|
||||
# just initial testing of MultiHexGrid
|
||||
for agent in model.grid.get_neighbors(pos=nest_position, include_center=True):
|
||||
if agent.unique_id == 2:
|
||||
agent.do_follow_chemical_A = False
|
||||
agent.prev_pos = (9,10)
|
||||
print(agent.front_neighbors)
|
||||
print(agent.pos, agent.unique_id, agent.do_follow_chemical_A)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
"""
|
65
model.py
Normal file
65
model.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""
|
||||
model.py - Part of ants project
|
||||
|
||||
This file implements the mesa model on which our ActiveRandomWalkerAnts
|
||||
will act
|
||||
|
||||
License: AGPL 3 (see end of file)
|
||||
(C) Alexander Bocken, Viviane Fahrni, Grace Kragho
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from mesa.model import Model
|
||||
from mesa.space import Coordinate, HexGrid, Iterable
|
||||
from multihex import MultiHexGrid
|
||||
from mesa.time import SimultaneousActivation
|
||||
from mesa.datacollection import DataCollector
|
||||
from agent import RandomWalkerAnt
|
||||
|
||||
class ActiveWalkerModel(Model):
|
||||
def __init__(self, width : int, height : int , num_max_agents : int,
|
||||
num_initial_roamers : int,
|
||||
nest_position : Coordinate,
|
||||
max_steps:int=1000) -> None:
|
||||
super().__init__()
|
||||
self.schedule = SimultaneousActivation(self)
|
||||
self.grid = MultiHexGrid(width=width, height=height, torus=True) # TODO: replace with MultiHexGrid
|
||||
self._unique_id_counter : int = -1 # only touch via get_unique_id() or get_unique_ids(num_ids)
|
||||
|
||||
self.max_steps = max_steps
|
||||
self.nest_position : Coordinate = nest_position
|
||||
self.num_max_agents = num_max_agents
|
||||
|
||||
for agent_id in self.get_unique_ids(num_initial_roamers):
|
||||
agent = RandomWalkerAnt(unique_id=agent_id, model=self, do_follow_chemical_A=True)
|
||||
self.schedule.add(agent)
|
||||
self.grid.place_agent(agent, pos=nest_position)
|
||||
|
||||
self.datacollector = DataCollector(
|
||||
model_reporters={},
|
||||
agent_reporters={}
|
||||
)
|
||||
self.datacollector.collect(self) # keep at end of __init___
|
||||
|
||||
def step(self):
|
||||
self.schedule.step()
|
||||
self.datacollector.collect(self)
|
||||
|
||||
if self.schedule.steps >= self.max_steps:
|
||||
self.running = False
|
||||
|
||||
def get_unique_id(self) -> int:
|
||||
self._unique_id_counter += 1
|
||||
return self._unique_id_counter
|
||||
|
||||
def get_unique_ids(self, num_ids : int):
|
||||
for _ in range(num_ids):
|
||||
yield self.get_unique_id()
|
||||
|
||||
"""
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
"""
|
101
multihex.py
Normal file
101
multihex.py
Normal file
@ -0,0 +1,101 @@
|
||||
"""
|
||||
multihex.py - Part of ants project
|
||||
|
||||
This file impements a Mesa HexGrid while allowing for multiple agents to be
|
||||
at the same location. The base for this code comes from the MultiGrid class
|
||||
in mesa/space.py
|
||||
|
||||
License: AGPL 3 (see end of file)
|
||||
(C) Alexander Bocken, Viviane Fahrni, Grace Kragho
|
||||
"""
|
||||
|
||||
from mesa.space import HexGrid
|
||||
from mesa.agent import Agent
|
||||
import numpy as np
|
||||
from mesa.space import Coordinate, accept_tuple_argument
|
||||
import itertools
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Sequence,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
)
|
||||
|
||||
MultiGridContent = list[Agent]
|
||||
|
||||
class MultiHexGrid(HexGrid):
|
||||
"""Hexagonal grid where each cell can contain more than one agent.
|
||||
Mostly based of mesa's HexGrid
|
||||
Functions according to odd-q rules.
|
||||
See http://www.redblobgames.com/grids/hexagons/#coordinates for more.
|
||||
|
||||
Properties:
|
||||
width, height: The grid's width and height.
|
||||
torus: Boolean which determines whether to treat the grid as a torus.
|
||||
|
||||
Methods:
|
||||
get_neighbors: Returns the objects surrounding a given cell.
|
||||
get_neighborhood: Returns the cells surrounding a given cell.
|
||||
iter_neighbors: Iterates over position neighbors.
|
||||
iter_neighborhood: Returns an iterator over cell coordinates that are
|
||||
in the neighborhood of a certain point.
|
||||
"""
|
||||
grid: list[list[MultiGridContent]]
|
||||
|
||||
@staticmethod
|
||||
def default_val() -> MultiGridContent:
|
||||
"""Default value for new cell elements."""
|
||||
return []
|
||||
|
||||
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
|
||||
"""Place the agent at the specified location, and set its pos variable."""
|
||||
x, y = pos
|
||||
if agent.pos is None or agent not in self._grid[x][y]:
|
||||
self._grid[x][y].append(agent)
|
||||
agent.pos = pos
|
||||
if self._empties_built:
|
||||
self._empties.discard(pos)
|
||||
|
||||
def remove_agent(self, agent: Agent) -> None:
|
||||
"""Remove the agent from the given location and set its pos attribute to None."""
|
||||
pos = agent.pos
|
||||
x, y = pos
|
||||
self._grid[x][y].remove(agent)
|
||||
if self._empties_built and self.is_cell_empty(pos):
|
||||
self._empties.add(pos)
|
||||
agent.pos = None
|
||||
|
||||
@accept_tuple_argument
|
||||
def iter_cell_list_contents(
|
||||
self, cell_list: Iterable[Coordinate]
|
||||
) -> Iterator[Agent]:
|
||||
"""Returns an iterator of the agents contained in the cells identified
|
||||
in `cell_list`; cells with empty content are excluded.
|
||||
|
||||
Args:
|
||||
cell_list: Array-like of (x, y) tuples, or single tuple.
|
||||
|
||||
Returns:
|
||||
An iterator of the agents contained in the cells identified in `cell_list`.
|
||||
"""
|
||||
return itertools.chain.from_iterable(
|
||||
self._grid[x][y]
|
||||
for x, y in itertools.filterfalse(self.is_cell_empty, cell_list)
|
||||
)
|
||||
|
||||
|
||||
|
||||
"""
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
|
||||
"""
|
45
shortlist.md
Normal file
45
shortlist.md
Normal file
@ -0,0 +1,45 @@
|
||||
# SHORTLIST
|
||||
|
||||
- nonlinear response to concentration of pheromones (with upper and lower threshold)
|
||||
-> needs a separation of sensitivity and internal energy
|
||||
|
||||
## More chaos
|
||||
- what happens if you disrupt the trail with an obstacle?
|
||||
- limited node capacity
|
||||
|
||||
|
||||
# Model setup
|
||||
|
||||
## Agents
|
||||
- previous_position
|
||||
- position
|
||||
- sensitivity
|
||||
- internal energy
|
||||
- pheromone drop rate (A/B)
|
||||
- what chemical we´re looking for
|
||||
- optional: how much food we have with us (and decrement to prevent dying if energy is low)
|
||||
- alpha
|
||||
|
||||
### step function
|
||||
- probablistic forward step based on concentrations and sensitvity
|
||||
- follow highest concentration probabilistically and be random otherwise
|
||||
- drop pheromones
|
||||
- have we found food -> change behaviour and decrease food amount + reset stuff
|
||||
- are we at the nest (having found food)? -> recruit new ants + reset stuff
|
||||
- decrement sensitivity
|
||||
- decrement energy (optional: only without food)
|
||||
- do i need to die
|
||||
|
||||
## Model
|
||||
|
||||
### hexagonal grid
|
||||
- pheromone a/b concentration
|
||||
- nest location
|
||||
- food location/concentration
|
||||
- for later: node capacity
|
||||
- N total ants
|
||||
- a few other constants which need to be set
|
||||
|
||||
### step
|
||||
advance ants (call ants step function)
|
||||
decrease pheromone concentration on grid
|
Loading…
Reference in New Issue
Block a user