Mesa 3.0.0rc0__py3-none-any.whl → 3.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Mesa might be problematic. Click here for more details.

Files changed (41) hide show
  1. mesa/__init__.py +1 -1
  2. mesa/agent.py +15 -3
  3. mesa/examples/__init__.py +2 -2
  4. mesa/examples/advanced/epstein_civil_violence/Readme.md +2 -4
  5. mesa/examples/advanced/pd_grid/app.py +1 -1
  6. mesa/examples/advanced/pd_grid/model.py +1 -1
  7. mesa/examples/advanced/sugarscape_g1mt/app.py +18 -2
  8. mesa/examples/advanced/sugarscape_g1mt/model.py +3 -1
  9. mesa/examples/advanced/wolf_sheep/agents.py +53 -39
  10. mesa/examples/advanced/wolf_sheep/app.py +17 -6
  11. mesa/examples/advanced/wolf_sheep/model.py +68 -74
  12. mesa/examples/basic/boid_flockers/agents.py +48 -16
  13. mesa/examples/basic/boid_flockers/app.py +2 -5
  14. mesa/examples/basic/boid_flockers/model.py +55 -19
  15. mesa/examples/basic/boltzmann_wealth_model/agents.py +23 -5
  16. mesa/examples/basic/boltzmann_wealth_model/app.py +8 -3
  17. mesa/examples/basic/boltzmann_wealth_model/model.py +48 -13
  18. mesa/examples/basic/boltzmann_wealth_model/st_app.py +2 -2
  19. mesa/examples/basic/conways_game_of_life/app.py +25 -3
  20. mesa/examples/basic/conways_game_of_life/model.py +2 -2
  21. mesa/examples/basic/schelling/agents.py +9 -5
  22. mesa/examples/basic/schelling/app.py +1 -1
  23. mesa/examples/basic/schelling/model.py +48 -26
  24. mesa/examples/basic/virus_on_network/Readme.md +2 -13
  25. mesa/examples/basic/virus_on_network/app.py +1 -1
  26. mesa/experimental/cell_space/cell_collection.py +14 -2
  27. mesa/experimental/cell_space/discrete_space.py +16 -2
  28. mesa/experimental/devs/simulator.py +59 -14
  29. mesa/model.py +4 -4
  30. mesa/time.py +4 -4
  31. mesa/visualization/__init__.py +1 -1
  32. mesa/visualization/components/matplotlib_components.py +1 -2
  33. mesa/visualization/mpl_space_drawing.py +42 -7
  34. mesa/visualization/solara_viz.py +133 -54
  35. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/METADATA +6 -8
  36. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/RECORD +41 -41
  37. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/WHEEL +1 -1
  38. /mesa/visualization/{UserParam.py → user_param.py} +0 -0
  39. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/entry_points.txt +0 -0
  40. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/licenses/LICENSE +0 -0
  41. {mesa-3.0.0rc0.dist-info → mesa-3.0.2.dist-info}/licenses/NOTICE +0 -0
@@ -3,10 +3,7 @@ from mesa.visualization import Slider, SolaraViz, make_space_component
3
3
 
4
4
 
5
5
  def boid_draw(agent):
6
- if not agent.neighbors: # Only for the first Frame
7
- neighbors = len(agent.model.space.get_neighbors(agent.pos, agent.vision, False))
8
- else:
9
- neighbors = len(agent.neighbors)
6
+ neighbors = len(agent.neighbors)
10
7
 
11
8
  if neighbors <= 1:
12
9
  return {"color": "red", "size": 20}
@@ -51,7 +48,7 @@ model = BoidFlockers()
51
48
 
52
49
  page = SolaraViz(
53
50
  model,
54
- [make_space_component(agent_portrayal=boid_draw, backend="matplotlib")],
51
+ components=[make_space_component(agent_portrayal=boid_draw, backend="matplotlib")],
55
52
  model_params=model_params,
56
53
  name="Boid Flocking Model",
57
54
  )
@@ -1,60 +1,81 @@
1
- """Flockers.
2
- =============================================================
1
+ """
2
+ Boids Flocking Model
3
+ ===================
3
4
  A Mesa implementation of Craig Reynolds's Boids flocker model.
4
5
  Uses numpy arrays to represent vectors.
5
6
  """
6
7
 
7
8
  import numpy as np
8
9
 
9
- import mesa
10
+ from mesa import Model
10
11
  from mesa.examples.basic.boid_flockers.agents import Boid
12
+ from mesa.space import ContinuousSpace
11
13
 
12
14
 
13
- class BoidFlockers(mesa.Model):
15
+ class BoidFlockers(Model):
14
16
  """Flocker model class. Handles agent creation, placement and scheduling."""
15
17
 
16
18
  def __init__(
17
19
  self,
18
- seed=None,
19
20
  population=100,
20
21
  width=100,
21
22
  height=100,
22
- vision=10,
23
23
  speed=1,
24
- separation=1,
24
+ vision=10,
25
+ separation=2,
25
26
  cohere=0.03,
26
27
  separate=0.015,
27
28
  match=0.05,
29
+ seed=None,
28
30
  ):
29
- """Create a new Flockers model.
31
+ """Create a new Boids Flocking model.
30
32
 
31
33
  Args:
32
- population: Number of Boids
33
- width, height: Size of the space.
34
- speed: How fast should the Boids move.
35
- vision: How far around should each Boid look for its neighbors
36
- separation: What's the minimum distance each Boid will attempt to
37
- keep from any other
38
- cohere, separate, match: factors for the relative importance of
39
- the three drives.
34
+ population: Number of Boids in the simulation (default: 100)
35
+ width: Width of the space (default: 100)
36
+ height: Height of the space (default: 100)
37
+ speed: How fast the Boids move (default: 1)
38
+ vision: How far each Boid can see (default: 10)
39
+ separation: Minimum distance between Boids (default: 2)
40
+ cohere: Weight of cohesion behavior (default: 0.03)
41
+ separate: Weight of separation behavior (default: 0.015)
42
+ match: Weight of alignment behavior (default: 0.05)
43
+ seed: Random seed for reproducibility (default: None)
40
44
  """
41
45
  super().__init__(seed=seed)
46
+
47
+ # Model Parameters
42
48
  self.population = population
43
49
  self.vision = vision
44
50
  self.speed = speed
45
51
  self.separation = separation
46
52
 
47
- self.space = mesa.space.ContinuousSpace(width, height, True)
53
+ # Set up the space
54
+ self.space = ContinuousSpace(width, height, torus=True)
55
+
56
+ # Store flocking weights
48
57
  self.factors = {"cohere": cohere, "separate": separate, "match": match}
58
+
59
+ # Create and place the Boid agents
49
60
  self.make_agents()
50
61
 
62
+ # For tracking statistics
63
+ self.average_heading = None
64
+ self.update_average_heading()
65
+
51
66
  def make_agents(self):
52
- """Create self.population agents, with random positions and starting headings."""
67
+ """Create and place all Boid agents randomly in the space."""
53
68
  for _ in range(self.population):
69
+ # Random position
54
70
  x = self.random.random() * self.space.x_max
55
71
  y = self.random.random() * self.space.y_max
56
72
  pos = np.array((x, y))
57
- direction = np.random.random(2) * 2 - 1
73
+
74
+ # Random initial direction
75
+ direction = np.random.random(2) * 2 - 1 # Random vector between -1 and 1
76
+ direction /= np.linalg.norm(direction) # Normalize
77
+
78
+ # Create and place the Boid
58
79
  boid = Boid(
59
80
  model=self,
60
81
  speed=self.speed,
@@ -65,5 +86,20 @@ class BoidFlockers(mesa.Model):
65
86
  )
66
87
  self.space.place_agent(boid, pos)
67
88
 
89
+ def update_average_heading(self):
90
+ """Calculate the average heading (direction) of all Boids."""
91
+ if not self.agents:
92
+ self.average_heading = 0
93
+ return
94
+
95
+ headings = np.array([agent.direction for agent in self.agents])
96
+ mean_heading = np.mean(headings, axis=0)
97
+ self.average_heading = np.arctan2(mean_heading[1], mean_heading[0])
98
+
68
99
  def step(self):
100
+ """Run one step of the model.
101
+
102
+ All agents are activated in random order using the AgentSet shuffle_do method.
103
+ """
69
104
  self.agents.shuffle_do("step")
105
+ self.update_average_heading()
@@ -2,13 +2,26 @@ from mesa import Agent
2
2
 
3
3
 
4
4
  class MoneyAgent(Agent):
5
- """An agent with fixed initial wealth."""
5
+ """An agent with fixed initial wealth.
6
+
7
+ Each agent starts with 1 unit of wealth and can give 1 unit to other agents
8
+ if they occupy the same cell.
9
+
10
+ Attributes:
11
+ wealth (int): The agent's current wealth (starts at 1)
12
+ """
6
13
 
7
14
  def __init__(self, model):
15
+ """Create a new agent.
16
+
17
+ Args:
18
+ model (Model): The model instance that contains the agent
19
+ """
8
20
  super().__init__(model)
9
21
  self.wealth = 1
10
22
 
11
23
  def move(self):
24
+ """Move the agent to a random neighboring cell."""
12
25
  possible_steps = self.model.grid.get_neighborhood(
13
26
  self.pos, moore=True, include_center=False
14
27
  )
@@ -16,16 +29,21 @@ class MoneyAgent(Agent):
16
29
  self.model.grid.move_agent(self, new_position)
17
30
 
18
31
  def give_money(self):
32
+ """Give 1 unit of wealth to a random agent in the same cell."""
19
33
  cellmates = self.model.grid.get_cell_list_contents([self.pos])
20
- cellmates.pop(
21
- cellmates.index(self)
22
- ) # Ensure agent is not giving money to itself
23
- if len(cellmates) > 0:
34
+ # Remove self from potential recipients
35
+ cellmates.pop(cellmates.index(self))
36
+
37
+ if cellmates: # Only give money if there are other agents present
24
38
  other = self.random.choice(cellmates)
25
39
  other.wealth += 1
26
40
  self.wealth -= 1
27
41
 
28
42
  def step(self):
43
+ """Execute one step for the agent:
44
+ 1. Move to a neighboring cell
45
+ 2. If wealth > 0, maybe give money to another agent in the same cell
46
+ """
29
47
  self.move()
30
48
  if self.wealth > 0:
31
49
  self.give_money()
@@ -1,4 +1,4 @@
1
- from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
1
+ from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
2
2
  from mesa.visualization import (
3
3
  SolaraViz,
4
4
  make_plot_component,
@@ -20,6 +20,11 @@ model_params = {
20
20
  "max": 100,
21
21
  "step": 1,
22
22
  },
23
+ "seed": {
24
+ "type": "InputText",
25
+ "value": 42,
26
+ "label": "Random Seed",
27
+ },
23
28
  "width": 10,
24
29
  "height": 10,
25
30
  }
@@ -30,7 +35,7 @@ def post_process(ax):
30
35
 
31
36
 
32
37
  # Create initial model instance
33
- model1 = BoltzmannWealthModel(50, 10, 10)
38
+ model = BoltzmannWealth(50, 10, 10)
34
39
 
35
40
  # Create visualization elements. The visualization elements are solara components
36
41
  # that receive the model instance as a "prop" and display it in a certain way.
@@ -49,7 +54,7 @@ GiniPlot = make_plot_component("Gini")
49
54
  # solara run app.py
50
55
  # It will automatically update and display any changes made to this file
51
56
  page = SolaraViz(
52
- model1,
57
+ model,
53
58
  components=[SpaceGraph, GiniPlot],
54
59
  model_params=model_params,
55
60
  name="Boltzmann Wealth Model",
@@ -1,43 +1,78 @@
1
- import mesa
1
+ """
2
+ Boltzmann Wealth Model
3
+ =====================
4
+
5
+ A simple model of wealth distribution based on the Boltzmann-Gibbs distribution.
6
+ Agents move randomly on a grid, giving one unit of wealth to a random neighbor
7
+ when they occupy the same cell.
8
+ """
9
+
10
+ from mesa import Model
11
+ from mesa.datacollection import DataCollector
2
12
  from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
13
+ from mesa.space import MultiGrid
3
14
 
4
15
 
5
- class BoltzmannWealthModel(mesa.Model):
16
+ class BoltzmannWealth(Model):
6
17
  """A simple model of an economy where agents exchange currency at random.
7
18
 
8
- All the agents begin with one unit of currency, and each time step can give
9
- a unit of currency to another agent. Note how, over time, this produces a
10
- highly skewed distribution of wealth.
19
+ All agents begin with one unit of currency, and each time step agents can give
20
+ a unit of currency to another agent in the same cell. Over time, this produces
21
+ a highly skewed distribution of wealth.
22
+
23
+ Attributes:
24
+ num_agents (int): Number of agents in the model
25
+ grid (MultiGrid): The space in which agents move
26
+ running (bool): Whether the model should continue running
27
+ datacollector (DataCollector): Collects and stores model data
11
28
  """
12
29
 
13
30
  def __init__(self, n=100, width=10, height=10, seed=None):
31
+ """Initialize the model.
32
+
33
+ Args:
34
+ n (int, optional): Number of agents. Defaults to 100.
35
+ width (int, optional): Grid width. Defaults to 10.
36
+ height (int, optional): Grid height. Defaults to 10.
37
+ seed (int, optional): Random seed. Defaults to None.
38
+ """
14
39
  super().__init__(seed=seed)
40
+
15
41
  self.num_agents = n
16
- self.grid = mesa.space.MultiGrid(width, height, True)
42
+ self.grid = MultiGrid(width, height, torus=True)
17
43
 
18
- self.datacollector = mesa.DataCollector(
44
+ # Set up data collection
45
+ self.datacollector = DataCollector(
19
46
  model_reporters={"Gini": self.compute_gini},
20
47
  agent_reporters={"Wealth": "wealth"},
21
48
  )
22
- # Create agents
49
+
50
+ # Create and place the agents
23
51
  for _ in range(self.num_agents):
24
- a = MoneyAgent(self)
52
+ agent = MoneyAgent(self)
25
53
 
26
- # Add the agent to a random grid cell
54
+ # Add agent to random grid cell
27
55
  x = self.random.randrange(self.grid.width)
28
56
  y = self.random.randrange(self.grid.height)
29
- self.grid.place_agent(a, (x, y))
57
+ self.grid.place_agent(agent, (x, y))
30
58
 
31
59
  self.running = True
32
60
  self.datacollector.collect(self)
33
61
 
34
62
  def step(self):
35
- self.agents.shuffle_do("step")
36
- self.datacollector.collect(self)
63
+ self.agents.shuffle_do("step") # Activate all agents in random order
64
+ self.datacollector.collect(self) # Collect data
37
65
 
38
66
  def compute_gini(self):
67
+ """Calculate the Gini coefficient for the model's current wealth distribution.
68
+
69
+ The Gini coefficient is a measure of inequality in distributions.
70
+ - A Gini of 0 represents complete equality, where all agents have equal wealth.
71
+ - A Gini of 1 represents maximal inequality, where one agent has all wealth.
72
+ """
39
73
  agent_wealths = [agent.wealth for agent in self.agents]
40
74
  x = sorted(agent_wealths)
41
75
  n = self.num_agents
76
+ # Calculate using the standard formula for Gini coefficient
42
77
  b = sum(xi * (n - i) for i, xi in enumerate(x)) / (n * sum(x))
43
78
  return 1 + (1 / n) - 2 * b
@@ -5,7 +5,7 @@ import time
5
5
  import altair as alt
6
6
  import pandas as pd
7
7
  import streamlit as st
8
- from model import BoltzmannWealthModel
8
+ from model import BoltzmannWealth
9
9
 
10
10
  model = st.title("Boltzman Wealth Model")
11
11
  num_agents = st.slider(
@@ -19,7 +19,7 @@ num_ticks = st.slider(
19
19
  )
20
20
  height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15)
21
21
  width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20)
22
- model = BoltzmannWealthModel(num_agents, height, width)
22
+ model = BoltzmannWealth(num_agents, height, width)
23
23
 
24
24
 
25
25
  status_text = st.empty()
@@ -20,12 +20,34 @@ def post_process(ax):
20
20
 
21
21
 
22
22
  model_params = {
23
- "width": 50,
24
- "height": 50,
23
+ "width": {
24
+ "type": "SliderInt",
25
+ "value": 50,
26
+ "label": "Width",
27
+ "min": 5,
28
+ "max": 60,
29
+ "step": 1,
30
+ },
31
+ "height": {
32
+ "type": "SliderInt",
33
+ "value": 50,
34
+ "label": "Height",
35
+ "min": 5,
36
+ "max": 60,
37
+ "step": 1,
38
+ },
39
+ "initial_fraction_alive": {
40
+ "type": "SliderFloat",
41
+ "value": 0.2,
42
+ "label": "Cells initially alive",
43
+ "min": 0,
44
+ "max": 1,
45
+ "step": 0.01,
46
+ },
25
47
  }
26
48
 
27
49
  # Create initial model instance
28
- model1 = ConwaysGameOfLife(50, 50)
50
+ model1 = ConwaysGameOfLife()
29
51
 
30
52
  # Create visualization elements. The visualization elements are solara components
31
53
  # that receive the model instance as a "prop" and display it in a certain way.
@@ -6,7 +6,7 @@ from mesa.space import SingleGrid
6
6
  class ConwaysGameOfLife(Model):
7
7
  """Represents the 2-dimensional array of cells in Conway's Game of Life."""
8
8
 
9
- def __init__(self, width=50, height=50, seed=None):
9
+ def __init__(self, width=50, height=50, initial_fraction_alive=0.2, seed=None):
10
10
  """Create a new playing area of (width, height) cells."""
11
11
  super().__init__(seed=seed)
12
12
  # Use a simple grid, where edges wrap around.
@@ -16,7 +16,7 @@ class ConwaysGameOfLife(Model):
16
16
  # ALIVE and some to DEAD.
17
17
  for _contents, (x, y) in self.grid.coord_iter():
18
18
  cell = Cell((x, y), self)
19
- if self.random.random() < 0.1:
19
+ if self.random.random() < initial_fraction_alive:
20
20
  cell.state = cell.ALIVE
21
21
  self.grid.place_agent(cell, (x, y))
22
22
 
@@ -1,25 +1,29 @@
1
- from mesa import Agent, Model
1
+ from mesa import Agent
2
2
 
3
3
 
4
4
  class SchellingAgent(Agent):
5
5
  """Schelling segregation agent."""
6
6
 
7
- def __init__(self, model: Model, agent_type: int) -> None:
7
+ def __init__(self, model, agent_type: int) -> None:
8
8
  """Create a new Schelling agent.
9
9
 
10
10
  Args:
11
- agent_type: Indicator for the agent's type (minority=1, majority=0)
11
+ model: The model instance the agent belongs to
12
+ agent_type: Indicator for the agent's type (minority=1, majority=0)
12
13
  """
13
14
  super().__init__(model)
14
15
  self.type = agent_type
15
16
 
16
17
  def step(self) -> None:
18
+ """Determine if agent is happy and move if necessary."""
17
19
  neighbors = self.model.grid.iter_neighbors(
18
20
  self.pos, moore=True, radius=self.model.radius
19
21
  )
20
- similar = sum(1 for neighbor in neighbors if neighbor.type == self.type)
21
22
 
22
- # If unhappy, move:
23
+ # Count similar neighbors
24
+ similar = sum(neighbor.type == self.type for neighbor in neighbors)
25
+
26
+ # If unhappy, move to a random empty cell:
23
27
  if similar < self.model.homophily:
24
28
  self.model.grid.move_to_empty(self)
25
29
  else:
@@ -26,7 +26,7 @@ model_params = {
26
26
  "height": 20,
27
27
  }
28
28
 
29
- model1 = Schelling(20, 20, 0.8, 0.2, 3)
29
+ model1 = Schelling()
30
30
 
31
31
  HappyPlot = make_plot_component({"happy": "tab:green"})
32
32
 
@@ -1,6 +1,7 @@
1
- import mesa
2
1
  from mesa import Model
2
+ from mesa.datacollection import DataCollector
3
3
  from mesa.examples.basic.schelling.agents import SchellingAgent
4
+ from mesa.space import SingleGrid
4
5
 
5
6
 
6
7
  class Schelling(Model):
@@ -8,52 +9,73 @@ class Schelling(Model):
8
9
 
9
10
  def __init__(
10
11
  self,
11
- height=20,
12
- width=20,
13
- homophily=3,
14
- radius=1,
15
- density=0.8,
16
- minority_pc=0.2,
12
+ height: int = 40,
13
+ width: int = 40,
14
+ density: float = 0.8,
15
+ minority_pc: float = 0.5,
16
+ homophily: int = 3,
17
+ radius: int = 1,
17
18
  seed=None,
18
19
  ):
19
20
  """Create a new Schelling model.
20
21
 
21
22
  Args:
22
- width, height: Size of the space.
23
- density: Initial Chance for a cell to populated
24
- minority_pc: Chances for an agent to be in minority class
25
- homophily: Minimum number of agents of same class needed to be happy
26
- radius: Search radius for checking similarity
27
- seed: Seed for Reproducibility
23
+ width: Width of the grid
24
+ height: Height of the grid
25
+ density: Initial chance for a cell to be populated (0-1)
26
+ minority_pc: Chance for an agent to be in minority class (0-1)
27
+ homophily: Minimum number of similar neighbors needed for happiness
28
+ radius: Search radius for checking neighbor similarity
29
+ seed: Seed for reproducibility
28
30
  """
29
31
  super().__init__(seed=seed)
32
+
33
+ # Model parameters
34
+ self.height = height
35
+ self.width = width
36
+ self.density = density
37
+ self.minority_pc = minority_pc
30
38
  self.homophily = homophily
31
39
  self.radius = radius
32
40
 
33
- self.grid = mesa.space.SingleGrid(width, height, torus=True)
41
+ # Initialize grid
42
+ self.grid = SingleGrid(width, height, torus=True)
34
43
 
44
+ # Track happiness
35
45
  self.happy = 0
36
- self.datacollector = mesa.DataCollector(
37
- model_reporters={"happy": "happy"}, # Model-level count of happy agents
46
+
47
+ # Set up data collection
48
+ self.datacollector = DataCollector(
49
+ model_reporters={
50
+ "happy": "happy",
51
+ "pct_happy": lambda m: (m.happy / len(m.agents)) * 100
52
+ if len(m.agents) > 0
53
+ else 0,
54
+ "population": lambda m: len(m.agents),
55
+ "minority_pct": lambda m: (
56
+ sum(1 for agent in m.agents if agent.type == 1)
57
+ / len(m.agents)
58
+ * 100
59
+ if len(m.agents) > 0
60
+ else 0
61
+ ),
62
+ },
63
+ agent_reporters={"agent_type": "type"},
38
64
  )
39
65
 
40
- # Set up agents
41
- # We use a grid iterator that returns
42
- # the coordinates of a cell as well as
43
- # its contents. (coord_iter)
66
+ # Create agents and place them on the grid
44
67
  for _, pos in self.grid.coord_iter():
45
- if self.random.random() < density:
68
+ if self.random.random() < self.density:
46
69
  agent_type = 1 if self.random.random() < minority_pc else 0
47
70
  agent = SchellingAgent(self, agent_type)
48
71
  self.grid.place_agent(agent, pos)
49
72
 
73
+ # Collect initial state
50
74
  self.datacollector.collect(self)
51
75
 
52
76
  def step(self):
53
77
  """Run one step of the model."""
54
78
  self.happy = 0 # Reset counter of happy agents
55
- self.agents.shuffle_do("step")
56
-
57
- self.datacollector.collect(self)
58
-
59
- self.running = self.happy != len(self.agents)
79
+ self.agents.shuffle_do("step") # Activate all agents in random order
80
+ self.datacollector.collect(self) # Collect data
81
+ self.running = self.happy < len(self.agents) # Continue until everyone is happy
@@ -24,23 +24,12 @@ To install the dependencies use pip and the requirements.txt in this directory.
24
24
 
25
25
  ## How to Run
26
26
 
27
- To run the model interactively, run ``mesa runserver`` in this directory. e.g.
27
+ To run the model interactively, in this directory, run the following command
28
28
 
29
29
  ```
30
- $ mesa runserver
30
+ $ solara run app.py
31
31
  ```
32
32
 
33
- Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
34
-
35
- or
36
-
37
- Directly run the file ``run.py`` in the terminal. e.g.
38
-
39
- ```
40
- $ python run.py
41
- ```
42
-
43
-
44
33
  ## Files
45
34
 
46
35
  * ``model.py``: Contains the agent class, and the overall model class.
@@ -103,7 +103,7 @@ model1 = VirusOnNetwork()
103
103
 
104
104
  page = SolaraViz(
105
105
  model1,
106
- [
106
+ components=[
107
107
  SpacePlot,
108
108
  StatePlot,
109
109
  get_resistant_susceptible_ratio,
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import itertools
6
+ import warnings
6
7
  from collections.abc import Callable, Iterable, Mapping
7
8
  from functools import cached_property
8
9
  from random import Random
@@ -23,6 +24,12 @@ class CellCollection(Generic[T]):
23
24
  agents (List[CellAgent]) : List of agents occupying the cells in this collection
24
25
  random (Random) : The random number generator
25
26
 
27
+ Notes:
28
+ A `UserWarning` is issued if `random=None`. You can resolve this warning by explicitly
29
+ passing a random number generator. In most cases, this will be the seeded random number
30
+ generator in the model. So, you would do `random=self.random` in a `Model` or `Agent` instance.
31
+
32
+
26
33
  """
27
34
 
28
35
  def __init__(
@@ -45,7 +52,12 @@ class CellCollection(Generic[T]):
45
52
  self._capacity: int = next(iter(self._cells.keys())).capacity
46
53
 
47
54
  if random is None:
48
- random = Random() # FIXME
55
+ warnings.warn(
56
+ "Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly",
57
+ UserWarning,
58
+ stacklevel=2,
59
+ )
60
+ random = Random()
49
61
  self.random = random
50
62
 
51
63
  def __iter__(self): # noqa
@@ -115,4 +127,4 @@ class CellCollection(Generic[T]):
115
127
  yield cell
116
128
  count += 1
117
129
 
118
- return CellCollection(cell_generator(filter_func, at_most))
130
+ return CellCollection(cell_generator(filter_func, at_most), random=self.random)