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

Epstein civil violence: Switch to experimental datacollector #158

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
58 changes: 58 additions & 0 deletions examples/epstein_civil_violence/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from mesa.visualization import Slider, SolaraViz

from epstein_civil_violence.agent import Citizen, Cop
from epstein_civil_violence.model import EpsteinCivilViolence


COP_COLOR = "#000000"
AGENT_QUIET_COLOR = "#648FFF"
AGENT_REBEL_COLOR = "#FE6100"
JAIL_COLOR = "#808080"
JAIL_SHAPE = "rect"


def citizen_cop_portrayal(agent):
portrayal = {
"Shape": "circle",
"x": agent.pos[0],
"y": agent.pos[1],
"Filled": "true",
}

if isinstance(agent, Citizen):
color = (
AGENT_QUIET_COLOR if agent.condition == "Quiescent" else AGENT_REBEL_COLOR
)
color = JAIL_COLOR if agent.jail_sentence else color
shape = JAIL_SHAPE if agent.jail_sentence else "circle"
portrayal["color"] = color
portrayal["Shape"] = shape
# TODO add marker to SolaraViz to display rectangle.
portrayal["size"] = 0.5
portrayal["Filled"] = "false"

else:
assert isinstance(agent, Cop)
portrayal["color"] = COP_COLOR
portrayal["size"] = 0.9

return portrayal


model_params = {
"height": 40,
"width": 40,
"citizen_density": Slider("Initial Agent Density", 0.7, 0.0, 0.9, 0.1),
"cop_density": Slider("Initial Cop Density", 0.04, 0.0, 0.1, 0.01),
"citizen_vision": Slider("Citizen Vision", 7, 1, 10, 1),
"cop_vision": Slider("Cop Vision", 7, 1, 10, 1),
"legitimacy": Slider("Government Legitimacy", 0.82, 0.0, 1, 0.01),
"max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1),
}
page = SolaraViz(
EpsteinCivilViolence,
model_params,
measures=[{"Quiescent": "#648FFF", "Active": "#FE6100", "Jailed": "#808080"}],
agent_portrayal=citizen_cop_portrayal,
)
page # noqa
48 changes: 28 additions & 20 deletions examples/epstein_civil_violence/epstein_civil_violence/model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import mesa
from mesa.experimental import DataCollector

from .agent import Citizen, Cop

Expand Down Expand Up @@ -61,23 +62,6 @@ def __init__(
self.schedule = mesa.time.RandomActivation(self)
self.grid = mesa.space.SingleGrid(width, height, torus=True)

model_reporters = {
"Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"),
"Active": lambda m: self.count_type_citizens(m, "Active"),
"Jailed": self.count_jailed,
"Cops": self.count_cops,
}
agent_reporters = {
"x": lambda a: a.pos[0],
"y": lambda a: a.pos[1],
"breed": lambda a: a.breed,
"jail_sentence": lambda a: getattr(a, "jail_sentence", None),
"condition": lambda a: getattr(a, "condition", None),
"arrest_probability": lambda a: getattr(a, "arrest_probability", None),
}
self.datacollector = mesa.DataCollector(
model_reporters=model_reporters, agent_reporters=agent_reporters
)
unique_id = 0
if self.cop_density + self.citizen_density > 1:
raise ValueError("Cop density + citizen density must be less than 1")
Expand All @@ -102,16 +86,40 @@ def __init__(
self.grid[x][y] = citizen
self.schedule.add(citizen)

self.running = True
self.datacollector.collect(self)
model_reporters = {
"Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"),
"Active": lambda m: self.count_type_citizens(m, "Active"),
"Jailed": self.count_jailed,
"Cops": self.count_cops,
}
agent_reporters = {
"x": lambda agents: [pos[0] for pos in agents.get("pos")],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This API is awkward. It is better to use instead lambda a: a.pos[0]. But this means AgentSet needs to have the feature of .apply() along the line of DataFrame's apply.

This also means that the new DataCollector heavily relies on AgentSet in order to remain simple. Simple is good.

Copy link
Member

Choose a reason for hiding this comment

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

I think @quaquel was also thinking in this direction: projectmesa/mesa#1944 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

I am back in office and do have some time. However, I lot has been happening wrt to MESA so @EwoutH if you have 30 minutes to bring me up to speed, I am happy to take a look at the apply idea.

Copy link
Member

Choose a reason for hiding this comment

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

Good to hear! Let me get back to you what works.

Copy link
Member

Choose a reason for hiding this comment

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

@quaquel would tomorrow early afternoon work?

Copy link
Member

Choose a reason for hiding this comment

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

yes, just email me a time and place (online or my office)

"y": lambda agents: [pos[1] for pos in agents.get("pos")],
"breed": "breed",
}
self.citizens = self.get_agents_of_type(Citizen)
self.datacollector = DataCollector(
self,
{
"model": model_reporters,
"agents": agent_reporters,
"citizens": {
"jail_sentence": "jail_sentence",
"condition": "condition",
"arrest_probability": "arrest_probability",
},
},
)

self.datacollector.collect()

def step(self):
"""
Advance the model by one step and collect data.
"""
self.schedule.step()
# collect data
self.datacollector.collect(self)
self.datacollector.collect()
self.iteration += 1
if self.iteration > self.max_iters:
self.running = False
Expand Down
Loading