-
Notifications
You must be signed in to change notification settings - Fork 926
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
Optimize wolf-sheep movement performance #2565
Comments
Hello, I'd like to work on the project by fixing this issue. Can you assign this to me? Thanks |
Awesome! Let me know if you have any questions. |
@EwoutH I looked into this issue and tried optimizing the code for movement using property layers but I couldn't get that much of a performance boost. Is my approach wrong, and If yes how can it be improved? def move(self):
"""Generic movement logic using property layers."""
current_pos = self.cell.coordinate
x, y = current_pos
w = self.model.grid.width
h = self.model.grid.height
# Get neighbor positions using array operations
neighbors = np.array(
[((x + 1) % w, y), ((x - 1) % w, y), (x, (y + 1) % h), (x, (y - 1) % h)]
)
# Get target layer based on agent type
target_layer = (
self.model.sheep_layer if isinstance(self, Wolf) else self.model.grass_layer
)
avoid_layer = self.model.wolf_layer if isinstance(self, Sheep) else None
# Create masks for valid moves
if avoid_layer is not None:
valid_mask = np.array(
[avoid_layer.data[tuple(pos)] == 0 for pos in neighbors]
)
if not valid_mask.any():
return False
neighbors = neighbors[valid_mask]
target_mask = np.array([target_layer.data[tuple(pos)] > 0 for pos in neighbors])
# Choose target position
if len(neighbors) == 0:
return False
if target_mask.any():
idx = self.random.randint(0, int(target_mask.sum()) - 1)
target_pos = neighbors[target_mask][idx]
else:
idx = self.random.randint(0, len(neighbors) - 1)
target_pos = neighbors[idx]
# Update position and layers
self._update_layer(-1) # Remove from old position
self.cell = self.model.grid[tuple(target_pos)]
self._update_layer(1) # Add to new position
return True Upon running the profiler and looking at results:
|
The current movement implementation in the wolf-sheep model performs redundant checks when agents search their neighborhood for valid moves, after #2503.
mesa/mesa/examples/advanced/wolf_sheep/agents.py
Lines 70 to 72 in dd77c0a
mesa/mesa/examples/advanced/wolf_sheep/agents.py
Lines 78 to 82 in dd77c0a
mesa/mesa/examples/advanced/wolf_sheep/agents.py
Lines 103 to 105 in dd77c0a
This could be performance optimized. Two promising optimization approaches:
Early exit strategy
Instead of checking every cell in the neighborhood for agent types, we can break out of loops early once a valid cell is found. For example, when a sheep checks for wolves, it can immediately skip a cell once a wolf is detected rather than continuing to check other agents.
PropertyLayer-based vectorization
We can leverage Mesa's PropertyLayer to track wolf and grass positions as boolean layers. By having wolves and grass patches update their position in the property layer during movement, we can perform vectorized operations to find valid moves instead of iterating through agent lists. This would allow expressing the entire movement logic using fast numpy operations.
Trade-offs:
Worth benchmarking both approaches against current implementation with different model parameters.
Somewhat related:
The text was updated successfully, but these errors were encountered: