Skip to content
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

Speedup of Agentset.shuffle #2010

Merged
merged 29 commits into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
bc1718c
Make RandomActivationByType.agents_by_type backward compatible
quaquel Jan 15, 2024
a7ddad5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 15, 2024
1f9af49
updated warning
quaquel Jan 15, 2024
59885b6
Merge remote-tracking branch 'upstream/main'
quaquel Jan 15, 2024
b9fe806
Merge remote-tracking branch 'upstream/main'
quaquel Jan 16, 2024
2076b6d
Update .gitignore
quaquel Jan 21, 2024
600c372
Merge remote-tracking branch 'upstream/main'
quaquel Jan 21, 2024
c83179d
Update global_benchmark.py
quaquel Jan 21, 2024
4c8cb8b
pep8 rename
quaquel Jan 21, 2024
7bccdd1
Update schelling.py
quaquel Jan 21, 2024
64d8c46
Update schelling.py
quaquel Jan 21, 2024
1e794d3
Update schelling.py
quaquel Jan 21, 2024
4bc5fac
Update schelling.py
quaquel Jan 21, 2024
9485087
Squashed commit of the following:
quaquel Jan 21, 2024
5cd0e74
Revert "Squashed commit of the following:"
quaquel Jan 21, 2024
63eec5e
Update .gitignore
quaquel Jan 21, 2024
2a4459c
further updates
quaquel Jan 21, 2024
1b5661e
Update benchmarks/WolfSheep/__init__.py
quaquel Jan 21, 2024
2be6424
Merge remote-tracking branch 'upstream/main'
quaquel Jan 22, 2024
c2e9313
Merge remote-tracking branch 'upstream/main'
quaquel Jan 23, 2024
7b8b7ea
Merge remote-tracking branch 'upstream/main'
quaquel Jan 24, 2024
dcf1e50
shuffle update
quaquel Jan 26, 2024
c40a324
Merge remote-tracking branch 'upstream/main'
quaquel Jan 26, 2024
fefe684
Merge branch 'main' into agentset_speed
quaquel Jan 26, 2024
d196386
switch to walrus operator in do
quaquel Jan 27, 2024
4ef66de
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 27, 2024
f910106
revert inplace to False
quaquel Jan 27, 2024
f7072ab
Merge branch 'agentset_speed' of https://github.com/quaquel/mesa into…
quaquel Jan 27, 2024
9710914
remove old code
quaquel Jan 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions mesa/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
except AttributeError:
# model super has not been called
self.model.agents_ = defaultdict(dict)
self.model.agents_[type(self)][self] = None

Check warning on line 57 in mesa/agent.py

View check run for this annotation

Codecov / codecov/patch

mesa/agent.py#L57

Added line #L57 was not covered by tests
self.model.agentset_experimental_warning_given = False

warnings.warn(
Expand Down Expand Up @@ -188,15 +188,21 @@

Returns:
AgentSet: A shuffled AgentSet. Returns the current AgentSet if inplace is True.
"""
shuffled_agents = list(self)
self.random.shuffle(shuffled_agents)

return (
AgentSet(shuffled_agents, self.model)
if not inplace
else self._update(shuffled_agents)
)
Note:
Using inplace = True is more performant

"""
weakrefs = list(self._agents.keyrefs())
self.random.shuffle(weakrefs)

if inplace:
self._agents.data = {entry: None for entry in weakrefs}
Copy link
Contributor

Choose a reason for hiding this comment

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

How come in this line there is no need for (agent := ref()) is not None), but it is needed in L204?

Copy link
Member Author

Choose a reason for hiding this comment

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

When you create a new AgentSet, it has its own internal WeakkeyDict. Each WeakkeyDict creates its own weakrefs.

As can be seen in the docs, weakref.ref takes an optional callback. In the case of WeakkeyDict, this callback is a method on the dict itself (i.e., self.remove). So, when not shuffling in place, I have to unpack the weakrefs to ensure the new WeakkeyDict behaves correctly.

Moreover, AgentSet takes an iteratable of agents as an argument. Not an iterable of weakref. ref instances.

Hope this clarifies.

return self
else:
return AgentSet(
(agent for ref in weakrefs if (agent := ref()) is not None), self.model
quaquel marked this conversation as resolved.
Show resolved Hide resolved
)

def sort(
self,
Expand Down Expand Up @@ -251,9 +257,9 @@
"""
# we iterate over the actual weakref keys and check if weakref is alive before calling the method
res = [
getattr(agentref(), method_name)(*args, **kwargs)
getattr(agent, method_name)(*args, **kwargs)
for agentref in self._agents.keyrefs()
if agentref()
if (agent := agentref()) is not None
]

return res if return_results else self
Expand Down
2 changes: 1 addition & 1 deletion tests/test_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_function(agent):
assert all(a1 == a2 for a1, a2 in zip(agentset.select(), agentset))
assert all(a1 == a2 for a1, a2 in zip(agentset.select(n=5), agentset[:5]))

assert len(agentset.shuffle().select(n=5)) == 5
assert len(agentset.shuffle(inplace=False).select(n=5)) == 5

def test_function(agent):
return agent.unique_id
Expand Down
Loading