add scalarfields on grid

This commit is contained in:
Alexander Bocken 2023-04-28 14:45:56 +02:00
parent 808dc6f78c
commit 1952198fad
Signed by: Alexander
GPG Key ID: 1D237BE83F9B05E8
4 changed files with 76 additions and 12 deletions

View File

@ -13,43 +13,57 @@ 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:
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.prev_pos : None | Coordinate = 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 sensitvity_to_concentration(self, prop : float) -> float:
# TODO
return prop
def step(self):
# Calculate where next ant location should be and store in _next_pos
# TODO
pass
def drop_chemicals(self):
# drop chemicals (depending on current state) on concentration field
# TODO
# use self.model.grid.add_to_field(key, value, pos) to not interfere with other ants
pass
def advance(self) -> None:
self.drop_chemicals()
self.pos = self._next_pos
@property
def front_neighbors(self):
if self.prev_pos is not None:
assert(self.pos is not None)
x, y = self.pos
x_prev, y_prev = self.prev_pos
dx, dy = x - x_prev, y - y_prev
front = [
front = np.array([
(x, y + dy),
(x + dx, y),
(x + dx, y + dy),
]
(x + dx, y),
])
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.

View File

@ -32,6 +32,7 @@ def main():
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)

View File

@ -11,25 +11,37 @@ License: AGPL 3 (see end of file)
import numpy as np
from mesa.model import Model
from mesa.space import Coordinate, HexGrid, Iterable
from multihex import MultiHexGrid
from multihex import MultiHexGrid, MultiHexGridScalarFields
from mesa.time import SimultaneousActivation
from mesa.datacollection import DataCollector
from agent import RandomWalkerAnt
from agent import Pheromone
class ActiveWalkerModel(Model):
# TODO: separate food and source into new agents?
# TODO: pheromone concentrations as well as agents?
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__()
fields={"A" : True, # key : also have _next prop (for no interference in step)
"B": True,
"nests": False,
"food" : False,
}
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.grid = MultiHexGridScalarFields(width=width, height=height, torus=True, fields=fields)
self._unique_id_counter = -1
self.max_steps = max_steps
self.nest_position : Coordinate = nest_position
self.num_max_agents = num_max_agents
self.decay_rates = {"A" :1,
"B": 1,
}
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)
@ -41,8 +53,18 @@ class ActiveWalkerModel(Model):
)
self.datacollector.collect(self) # keep at end of __init___
def step(self):
self.schedule.step()
self.schedule.step() # step() and advance() all agents
# apply decay rate on pheromone levels
for key in ("A", "B"):
field = self.grid.fields[key]
self.grid.fields[key] = field - self.decay_rates[key]*field
self.grid.step() # actually apply deposits on fields
self.datacollector.collect(self)
if self.schedule.steps >= self.max_steps:

View File

@ -12,6 +12,7 @@ License: AGPL 3 (see end of file)
from mesa.space import HexGrid
from mesa.agent import Agent
import numpy as np
import numpy.typing as npt
from mesa.space import Coordinate, accept_tuple_argument
import itertools
from typing import (
@ -91,6 +92,32 @@ class MultiHexGrid(HexGrid):
)
class MultiHexGridScalarFields(MultiHexGrid):
def __init__(self, fields: dict[str, bool], width : int, height : int, torus : bool, scalar_initial_value : float=0) -> None:
super().__init__(width=width, height=height, torus=torus)
self._field_props = fields
self.fields : dict[str, npt.NDArray[np.float_]] = {}
for key, is_step_field in fields.items():
self.fields[key] = np.ones((width, height)).astype(float) * scalar_initial_value
if is_step_field:
self.fields[f"_next_{key}"] = np.zeros((width, height)).astype(float)
def reset_field(self, key : str) -> None:
self.fields[key] = np.zeros((self.width, self.height))
def add_to_field(self, field_key : str, value : float, pos : Coordinate) -> None:
if self._field_props[field_key]:
self.fields[f"_next_{field_key}"][pos] += value
else:
self.fields[field_key][pos] += value
def step(self) -> None:
for key, is_step_field in self._field_props.items():
if is_step_field:
self.fields[key] += self.fields[f"_next_{key}"]
self.reset_field(f"_next_{key}")
"""
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.