Skip to content

Conversation

@adamamer20
Copy link
Member

@adamamer20 adamamer20 commented Aug 19, 2024

This PR adds the first example of mesa-frames, SugarScape with Instantaneous Growback. Results are very promising as you can see in the plot (I tried to make results comparable, changing mesa.model.agents to a list instead of AgentSet).

The model is completely analogous to mesa-examples, where agents take decisions sequentially. Since agents' sequential decisions might affect the State Space for other agents, I implemented a looping mechanism. Note that although there is a loop, vectorized operations are still used and the iterations are minimal.

I considered a ranking mechanism to completely avoid loops, but I believe it's not possible. There's potential to use Numba for a custom rolling expression, which will be added in a future PR.

Observations for future issues found during development:

  1. It would be beneficial to have a function in the DiscreteSpaceDF (and AgentContainer) for moving agents based on attributes:

    move_to(agents: AgentLike, attr_names: str | list[str], rank_order: ['max' | 'min'] | list['max' | 'min'])

    For example: move_to('sugar', 'max'). This would abstract the code for handling sequential conflicts, making it easier than handling them from scratch each time.

  2. Previously, I returned pandas DataFrames with set indices for cells or agents:

    • self.space.cells had dimensions as index
    • self.agents had 'unique_id' as index

    This causes issues with pandas operations due to index alignment. It might be better to always return a neutral (1,n) index, making the functionality more similar to polars, which doesn't have an index.

  3. Renaming self.agents to self.df will make the API clearer for AgentContainer.

  4. When get_neighborhood or get_neighbors are called with agents, it should return 'agent_id_center' to indicate which agent_id called, rather than 'dim_0_center' and 'dim_1_center' (or possibly both?).

  5. I encountered and solved some bugs related to edge cases and duck typing. As the project scales, we might need to seriously consider using hypothesis/deal and runtime type checking.

Tomorrow, I will upload Sugarscape IG with Polars.

@adamamer20 adamamer20 linked an issue Aug 19, 2024 that may be closed by this pull request
4 tasks
@adamamer20 adamamer20 requested a review from rht August 19, 2024 16:43
@adamamer20 adamamer20 self-assigned this Aug 19, 2024
@adamamer20 adamamer20 added the examples additions or modifications to sample models label Aug 19, 2024
@rht
Copy link
Contributor

rht commented Aug 19, 2024

🚀 👨‍🚀 👩‍🚀 finally! @tpike3 check this out.

@adamamer20 adamamer20 added this to the 0.1.0 Alpha Release milestone Aug 19, 2024
@codecov
Copy link

codecov bot commented Aug 19, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

@adamamer20 adamamer20 removed a link to an issue Aug 19, 2024
4 tasks
@adamamer20 adamamer20 linked an issue Aug 19, 2024 that may be closed by this pull request
best_moves = pd.DataFrame()

# While there are agents that do not have a best move, keep looking for one
while len(best_moves) < len(self.agents):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome, a hybrid of sequential and GPU/CPU parallelizable executions (whenever there is no collision). We should use this pattern a lot more in other models. I would want to know how many iterations this does across the steps, as a function of (num_agents / (width * height)).

I can't remember how the FLAME 2 folks did it, or how the Sugarscape on steroid folks did it. I think the former did tie-breaking whenever there is a collision?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would want to know how many iterations this does across the steps, as a function of (num_agents / (width * height)).

This is optional, by the way. There are a lot more other pressing stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome, a hybrid of sequential and GPU/CPU parallelizable executions (whenever there is no collision). We should use this pattern a lot more in other models. I would want to know how many iterations this does across the steps, as a function of (num_agents / (width * height)).

I can't remember how the FLAME 2 folks did it, or how the Sugarscape on steroid folks did it. I think the former did tie-breaking whenever there is a collision?

Yes, you mentioned that, I don't know about enough CUDA to look through the code :).
When prototyping with 100 agents 10*10 space (so full of collisions) was about 4 iterations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's really good to know. I'm going over the CUDA code right now. I approved this PR so that we can move faster. Any direct comparison with the same algorithm as FLAME 2 can happen later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The movement diagram for FLAME GPU 2 can be found at figure 6 of the paper:

  1. Movement request
  2. Movement response, if there is a conflict, tie breaking happens, where the priority is chosen at random float message_priority = FLAMEGPU->random.uniform<float>().
  3. Movement transaction

Based on what they said:

The cell responds by selecting the agent with highest priority therefore allowing a single agent to move in the first iteration of the model. An exit condition only allows the main simulation to resume once all agents are ‘resolved’ and as such a second iteration of the sub-model is required for the second agent to find a collision free location (and the exit condition to pass).

That "2nd iteration" could probably happen several times as long as there are still conflicts, I think? Their algorithm might be very similar to yours.

neighborhood.groupby("agent_id_center", sort=False)
.first()
.sort_values("agent_order")
.drop_duplicates(["dim_0", "dim_1"], keep="first")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ["dim_0", "dim_1"] pair keeps occurring several times. What if we name them, in a way that can be imported, such as from mesa_frames import POSITION_INDEX (or POSITION_INDICES, POSITION_COLUMNS)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah we can definitely do that, but you mean for type hinting?
I was thinking of refactoring DataFrameMixin more "object-like" having DF and index as attributes and putting an interface on that. we are building the backend for all DataFrames, we might as well use AgentSetDF as interface.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was for making it more semantic. It was capitalized because it's a constant. Would the mental model & designing the code be easier with object attributes instead of DF columns?

self.max_sugar = max_sugar

def step(self):
self.amount = self.max_sugar
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so that it is apple vs apple benchmark comparison. Even though this probably won't affect the runtime that much.

@rht rht merged commit 073c045 into main Aug 20, 2024
@rht rht deleted the 54-sugarscape-with-instantaneous-growback branch August 20, 2024 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

examples additions or modifications to sample models

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SugarScape Instantaneous Growback (Pandas-With-Loops)

3 participants