Mesa 3.1.4__py3-none-any.whl → 3.2.0.dev0__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 (47) hide show
  1. mesa/__init__.py +3 -1
  2. mesa/agent.py +20 -5
  3. mesa/batchrunner.py +6 -3
  4. mesa/discrete_space/__init__.py +50 -0
  5. mesa/{experimental/cell_space → discrete_space}/cell.py +29 -10
  6. mesa/{experimental/cell_space → discrete_space}/cell_agent.py +1 -1
  7. mesa/{experimental/cell_space → discrete_space}/cell_collection.py +3 -3
  8. mesa/{experimental/cell_space → discrete_space}/discrete_space.py +65 -3
  9. mesa/{experimental/cell_space → discrete_space}/grid.py +2 -2
  10. mesa/{experimental/cell_space → discrete_space}/network.py +22 -2
  11. mesa/{experimental/cell_space → discrete_space}/property_layer.py +1 -10
  12. mesa/{experimental/cell_space → discrete_space}/voronoi.py +2 -2
  13. mesa/examples/README.md +9 -4
  14. mesa/examples/advanced/epstein_civil_violence/agents.py +1 -1
  15. mesa/examples/advanced/epstein_civil_violence/model.py +1 -1
  16. mesa/examples/advanced/pd_grid/agents.py +1 -1
  17. mesa/examples/advanced/pd_grid/model.py +1 -1
  18. mesa/examples/advanced/sugarscape_g1mt/agents.py +1 -1
  19. mesa/examples/advanced/sugarscape_g1mt/model.py +2 -2
  20. mesa/examples/advanced/wolf_sheep/agents.py +1 -1
  21. mesa/examples/advanced/wolf_sheep/app.py +2 -1
  22. mesa/examples/advanced/wolf_sheep/model.py +1 -1
  23. mesa/examples/basic/boid_flockers/agents.py +1 -0
  24. mesa/examples/basic/boid_flockers/app.py +17 -2
  25. mesa/examples/basic/boid_flockers/model.py +12 -0
  26. mesa/examples/basic/boltzmann_wealth_model/agents.py +6 -11
  27. mesa/examples/basic/boltzmann_wealth_model/app.py +2 -2
  28. mesa/examples/basic/boltzmann_wealth_model/model.py +7 -11
  29. mesa/examples/basic/conways_game_of_life/agents.py +13 -5
  30. mesa/examples/basic/conways_game_of_life/model.py +10 -7
  31. mesa/examples/basic/schelling/agents.py +13 -8
  32. mesa/examples/basic/schelling/model.py +6 -9
  33. mesa/examples/basic/virus_on_network/agents.py +13 -17
  34. mesa/examples/basic/virus_on_network/model.py +20 -24
  35. mesa/experimental/__init__.py +2 -2
  36. mesa/experimental/cell_space/__init__.py +18 -8
  37. mesa/space.py +1 -12
  38. mesa/visualization/__init__.py +2 -0
  39. mesa/visualization/command_console.py +482 -0
  40. mesa/visualization/components/altair_components.py +276 -16
  41. mesa/visualization/mpl_space_drawing.py +17 -9
  42. mesa/visualization/solara_viz.py +150 -21
  43. {mesa-3.1.4.dist-info → mesa-3.2.0.dev0.dist-info}/METADATA +12 -8
  44. {mesa-3.1.4.dist-info → mesa-3.2.0.dev0.dist-info}/RECORD +47 -45
  45. {mesa-3.1.4.dist-info → mesa-3.2.0.dev0.dist-info}/WHEEL +0 -0
  46. {mesa-3.1.4.dist-info → mesa-3.2.0.dev0.dist-info}/licenses/LICENSE +0 -0
  47. {mesa-3.1.4.dist-info → mesa-3.2.0.dev0.dist-info}/licenses/NOTICE +0 -0
@@ -1,7 +1,7 @@
1
- from mesa import Agent
1
+ from mesa.discrete_space import CellAgent
2
2
 
3
3
 
4
- class MoneyAgent(Agent):
4
+ class MoneyAgent(CellAgent):
5
5
  """An agent with fixed initial wealth.
6
6
 
7
7
  Each agent starts with 1 unit of wealth and can give 1 unit to other agents
@@ -11,28 +11,23 @@ class MoneyAgent(Agent):
11
11
  wealth (int): The agent's current wealth (starts at 1)
12
12
  """
13
13
 
14
- def __init__(self, model):
14
+ def __init__(self, model, cell):
15
15
  """Create a new agent.
16
16
 
17
17
  Args:
18
18
  model (Model): The model instance that contains the agent
19
19
  """
20
20
  super().__init__(model)
21
+ self.cell = cell
21
22
  self.wealth = 1
22
23
 
23
24
  def move(self):
24
25
  """Move the agent to a random neighboring cell."""
25
- possible_steps = self.model.grid.get_neighborhood(
26
- self.pos, moore=True, include_center=False
27
- )
28
- new_position = self.random.choice(possible_steps)
29
- self.model.grid.move_agent(self, new_position)
26
+ self.cell = self.cell.neighborhood.select_random_cell()
30
27
 
31
28
  def give_money(self):
32
29
  """Give 1 unit of wealth to a random agent in the same cell."""
33
- cellmates = self.model.grid.get_cell_list_contents([self.pos])
34
- # Remove self from potential recipients
35
- cellmates.pop(cellmates.index(self))
30
+ cellmates = [a for a in self.cell.agents if a is not self]
36
31
 
37
32
  if cellmates: # Only give money if there are other agents present
38
33
  other = self.random.choice(cellmates)
@@ -1,12 +1,12 @@
1
1
  from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
2
- from mesa.mesa_logging import DEBUG, log_to_stderr
2
+ from mesa.mesa_logging import INFO, log_to_stderr
3
3
  from mesa.visualization import (
4
4
  SolaraViz,
5
5
  make_plot_component,
6
6
  make_space_component,
7
7
  )
8
8
 
9
- log_to_stderr(DEBUG)
9
+ log_to_stderr(INFO)
10
10
 
11
11
 
12
12
  def agent_portrayal(agent):
@@ -9,8 +9,8 @@ when they occupy the same cell.
9
9
 
10
10
  from mesa import Model
11
11
  from mesa.datacollection import DataCollector
12
+ from mesa.discrete_space import OrthogonalMooreGrid
12
13
  from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
13
- from mesa.space import MultiGrid
14
14
 
15
15
 
16
16
  class BoltzmannWealth(Model):
@@ -39,22 +39,18 @@ class BoltzmannWealth(Model):
39
39
  super().__init__(seed=seed)
40
40
 
41
41
  self.num_agents = n
42
- self.grid = MultiGrid(width, height, torus=True)
42
+ self.grid = OrthogonalMooreGrid((width, height), random=self.random)
43
43
 
44
44
  # Set up data collection
45
45
  self.datacollector = DataCollector(
46
46
  model_reporters={"Gini": self.compute_gini},
47
47
  agent_reporters={"Wealth": "wealth"},
48
48
  )
49
-
50
- # Create and place the agents
51
- for _ in range(self.num_agents):
52
- agent = MoneyAgent(self)
53
-
54
- # Add agent to random grid cell
55
- x = self.random.randrange(self.grid.width)
56
- y = self.random.randrange(self.grid.height)
57
- self.grid.place_agent(agent, (x, y))
49
+ MoneyAgent.create_agents(
50
+ self,
51
+ self.num_agents,
52
+ self.random.choices(self.grid.all_cells.cells, k=self.num_agents),
53
+ )
58
54
 
59
55
  self.running = True
60
56
  self.datacollector.collect(self)
@@ -1,16 +1,24 @@
1
- from mesa import Agent
1
+ from mesa.discrete_space import FixedAgent
2
2
 
3
3
 
4
- class Cell(Agent):
4
+ class Cell(FixedAgent):
5
5
  """Represents a single ALIVE or DEAD cell in the simulation."""
6
6
 
7
7
  DEAD = 0
8
8
  ALIVE = 1
9
9
 
10
- def __init__(self, pos, model, init_state=DEAD):
10
+ @property
11
+ def x(self):
12
+ return self.cell.coordinate[0]
13
+
14
+ @property
15
+ def y(self):
16
+ return self.cell.coordinate[1]
17
+
18
+ def __init__(self, model, cell, init_state=DEAD):
11
19
  """Create a cell, in the given state, at the given x, y position."""
12
20
  super().__init__(model)
13
- self.x, self.y = pos
21
+ self.cell = cell
14
22
  self.state = init_state
15
23
  self._next_state = None
16
24
 
@@ -20,7 +28,7 @@ class Cell(Agent):
20
28
 
21
29
  @property
22
30
  def neighbors(self):
23
- return self.model.grid.iter_neighbors((self.x, self.y), True)
31
+ return self.cell.neighborhood.agents
24
32
 
25
33
  def determine_state(self):
26
34
  """Compute if the cell will be dead or alive at the next tick. This is
@@ -1,6 +1,6 @@
1
1
  from mesa import Model
2
+ from mesa.discrete_space import OrthogonalMooreGrid
2
3
  from mesa.examples.basic.conways_game_of_life.agents import Cell
3
- from mesa.space import SingleGrid
4
4
 
5
5
 
6
6
  class ConwaysGameOfLife(Model):
@@ -10,15 +10,18 @@ class ConwaysGameOfLife(Model):
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.
13
- self.grid = SingleGrid(width, height, torus=True)
13
+ self.grid = OrthogonalMooreGrid((width, height), capacity=1, torus=True)
14
14
 
15
15
  # Place a cell at each location, with some initialized to
16
16
  # ALIVE and some to DEAD.
17
- for _contents, (x, y) in self.grid.coord_iter():
18
- cell = Cell((x, y), self)
19
- if self.random.random() < initial_fraction_alive:
20
- cell.state = cell.ALIVE
21
- self.grid.place_agent(cell, (x, y))
17
+ for cell in self.grid.all_cells:
18
+ Cell(
19
+ self,
20
+ cell,
21
+ init_state=Cell.ALIVE
22
+ if self.random.random() < initial_fraction_alive
23
+ else Cell.DEAD,
24
+ )
22
25
 
23
26
  self.running = True
24
27
 
@@ -1,23 +1,28 @@
1
- from mesa import Agent
1
+ from mesa.discrete_space import CellAgent
2
2
 
3
3
 
4
- class SchellingAgent(Agent):
4
+ class SchellingAgent(CellAgent):
5
5
  """Schelling segregation agent."""
6
6
 
7
- def __init__(self, model, agent_type: int) -> None:
7
+ def __init__(
8
+ self, model, cell, agent_type: int, homophily: float = 0.4, radius: int = 1
9
+ ) -> None:
8
10
  """Create a new Schelling agent.
9
11
  Args:
10
12
  model: The model instance the agent belongs to
11
13
  agent_type: Indicator for the agent's type (minority=1, majority=0)
14
+ homophily: Minimum number of similar neighbors needed for happiness
15
+ radius: Search radius for checking neighbor similarity
12
16
  """
13
17
  super().__init__(model)
18
+ self.cell = cell
14
19
  self.type = agent_type
20
+ self.homophily = homophily
21
+ self.radius = radius
15
22
 
16
23
  def step(self) -> None:
17
24
  """Determine if agent is happy and move if necessary."""
18
- neighbors = self.model.grid.get_neighbors(
19
- self.pos, moore=True, radius=self.model.radius
20
- )
25
+ neighbors = list(self.cell.get_neighborhood(radius=self.radius).agents)
21
26
 
22
27
  # Count similar neighbors
23
28
  similar_neighbors = len([n for n in neighbors if n.type == self.type])
@@ -30,7 +35,7 @@ class SchellingAgent(Agent):
30
35
  similarity_fraction = 0.0
31
36
 
32
37
  # Move if unhappy
33
- if similarity_fraction < self.model.homophily:
34
- self.model.grid.move_to_empty(self)
38
+ if similarity_fraction < self.homophily:
39
+ self.cell = self.model.grid.select_random_empty_cell()
35
40
  else:
36
41
  self.model.happy += 1
@@ -1,7 +1,7 @@
1
1
  from mesa import Model
2
2
  from mesa.datacollection import DataCollector
3
+ from mesa.discrete_space import OrthogonalMooreGrid
3
4
  from mesa.examples.basic.schelling.agents import SchellingAgent
4
- from mesa.space import SingleGrid
5
5
 
6
6
 
7
7
  class Schelling(Model):
@@ -31,15 +31,11 @@ class Schelling(Model):
31
31
  super().__init__(seed=seed)
32
32
 
33
33
  # Model parameters
34
- self.height = height
35
- self.width = width
36
34
  self.density = density
37
35
  self.minority_pc = minority_pc
38
- self.homophily = homophily
39
- self.radius = radius
40
36
 
41
37
  # Initialize grid
42
- self.grid = SingleGrid(width, height, torus=True)
38
+ self.grid = OrthogonalMooreGrid((width, height), random=self.random, capacity=1)
43
39
 
44
40
  # Track happiness
45
41
  self.happy = 0
@@ -64,11 +60,12 @@ class Schelling(Model):
64
60
  )
65
61
 
66
62
  # Create agents and place them on the grid
67
- for _, pos in self.grid.coord_iter():
63
+ for cell in self.grid.all_cells:
68
64
  if self.random.random() < self.density:
69
65
  agent_type = 1 if self.random.random() < minority_pc else 0
70
- agent = SchellingAgent(self, agent_type)
71
- self.grid.place_agent(agent, pos)
66
+ SchellingAgent(
67
+ self, cell, agent_type, homophily=homophily, radius=radius
68
+ )
72
69
 
73
70
  # Collect initial state
74
71
  self.datacollector.collect(self)
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
2
 
3
- from mesa import Agent
3
+ from mesa.discrete_space import FixedAgent
4
4
 
5
5
 
6
6
  class State(Enum):
@@ -9,7 +9,7 @@ class State(Enum):
9
9
  RESISTANT = 2
10
10
 
11
11
 
12
- class VirusAgent(Agent):
12
+ class VirusAgent(FixedAgent):
13
13
  """Individual Agent definition and its properties/interaction methods."""
14
14
 
15
15
  def __init__(
@@ -20,6 +20,7 @@ class VirusAgent(Agent):
20
20
  virus_check_frequency,
21
21
  recovery_chance,
22
22
  gain_resistance_chance,
23
+ cell,
23
24
  ):
24
25
  super().__init__(model)
25
26
 
@@ -29,19 +30,14 @@ class VirusAgent(Agent):
29
30
  self.virus_check_frequency = virus_check_frequency
30
31
  self.recovery_chance = recovery_chance
31
32
  self.gain_resistance_chance = gain_resistance_chance
33
+ self.cell = cell
32
34
 
33
35
  def try_to_infect_neighbors(self):
34
- neighbors_nodes = self.model.grid.get_neighborhood(
35
- self.pos, include_center=False
36
- )
37
- susceptible_neighbors = [
38
- agent
39
- for agent in self.model.grid.get_cell_list_contents(neighbors_nodes)
40
- if agent.state is State.SUSCEPTIBLE
41
- ]
42
- for a in susceptible_neighbors:
43
- if self.random.random() < self.virus_spread_chance:
44
- a.state = State.INFECTED
36
+ for agent in self.cell.neighborhood.agents:
37
+ if (agent.state is State.SUSCEPTIBLE) and (
38
+ self.random.random() < self.virus_spread_chance
39
+ ):
40
+ agent.state = State.INFECTED
45
41
 
46
42
  def try_gain_resistance(self):
47
43
  if self.random.random() < self.gain_resistance_chance:
@@ -57,13 +53,13 @@ class VirusAgent(Agent):
57
53
  # Failed
58
54
  self.state = State.INFECTED
59
55
 
60
- def try_check_situation(self):
61
- if (self.random.random() < self.virus_check_frequency) and (
62
- self.state is State.INFECTED
56
+ def check_situation(self):
57
+ if (self.state is State.INFECTED) and (
58
+ self.random.random() < self.virus_check_frequency
63
59
  ):
64
60
  self.try_remove_infection()
65
61
 
66
62
  def step(self):
67
63
  if self.state is State.INFECTED:
68
64
  self.try_to_infect_neighbors()
69
- self.try_check_situation()
65
+ self.check_situation()
@@ -4,11 +4,12 @@ import networkx as nx
4
4
 
5
5
  import mesa
6
6
  from mesa import Model
7
+ from mesa.discrete_space import CellCollection, Network
7
8
  from mesa.examples.basic.virus_on_network.agents import State, VirusAgent
8
9
 
9
10
 
10
11
  def number_state(model, state):
11
- return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state)
12
+ return sum(1 for a in model.grid.all_cells.agents if a.state is state)
12
13
 
13
14
 
14
15
  def number_infected(model):
@@ -38,18 +39,13 @@ class VirusOnNetwork(Model):
38
39
  seed=None,
39
40
  ):
40
41
  super().__init__(seed=seed)
41
- self.num_nodes = num_nodes
42
- prob = avg_node_degree / self.num_nodes
43
- self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob)
44
- self.grid = mesa.space.NetworkGrid(self.G)
42
+ prob = avg_node_degree / num_nodes
43
+ graph = nx.erdos_renyi_graph(n=num_nodes, p=prob)
44
+ self.grid = Network(graph, capacity=1, random=self.random)
45
45
 
46
46
  self.initial_outbreak_size = (
47
47
  initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes
48
48
  )
49
- self.virus_spread_chance = virus_spread_chance
50
- self.virus_check_frequency = virus_check_frequency
51
- self.recovery_chance = recovery_chance
52
- self.gain_resistance_chance = gain_resistance_chance
53
49
 
54
50
  self.datacollector = mesa.DataCollector(
55
51
  {
@@ -60,23 +56,23 @@ class VirusOnNetwork(Model):
60
56
  }
61
57
  )
62
58
 
63
- # Create agents
64
- for node in self.G.nodes():
65
- a = VirusAgent(
66
- self,
67
- State.SUSCEPTIBLE,
68
- self.virus_spread_chance,
69
- self.virus_check_frequency,
70
- self.recovery_chance,
71
- self.gain_resistance_chance,
72
- )
73
-
74
- # Add the agent to the node
75
- self.grid.place_agent(a, node)
59
+ VirusAgent.create_agents(
60
+ self,
61
+ num_nodes,
62
+ State.SUSCEPTIBLE,
63
+ virus_spread_chance,
64
+ virus_check_frequency,
65
+ recovery_chance,
66
+ gain_resistance_chance,
67
+ list(self.grid.all_cells),
68
+ )
76
69
 
77
70
  # Infect some nodes
78
- infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size)
79
- for a in self.grid.get_cell_list_contents(infected_nodes):
71
+ infected_nodes = CellCollection(
72
+ self.random.sample(list(self.grid.all_cells), self.initial_outbreak_size),
73
+ random=self.random,
74
+ )
75
+ for a in infected_nodes.agents:
80
76
  a.state = State.INFECTED
81
77
 
82
78
  self.running = True
@@ -15,6 +15,6 @@ Notes:
15
15
  - Features graduate from experimental status once their APIs are stabilized
16
16
  """
17
17
 
18
- from mesa.experimental import cell_space, continuous_space, devs, mesa_signals
18
+ from mesa.experimental import continuous_space, devs, mesa_signals
19
19
 
20
- __all__ = ["cell_space", "continuous_space", "devs", "mesa_signals"]
20
+ __all__ = ["continuous_space", "devs", "mesa_signals"]
@@ -15,23 +15,25 @@ like resource growth, pollution diffusion, or infrastructure networks. The cell
15
15
  space system is experimental and under active development.
16
16
  """
17
17
 
18
- from mesa.experimental.cell_space.cell import Cell
19
- from mesa.experimental.cell_space.cell_agent import (
18
+ import warnings
19
+
20
+ from mesa.discrete_space.cell import Cell
21
+ from mesa.discrete_space.cell_agent import (
20
22
  CellAgent,
21
23
  FixedAgent,
22
24
  Grid2DMovingAgent,
23
25
  )
24
- from mesa.experimental.cell_space.cell_collection import CellCollection
25
- from mesa.experimental.cell_space.discrete_space import DiscreteSpace
26
- from mesa.experimental.cell_space.grid import (
26
+ from mesa.discrete_space.cell_collection import CellCollection
27
+ from mesa.discrete_space.discrete_space import DiscreteSpace
28
+ from mesa.discrete_space.grid import (
27
29
  Grid,
28
30
  HexGrid,
29
31
  OrthogonalMooreGrid,
30
32
  OrthogonalVonNeumannGrid,
31
33
  )
32
- from mesa.experimental.cell_space.network import Network
33
- from mesa.experimental.cell_space.property_layer import PropertyLayer
34
- from mesa.experimental.cell_space.voronoi import VoronoiGrid
34
+ from mesa.discrete_space.network import Network
35
+ from mesa.discrete_space.property_layer import PropertyLayer
36
+ from mesa.discrete_space.voronoi import VoronoiGrid
35
37
 
36
38
  __all__ = [
37
39
  "Cell",
@@ -48,3 +50,11 @@ __all__ = [
48
50
  "PropertyLayer",
49
51
  "VoronoiGrid",
50
52
  ]
53
+
54
+
55
+ warnings.warn(
56
+ "you are importing from mesa.experimental.cell_space, "
57
+ "all cell spaces have been moved to mesa.discrete_space",
58
+ DeprecationWarning,
59
+ stacklevel=2,
60
+ )
mesa/space.py CHANGED
@@ -6,7 +6,7 @@ Objects used to add a spatial component to a model.
6
6
  All Grid classes (:class:`_Grid`, :class:`SingleGrid`, :class:`MultiGrid`,
7
7
  :class:`HexGrid`, etc.) are now in maintenance-only mode. While these classes remain
8
8
  fully supported, new development occurs in the experimental cell space module
9
- (:mod:`mesa.experimental.cell_space`).
9
+ (:mod:`mesa.discrete_space`).
10
10
 
11
11
  The :class:`PropertyLayer` and :class:`ContinuousSpace` classes remain fully supported
12
12
  and actively developed.
@@ -607,8 +607,6 @@ class PropertyLayer:
607
607
 
608
608
  """
609
609
 
610
- propertylayer_experimental_warning_given = False
611
-
612
610
  def __init__(
613
611
  self, name: str, width: int, height: int, default_value, dtype=np.float64
614
612
  ):
@@ -653,15 +651,6 @@ class PropertyLayer:
653
651
 
654
652
  self.data = np.full((width, height), default_value, dtype=dtype)
655
653
 
656
- if not self.__class__.propertylayer_experimental_warning_given:
657
- warnings.warn(
658
- "The new PropertyLayer and _PropertyGrid classes experimental. It may be changed or removed in any and all future releases, including patch releases.\n"
659
- "We would love to hear what you think about this new feature. If you have any thoughts, share them with us here: https://github.com/projectmesa/mesa/discussions/1932",
660
- FutureWarning,
661
- stacklevel=2,
662
- )
663
- self.__class__.propertylayer_experimental_warning_given = True
664
-
665
654
  def set_cell(self, position: Coordinate, value):
666
655
  """Update a single cell's value in-place."""
667
656
  self.data[position] = value
@@ -9,12 +9,14 @@ from mesa.visualization.mpl_space_drawing import (
9
9
  draw_space,
10
10
  )
11
11
 
12
+ from .command_console import CommandConsole
12
13
  from .components import make_plot_component, make_space_component
13
14
  from .components.altair_components import make_space_altair
14
15
  from .solara_viz import JupyterViz, SolaraViz
15
16
  from .user_param import Slider
16
17
 
17
18
  __all__ = [
19
+ "CommandConsole",
18
20
  "JupyterViz",
19
21
  "Slider",
20
22
  "SolaraViz",