-
Notifications
You must be signed in to change notification settings - Fork 180
Add new termite example #251
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
3b6bcbb
f7dfc41
2e28660
89b93ba
df85824
35c6068
996e495
de11caa
048b26a
612787e
a1be110
0706d6b
b1c8d30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Termite WoodChip Behaviour | ||
|
||
This model simulates termites interacting with wood chips, inspired by the [NetLogo Termites model](https://ccl.northwestern.edu/netlogo/models/Termites). It explores emergent behavior in decentralized systems, demonstrating how simple agents (termites) collectively organize wood chips into piles without centralized coordination. | ||
|
||
## Summary | ||
|
||
In this simulation, multiple termite agents move randomly on a grid containing scattered wood chips. Each termite follows simple rules: | ||
|
||
1. Search for a wood chip. If found, pick it up and move away. | ||
2. When carrying a wood chip, search for a pile (another wood chip). | ||
3. When a pile is found, find a nearby empty space to place the carried chip. | ||
4. After dropping a chip, move away from the pile. | ||
|
||
Over time, these simple interactions lead to the formation of wood chip piles, illustrating decentralized organization without a central coordinator. | ||
|
||
## Installation | ||
|
||
Make sure that you have installed the `latest` version of mesa i.e `3.2` onwards. | ||
|
||
## Usage | ||
|
||
To run the simulation: | ||
```bash | ||
solara run app.py | ||
``` | ||
|
||
## Model Details | ||
|
||
### Agents | ||
|
||
- **Termite:** An agent that moves within the grid environment, capable of carrying a single wood chip at a time. The termite follows the precise logic of the original NetLogo model, with each behavior (searching, finding piles, dropping chips) continuing until successful. | ||
|
||
### Environment | ||
|
||
- **Grid:** A two-dimensional toroidal grid where termites interact with the wood chips. The toroidal nature means agents exiting one edge re-enter from the opposite edge. | ||
- **PropertyLayer:** A data structure overlaying the grid, storing the presence of wood chips at each cell. | ||
|
||
### Agent Behaviors | ||
|
||
- **wiggle():** Simulates random movement by selecting a random neighboring cell. | ||
- **search_for_chip():** Looks for a wood chip. If found, picks it up and moves forward significantly. | ||
- **find_new_pile():** When carrying a chip, searches for a cell that already has a wood chip. | ||
- **put_down_chip():** Attempts to place the carried wood chip in an empty cell near a pile. | ||
- **get_away():** After dropping a chip, moves away from the pile to prevent clustering. | ||
|
||
## References | ||
|
||
- Wilensky, U. (1997). NetLogo Termites model. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. Available at: [NetLogo Termites Model](https://ccl.northwestern.edu/netlogo/models/Termites) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from mesa.visualization import SolaraViz | ||
from mesa.visualization.components.matplotlib_components import make_mpl_space_component | ||
from termites.model import TermiteModel | ||
|
||
wood_chip_portrayal = { | ||
"woodcell": { | ||
"color": "blue", | ||
"alpha": 0.6, | ||
"colorbar": False, | ||
"vmin": 0, | ||
"vmax": 2, | ||
}, | ||
} | ||
|
||
|
||
def agent_portrayal(agent): | ||
return {"marker": ">", "color": "red" if agent.hasWoodChip else "black", "size": 10} | ||
|
||
|
||
model_params = { | ||
"seed": { | ||
"type": "InputText", | ||
"value": 42, | ||
"label": "Seed", | ||
}, | ||
"num_termites": { | ||
"type": "SliderInt", | ||
"value": 100, | ||
"label": "No. of Termites", | ||
"min": 10, | ||
"max": 1000, | ||
"step": 1, | ||
}, | ||
"wood_chip_density": { | ||
"type": "SliderFloat", | ||
"value": 0.1, | ||
"label": "Wood Chip Density", | ||
"min": 0.01, | ||
"max": 1, | ||
"step": 0.1, | ||
}, | ||
"width": { | ||
"type": "SliderInt", | ||
"value": 100, | ||
"label": "Width", | ||
"min": 10, | ||
"max": 500, | ||
"step": 1, | ||
}, | ||
"height": { | ||
"type": "SliderInt", | ||
"value": 100, | ||
"label": "Height", | ||
"min": 10, | ||
"max": 500, | ||
"step": 1, | ||
}, | ||
} | ||
|
||
model = TermiteModel() | ||
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def post_process(ax): | ||
ax.set_aspect("equal") | ||
ax.set_xticks([]) | ||
ax.set_yticks([]) | ||
|
||
|
||
woodchips_space = make_mpl_space_component( | ||
agent_portrayal=agent_portrayal, | ||
propertylayer_portrayal=wood_chip_portrayal, | ||
post_process=post_process, | ||
draw_grid=False, | ||
) | ||
|
||
page = SolaraViz( | ||
model, | ||
components=[woodchips_space], | ||
model_params=model_params, | ||
name="Termites Model", | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
from mesa.discrete_space import CellAgent | ||
|
||
|
||
class Termite(CellAgent): | ||
""" | ||
A Termite agent that has ability to carry woodchip. | ||
|
||
Attributes: | ||
hasWoodChip(bool): True if the agent is carrying a wood chip. | ||
""" | ||
|
||
def __init__(self, model, cell): | ||
""" | ||
Args: | ||
model: The model instance. | ||
cell: The starting cell (position) of the agent. | ||
""" | ||
super().__init__(model) | ||
self.cell = cell | ||
self.hasWoodChip = False | ||
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def wiggle(self): | ||
self.cell = self.model.random.choice(self.model.grid.all_cells.cells) | ||
|
||
def search_for_chip(self): | ||
if self.cell.woodcell: | ||
self.cell.woodcell = False | ||
self.hasWoodChip = True | ||
|
||
for _ in range(10): | ||
new_cell = self.cell.neighborhood.select_random_cell() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
if new_cell.is_empty: | ||
self.cell = new_cell | ||
break | ||
return True | ||
else: | ||
# No chip found, wiggle and return False to continue searching | ||
self.wiggle() | ||
return False | ||
|
||
def find_new_pile(self): | ||
# Continue wiggling until finding a cell with a wood chip. | ||
if not self.cell.woodcell: | ||
self.wiggle() | ||
return False | ||
return True | ||
|
||
def put_down_chip(self): | ||
if not self.hasWoodChip: | ||
return True | ||
|
||
if not self.cell.woodcell: | ||
self.cell.woodcell = True | ||
self.hasWoodChip = False | ||
|
||
self.get_away() | ||
return True | ||
else: | ||
empty_neighbors = [c for c in self.cell.neighborhood if c.is_empty] | ||
if empty_neighbors: | ||
self.cell = self.model.random.choice(empty_neighbors) | ||
return False | ||
|
||
def get_away(self): | ||
for _ in range(10): | ||
new_cell = self.cell.neighborhood.select_random_cell() | ||
if new_cell.is_empty: | ||
self.cell = new_cell | ||
if self.cell.woodcell: | ||
return self.get_away() | ||
break | ||
|
||
def step(self): | ||
""" | ||
Protocol which termite agent follows: | ||
1. Search for a wood chip if not carrying one. | ||
2. Find a new pile (a cell with a wood chip) if carrying a chip. | ||
3. Put down the chip if a suitable location is found. | ||
""" | ||
if not self.hasWoodChip: | ||
while not self.search_for_chip(): | ||
pass | ||
|
||
while not self.find_new_pile(): | ||
pass | ||
|
||
while not self.put_down_chip(): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import numpy as np | ||
from mesa import Model | ||
from mesa.discrete_space import OrthogonalMooreGrid, PropertyLayer | ||
|
||
from .agents import Termite | ||
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
class TermiteModel(Model): | ||
""" | ||
A simulation that depicts behavior of termite agents gathering wood chips into piles. | ||
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
|
||
def __init__( | ||
self, num_termites=100, width=100, height=100, wood_chip_density=0.1, seed=42 | ||
): | ||
"""Initialize the model. | ||
|
||
Args: | ||
num_termites: Number of Termite Agents, | ||
width: Grid width. | ||
height: Grid heights. | ||
wood_chip_density: Density of wood chips in the grid. | ||
seed : Random seed for reproducibility. | ||
""" | ||
super().__init__(seed=seed) | ||
self.num_termites = num_termites | ||
self.wood_chip_density = wood_chip_density | ||
|
||
self.grid = OrthogonalMooreGrid((width, height), torus=True) | ||
|
||
self.wood_chips_layer = PropertyLayer( | ||
"woodcell", (width, height), default_value=False, dtype=bool | ||
) | ||
self.wood_chips_layer.data = np.random.choice( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a smart way to initialize the PropertyLayer! That’s one of the advantages of using a standard data structure underneath, it can directly be accessed and modified. @Spartan-71, was this intuitive, or should we document this possibility better? CC @quaquel basically a two-liner to initialize the PorpertyLayers, really cool. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should document this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any suggestion where?
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[True, False], | ||
size=(width, height), | ||
p=[self.wood_chip_density, 1 - self.wood_chip_density], | ||
) | ||
|
||
self.grid.add_property_layer(self.wood_chips_layer) | ||
|
||
Termite.create_agents( | ||
Spartan-71 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self, | ||
self.num_termites, | ||
self.random.sample(self.grid.all_cells.cells, k=self.num_termites), | ||
) | ||
|
||
def step(self): | ||
self.agents.shuffle_do("step") |
Uh oh!
There was an error while loading. Please reload this page.