Conceptual model of Space #2585
Replies: 7 comments 24 replies
-
|
Thanks for this. It's really helpful. My 2 cents:
|
Beta Was this translation helpful? Give feedback.
-
For me, this is a no-go. The entire point of a space is that it defines what is close and far and thus specifies neighborhood relationships. This is why I agree with you that, conceptually, an agent can be part of multiple spaces. The issue thus revolves around having agent classes that allow for multiple "locations", which is trivial to implement already on a case-by-case basis, but very hard to implement generically. That is, in my view, nothing needs to change in MESA itself because it is already possible but requires some coding by the user. On the property side of things, I am not convinced we need to change anything either. Properties are currently tied to discrete spaces only and follow the coordinate system of that space. Why would that need to change even if an agent can be part of multiple spaces? Properties are understood as being properties of that space so why decouple them. |
Beta Was this translation helpful? Give feedback.
-
|
I've been in discussion with some people (@SongshGeo @AdamZh0u @LunnyRia @peter-kinger and a few others) who are interested in working on projectmesa/mesa-geo#201, and we came up with a proposed design for mesa-geo v1.0 (see figure below). Not sure whether it relates to this discussion, but the new design is very similar to the experimental cell space. Linking to Mesa spaces, we can think of the VectorLayer as the ContinuousSpace, and RasterLayer as the GridSpace. This perhaps resembles the idea of having multiple spaces (i.e., layers in mesa-geo) discussed above. We may even go beyond spaces and have a ModelCollection that allows stepping several models inside a metamodel, in a hierarchical manner. I don't know whether this is what @tpike3 wants for a multi-level mesa. Nonetheless it is beyond the scope of our current discussions and thus not included in the figure. |
Beta Was this translation helpful? Give feedback.
-
|
I want to challenge one of our core assumptions: Do agents actually need a single unified position? The "one true position" idea sounds elegant, but it breaks down when we consider different space types:
These aren't interchangeable or meaningfully convertible. A social network node isn't a coordinate you can map to (x, y). An agent's position in an "economic space" based on wealth similarity has no physical location. Two Distinct ConceptsI think we're conflating:
Architectural Inconsistency in Current MesaLooking at our current implementations, we already have two different architectures: Discrete Spaces (cell_space):
Continuous Spaces (experimental):
This inconsistency suggests we need to think about architectural unification. Options for Unifying SpacesOption A: Space Owns EverythingMake discrete spaces work like continuous spaces - space owns all agent-location mappings: # All spaces own the agent→location relationship
class DiscreteSpace:
def get_cell(self, agent) -> Cell:
return self._agent_to_cell[agent]
def move_agent(self, agent, new_cell):
# Space manages the relationship
...
class ContinuousSpace:
def get_position(self, agent) -> np.ndarray:
return self._agent_positions[self._agent_to_index[agent]]
# Agents query spaces
neighbors = space.get_neighbors(agent, radius=5)Pros: Consistent architecture, space controls optimization, clear separation of concerns Option B: Protocol-BasedDefine what spaces must provide, but don't enforce how: class SpatialSpace(Protocol):
def get_location(self, agent: Agent) -> Any: ...
def move_agent(self, agent: Agent, location: Any) -> None: ...
def get_neighbors(self, agent: Agent, **kwargs) -> list[Agent]: ...
# Each space implements however makes sense internally
class DiscreteSpace: # Can keep current cell-based implementation
def get_location(self, agent):
return self._agent_to_cell[agent]
class ContinuousSpace: # Uses efficient numpy arrays
def get_location(self, agent):
return self._agent_positions[self._agent_to_index[agent]]
# Usage is explicit and clear
physical_neighbors = model.physical_space.get_neighbors(agent, radius=5)
social_contacts = model.social_network.get_neighbors(agent, hops=2)Pros: Maximum flexibility, no required inheritance, easy multiple spaces, explicit queries Option C: Hybrid ApproachSpace owns data, but agents have convenience properties: # Space is source of truth
class DiscreteSpace:
def get_cell(self, agent) -> Cell:
return self._agent_to_cell[agent]
# Agent has convenience property that delegates
class CellAgent(Agent):
@property
def cell(self):
return self._space.get_cell(self)
@cell.setter
def cell(self, new_cell):
self._space.move_agent(self, new_cell)Pros: Keeps convenient API, space still controls data, backward compatible When Spaces AlignThe powerful case is when multiple spaces DO share a coordinate system: class FireModel(Model):
def __init__(self):
# These spaces share coordinates - fire on ground affects air
self.ground = CellSpace(100, 100, coordinate_system="physical")
self.air = ContinuousSpace([[0, 100], [0, 100]], coordinate_system="physical")
class Tree(Agent):
def __init__(self, model, position):
super().__init__(model)
model.ground.add_agent(self, position)
def step(self):
# Query different space types using same coordinate system
my_cell = self.model.ground.get_cell(self)
air_temp = self.model.air.get_property("temperature", my_cell.center)ConsiderationsI'm not sure which way to go at this point. Protocol-based might be the most flexible, it has a few advantages:
This means:
Open questions:
Impact on Agent CompositionThis protocol-based approach also solves the agent composition complexity discussed in https://github.com/projectmesa/mesa/pull/2584/files#r1907499549. If agents don't need space-specific base classes, the composition problem disappears: # No more ContinuousSpaceAgent, CellAgent, etc.
# Just Agent querying whatever spaces it needs
class Boid(Agent):
def __init__(self, model):
super().__init__(model)
model.air_space.add_agent(self, position=[50, 50])
def step(self):
neighbors = self.model.air_space.get_neighbors(self, radius=5)
class HybridAgent(Agent):
def __init__(self, model):
super().__init__(model)
# Register in multiple spaces - no special inheritance needed
model.physical_space.add_agent(self, position=[30, 40])
model.social_network.add_agent(self, node=5)
def step(self):
# Query whichever space is relevant
physical_neighbors = self.model.physical_space.get_neighbors(self, radius=10)
social_contacts = self.model.social_network.get_neighbors(self, hops=2)No capability injection, no multiple inheritance, no magic - just agents querying spaces explicitly. I'm increasingly thinking the explicit space query approach is cleaner and more aligned with our "spaces own the relationship logic" philosophy. Agents don't need special capabilities - they just need to know which space(s) to query. Curious what everybody thinks! |
Beta Was this translation helpful? Give feedback.
-
|
I don't follow your claimed inconsistency. In discrete spaces, we have |
Beta Was this translation helpful? Give feedback.
-
|
I think I compiled an "ultimate" use case which you are doing about everything you can possibly want to do in an spatial-based ABM. We can use it to further develop our conceptual model of space. Note that:
Model overviewA simplified agent-based model where citizens decide whether to migrate between cities based on environmental conditions, transportation networks, and social connections. A region contains multiple cities connected by road and rail networks, where citizens live, work, and travel. Citizens make daily travel decisions (commuting, visiting friends, business trips) using either roads or rails, with road speeds affected by congestion. Over longer timescales, citizens may decide to permanently migrate to different cities based on environmental conditions (temperature, agricultural fertility), economic opportunities, social connections (where their friends live), and ideological compatibility (finding like-minded people). The atmospheric conditions provide environmental context that affects both daily comfort and long-term livability, while the fertility layer influences the economic prosperity of different regions. Spaces1. Air Space (Continuous)
2. City Space (Voronoi Cells)
3. Road Network
4. Rail Network
5. Fertility Layer
6. Social Network
7. Perception Space
Agents: CitizensPosition References
Key Behaviors
Testing RequirementsMultiple coordinate systems:
Different position types:
Cross-space queries:
|
Beta Was this translation helpful? Give feedback.
-
|
In #2875, while adding support for multiple spaces in agents, I mentioned in passing the idea of "derived" spaces. That is, an agent has a location in a primary space, and other spaces can inherit/use this location as well:
Thinking about this a bit more, I realized that something analogous already exists in matplotlib with We might. consider doing something similar in mesa by adding a space = ContinuousSpace([[0, 10], [0,10]], random=model.random)
another_space = Network(graph, share_location=space) |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
-
Before we stabilize the cell space and new continuous space, I would like a discussion how we view spaces, properties and agents, and how they relate or interact to each other.
This is how I currently envision it for Mesa:
Mesa-Space-Conceptual-diagram.pptx
An Agent has a singular position in a single coordinate system. There can be multiple spaces (in which Agents are or are not present (see Q1). Each space can have 0, 1 or multiple properties (see Q2) which have the same "resolution" as the space (see Q3). All properties, spaces and Agents can interact with each other, because they all use a singular coordinate system.
Open questions:
Really curious on all the ideas how conceptually spaces should look in Mesa!
Beta Was this translation helpful? Give feedback.
All reactions