Proposal: Flexible move_agent method that allows multiple movement strategies
EwoutH opened this issue · 5 comments
The Mesa Space module allows a few specific agent movements and interactions within a grid environment. However, the current methods lack a consistent way to apply different movement strategies. This proposal suggests integrating a single, versatile move_agent
method, accepting a parameter that can either be a tuple for specific coordinates, a string indicating a movement strategy (like "random" or "empty"), or an object defining a neighborhood.
In the future, it can be extended with movement strategies based on properties (see #1898).
Motivation
I was building a toy model, and
x = self.random.randrange(self.grid.width)
y = self.random.randrange(self.grid.height)
self.grid.place_agent(a, (x, y))
just looked weird and limited.
Proposed changes
-
Unified move method:
move_agent(agent: Agent, destination) → None
- The
destination
parameter is versatile:- It can be a tuple
(x, y)
for moving the agent to specific coordinates. - It can be a string, such as
"random"
for a random cell,"empty"
for a random empty cell. - It can be an object or a dictionary defining a neighborhood, allowing custom definitions of neighboring cells. For this we need a formal neighborhood definition, see #1900.
- It can be a tuple
-
Retiring redundant methods:
- Methods like
move_to_empty
andplace_agent
would be redundant and can be removed, as their functionalities are integrated into the newmove_agent
.
- Methods like
-
Enhanced out-of-bounds and validity checking:
- Maintain
out_of_bounds
method, but possibly enhance it to include checks for cell occupancy, ensuring valid movement destinations.
- Maintain
-
Agent removal and position swapping:
remove_agent
andswap_pos
methods remain useful and unchanged.
Example Implementations
- Moving to a Specific Cell:
space.move_agent(agent, (x, y))
- Moving to a Random Cell:
space.move_agent(agent, "random")
- Moving to a Random Empty Cell:
space.move_agent(agent, "empty")
- Moving to a Custom-Defined Neighborhood:
(see #1900)neighborhood_def = {"type": "Moore", "radius": 2} space.move_agent(agent, neighborhood_def)
Conclusion
This proposal aims to simplify and unify the movement methods in the Mesa Space module. By consolidating various movement strategies into a single method, we enhance the API's usability and flexibility, allowing users to execute complex movements with minimal and more intuitive code.
Notes
Structural Pattern Matching in Python 3.10 might help a lot with the implementation.
I was thinking about how this would integrate with the _PropertyGrid introduced in #1898. That currently offers separate functions for the selection of target cells, and then moving to the target cells.
def select_cells_by_properties():
def move_agent_to_cell_by_properties():
def select_extreme_value_cells():
def move_agent_to_extreme_value_cell():
If move_agent
would also take a list or mask of target cells, that could allow removing the two movement methods from #1898, and make the API more consistent. All movement goes through move_agents
, and any amount of custom methods can be written to select some target cells.
Currently, #1898 also include two mask functions which limit the number of candidate cells to cells empty or in a neighborhood.
def get_empty_mask():
def get_neighborhood_mask():
We might also want to integrate that into the move_agent
method. It could take a mask or a list of masks.
A utility method to combine a list of masks into a single mask could also be provided.
Maybe we can combine things:
def move_agent(agent, pos=None, empty=False, neighborhood=None, mask=None, selection="random"):
- pos: A single position or list of possible positions
- empty: False allows all cells, True only empty ones
- neighborhood: Optional neighborhood (#1900)
- mask: a mask or list of masks of allowed input cells
- selection: Can be "random" or "closest". Maybe something else in the future (in the _PropertyGrid it could be highest or lowest maybe).
@jackiekazil @tpike3 Same story as with #1905, I would like to discuss and potentially implement this before moving on the PropertyLayer (#1898), since this is a more generalized solution for a problem that I specifically solve in that PR. So if we can implement this, it would make especially the _PropertyGrid in #1898 a lot simpler.
So my specific questions are:
- Do you agree we should expand the built-in
move_agent()
movement method to allow moving to an empty, random or neighbouring cell? - What do you think of the current proposed API? Do you like the initial one better or the last one?
There is still possibility for improvement here. The current most elegant way to place an agent on an empty cell I discovered is:
self.grid.place_agent(agent, pos=mesa.model.random.choice(list(self.grid.empties)))
This can't be the best way. The current problems are:
place_agent
doesn't directly allow selecting an empty cell, or a random cell for that matter. It needs a single position, and doesn't allow a list of positions likemove_agent
- All move functions (
move_to_empty
,move_agent_to_one_of
andmove_agent
) require a current position to be had, since they callremove_agent
internally.
I would like to discuss how we can make placing an agent on a random empty cell more elegant.