Mesa 3.0.0a5__py3-none-any.whl → 3.0.0b0__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.
- mesa/__init__.py +1 -1
- mesa/agent.py +12 -26
- mesa/batchrunner.py +7 -0
- mesa/experimental/cell_space/__init__.py +7 -1
- mesa/experimental/cell_space/cell.py +26 -6
- mesa/experimental/cell_space/cell_agent.py +114 -23
- mesa/experimental/cell_space/discrete_space.py +63 -2
- mesa/experimental/devs/examples/wolf_sheep.py +2 -1
- mesa/model.py +8 -9
- mesa/time.py +2 -2
- mesa/visualization/components/matplotlib.py +176 -87
- mesa/visualization/solara_viz.py +12 -3
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b0.dist-info}/METADATA +55 -13
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b0.dist-info}/RECORD +18 -17
- mesa-3.0.0b0.dist-info/licenses/LICENSE +202 -0
- mesa-3.0.0a5.dist-info/licenses/LICENSE → mesa-3.0.0b0.dist-info/licenses/NOTICE +2 -2
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b0.dist-info}/WHEEL +0 -0
- {mesa-3.0.0a5.dist-info → mesa-3.0.0b0.dist-info}/entry_points.txt +0 -0
mesa/__init__.py
CHANGED
|
@@ -23,7 +23,7 @@ __all__ = [
|
|
|
23
23
|
]
|
|
24
24
|
|
|
25
25
|
__title__ = "mesa"
|
|
26
|
-
__version__ = "3.0.
|
|
26
|
+
__version__ = "3.0.0b0"
|
|
27
27
|
__license__ = "Apache 2.0"
|
|
28
28
|
_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
29
29
|
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
mesa/agent.py
CHANGED
|
@@ -46,39 +46,25 @@ class Agent:
|
|
|
46
46
|
# so, unique_id is unique relative to a model, and counting starts from 1
|
|
47
47
|
_ids = defaultdict(functools.partial(itertools.count, 1))
|
|
48
48
|
|
|
49
|
-
def __init__(self, *args, **kwargs) -> None:
|
|
49
|
+
def __init__(self, model: Model, *args, **kwargs) -> None:
|
|
50
50
|
"""Create a new agent.
|
|
51
51
|
|
|
52
52
|
Args:
|
|
53
53
|
model (Model): The model instance in which the agent exists.
|
|
54
|
-
args:
|
|
55
|
-
kwargs:
|
|
56
|
-
"""
|
|
57
|
-
# TODO: Cleanup in future Mesa version (3.1+)
|
|
58
|
-
match args:
|
|
59
|
-
# Case 1: Only the model is provided. The new correct behavior.
|
|
60
|
-
case [model]:
|
|
61
|
-
self.model = model
|
|
62
|
-
self.unique_id = next(self._ids[model])
|
|
63
|
-
# Case 2: Both unique_id and model are provided, deprecated
|
|
64
|
-
case [_, model]:
|
|
65
|
-
warnings.warn(
|
|
66
|
-
"unique ids are assigned automatically to Agents in Mesa 3. The use of custom unique_id is "
|
|
67
|
-
"deprecated. Only input a model when calling `super()__init__(model)`. The unique_id inputted is not used.",
|
|
68
|
-
DeprecationWarning,
|
|
69
|
-
stacklevel=2,
|
|
70
|
-
)
|
|
71
|
-
self.model = model
|
|
72
|
-
self.unique_id = next(self._ids[model])
|
|
73
|
-
# Case 3: Anything else, raise an error
|
|
74
|
-
case _:
|
|
75
|
-
raise ValueError(
|
|
76
|
-
"Invalid arguments provided to initialize the Agent. Only input a model: `super()__init__(model)`."
|
|
77
|
-
)
|
|
54
|
+
args: passed on to super
|
|
55
|
+
kwargs: passed on to super
|
|
78
56
|
|
|
79
|
-
|
|
57
|
+
Notes:
|
|
58
|
+
to make proper use of python's super, in each class remove the arguments and
|
|
59
|
+
keyword arguments you need and pass on the rest to super
|
|
80
60
|
|
|
61
|
+
"""
|
|
62
|
+
super().__init__(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
self.model: Model = model
|
|
81
65
|
self.model.register_agent(self)
|
|
66
|
+
self.unique_id: int = next(self._ids[model])
|
|
67
|
+
self.pos: Position | None = None
|
|
82
68
|
|
|
83
69
|
def remove(self) -> None:
|
|
84
70
|
"""Remove and delete the agent from the model."""
|
mesa/batchrunner.py
CHANGED
|
@@ -38,6 +38,9 @@ def batch_run(
|
|
|
38
38
|
Returns:
|
|
39
39
|
List[Dict[str, Any]]
|
|
40
40
|
|
|
41
|
+
Notes:
|
|
42
|
+
batch_run assumes the model has a `datacollector` attribute that has a DataCollector object initialized.
|
|
43
|
+
|
|
41
44
|
"""
|
|
42
45
|
runs_list = []
|
|
43
46
|
run_id = 0
|
|
@@ -173,6 +176,10 @@ def _collect_data(
|
|
|
173
176
|
step: int,
|
|
174
177
|
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
|
|
175
178
|
"""Collect model and agent data from a model using mesas datacollector."""
|
|
179
|
+
if not hasattr(model, "datacollector"):
|
|
180
|
+
raise AttributeError(
|
|
181
|
+
"The model does not have a datacollector attribute. Please add a DataCollector to your model."
|
|
182
|
+
)
|
|
176
183
|
dc = model.datacollector
|
|
177
184
|
|
|
178
185
|
model_data = {param: values[step] for param, values in dc.model_vars.items()}
|
|
@@ -6,7 +6,11 @@ expressive that the default grids available in `mesa.space`.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from mesa.experimental.cell_space.cell import Cell
|
|
9
|
-
from mesa.experimental.cell_space.cell_agent import
|
|
9
|
+
from mesa.experimental.cell_space.cell_agent import (
|
|
10
|
+
CellAgent,
|
|
11
|
+
FixedAgent,
|
|
12
|
+
Grid2DMovingAgent,
|
|
13
|
+
)
|
|
10
14
|
from mesa.experimental.cell_space.cell_collection import CellCollection
|
|
11
15
|
from mesa.experimental.cell_space.discrete_space import DiscreteSpace
|
|
12
16
|
from mesa.experimental.cell_space.grid import (
|
|
@@ -22,6 +26,8 @@ __all__ = [
|
|
|
22
26
|
"CellCollection",
|
|
23
27
|
"Cell",
|
|
24
28
|
"CellAgent",
|
|
29
|
+
"Grid2DMovingAgent",
|
|
30
|
+
"FixedAgent",
|
|
25
31
|
"DiscreteSpace",
|
|
26
32
|
"Grid",
|
|
27
33
|
"HexGrid",
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from functools import cache, cached_property
|
|
6
7
|
from random import Random
|
|
7
|
-
from typing import TYPE_CHECKING
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
8
9
|
|
|
10
|
+
from mesa.experimental.cell_space.cell_agent import CellAgent
|
|
9
11
|
from mesa.experimental.cell_space.cell_collection import CellCollection
|
|
12
|
+
from mesa.space import PropertyLayer
|
|
10
13
|
|
|
11
14
|
if TYPE_CHECKING:
|
|
12
15
|
from mesa.agent import Agent
|
|
13
|
-
from mesa.experimental.cell_space.cell_agent import CellAgent
|
|
14
16
|
|
|
15
17
|
Coordinate = tuple[int, ...]
|
|
16
18
|
|
|
@@ -34,6 +36,7 @@ class Cell:
|
|
|
34
36
|
"capacity",
|
|
35
37
|
"properties",
|
|
36
38
|
"random",
|
|
39
|
+
"_mesa_property_layers",
|
|
37
40
|
"__dict__",
|
|
38
41
|
]
|
|
39
42
|
|
|
@@ -66,9 +69,10 @@ class Cell:
|
|
|
66
69
|
self.agents: list[
|
|
67
70
|
Agent
|
|
68
71
|
] = [] # TODO:: change to AgentSet or weakrefs? (neither is very performant, )
|
|
69
|
-
self.capacity: int = capacity
|
|
72
|
+
self.capacity: int | None = capacity
|
|
70
73
|
self.properties: dict[Coordinate, object] = {}
|
|
71
74
|
self.random = random
|
|
75
|
+
self._mesa_property_layers: dict[str, PropertyLayer] = {}
|
|
72
76
|
|
|
73
77
|
def connect(self, other: Cell, key: Coordinate | None = None) -> None:
|
|
74
78
|
"""Connects this cell to another cell.
|
|
@@ -117,7 +121,6 @@ class Cell:
|
|
|
117
121
|
|
|
118
122
|
"""
|
|
119
123
|
self.agents.remove(agent)
|
|
120
|
-
agent.cell = None
|
|
121
124
|
|
|
122
125
|
@property
|
|
123
126
|
def is_empty(self) -> bool:
|
|
@@ -133,7 +136,7 @@ class Cell:
|
|
|
133
136
|
return f"Cell({self.coordinate}, {self.agents})"
|
|
134
137
|
|
|
135
138
|
@cached_property
|
|
136
|
-
def neighborhood(self) -> CellCollection:
|
|
139
|
+
def neighborhood(self) -> CellCollection[Cell]:
|
|
137
140
|
"""Returns the direct neighborhood of the cell.
|
|
138
141
|
|
|
139
142
|
This is equivalent to cell.get_neighborhood(radius=1)
|
|
@@ -145,7 +148,7 @@ class Cell:
|
|
|
145
148
|
@cache # noqa: B019
|
|
146
149
|
def get_neighborhood(
|
|
147
150
|
self, radius: int = 1, include_center: bool = False
|
|
148
|
-
) -> CellCollection:
|
|
151
|
+
) -> CellCollection[Cell]:
|
|
149
152
|
"""Returns a list of all neighboring cells for the given radius.
|
|
150
153
|
|
|
151
154
|
For getting the direct neighborhood (i.e., radius=1) you can also use
|
|
@@ -191,3 +194,20 @@ class Cell:
|
|
|
191
194
|
if not include_center:
|
|
192
195
|
neighborhood.pop(self, None)
|
|
193
196
|
return neighborhood
|
|
197
|
+
|
|
198
|
+
# PropertyLayer methods
|
|
199
|
+
def get_property(self, property_name: str) -> Any:
|
|
200
|
+
"""Get the value of a property."""
|
|
201
|
+
return self._mesa_property_layers[property_name].data[self.coordinate]
|
|
202
|
+
|
|
203
|
+
def set_property(self, property_name: str, value: Any):
|
|
204
|
+
"""Set the value of a property."""
|
|
205
|
+
self._mesa_property_layers[property_name].set_cell(self.coordinate, value)
|
|
206
|
+
|
|
207
|
+
def modify_property(
|
|
208
|
+
self, property_name: str, operation: Callable, value: Any = None
|
|
209
|
+
):
|
|
210
|
+
"""Modify the value of a property."""
|
|
211
|
+
self._mesa_property_layers[property_name].modify_cell(
|
|
212
|
+
self.coordinate, operation, value
|
|
213
|
+
)
|
|
@@ -2,41 +2,132 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING, Protocol
|
|
6
6
|
|
|
7
|
-
from mesa import Agent
|
|
7
|
+
from mesa.agent import Agent
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
-
from mesa.experimental.cell_space
|
|
10
|
+
from mesa.experimental.cell_space import Cell
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
class
|
|
14
|
-
"""
|
|
13
|
+
class HasCellProtocol(Protocol):
|
|
14
|
+
"""Protocol for discrete space cell holders."""
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
unique_id (int): A unique identifier for this agent.
|
|
18
|
-
model (Model): The model instance to which the agent belongs
|
|
19
|
-
pos: (Position | None): The position of the agent in the space
|
|
20
|
-
cell: (Cell | None): the cell which the agent occupies
|
|
21
|
-
"""
|
|
16
|
+
cell: Cell
|
|
22
17
|
|
|
23
|
-
def __init__(self, model: Model) -> None:
|
|
24
|
-
"""Create a new agent.
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
super().__init__(model)
|
|
30
|
-
self.cell: Cell | None = None
|
|
19
|
+
class HasCell:
|
|
20
|
+
"""Descriptor for cell movement behavior."""
|
|
31
21
|
|
|
32
|
-
|
|
33
|
-
"""Move agent to cell.
|
|
22
|
+
_mesa_cell: Cell | None = None
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
@property
|
|
25
|
+
def cell(self) -> Cell | None: # noqa: D102
|
|
26
|
+
return self._mesa_cell
|
|
37
27
|
|
|
38
|
-
|
|
28
|
+
@cell.setter
|
|
29
|
+
def cell(self, cell: Cell | None) -> None:
|
|
30
|
+
# remove from current cell
|
|
39
31
|
if self.cell is not None:
|
|
40
32
|
self.cell.remove_agent(self)
|
|
33
|
+
|
|
34
|
+
# update private attribute
|
|
35
|
+
self._mesa_cell = cell
|
|
36
|
+
|
|
37
|
+
# add to new cell
|
|
38
|
+
if cell is not None:
|
|
39
|
+
cell.add_agent(self)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BasicMovement:
|
|
43
|
+
"""Mixin for moving agents in discrete space."""
|
|
44
|
+
|
|
45
|
+
def move_to(self: HasCellProtocol, cell: Cell) -> None:
|
|
46
|
+
"""Move to a new cell."""
|
|
41
47
|
self.cell = cell
|
|
48
|
+
|
|
49
|
+
def move_relative(self: HasCellProtocol, direction: tuple[int, ...]):
|
|
50
|
+
"""Move to a cell relative to the current cell.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
direction: The direction to move in.
|
|
54
|
+
"""
|
|
55
|
+
new_cell = self.cell.connections.get(direction)
|
|
56
|
+
if new_cell is not None:
|
|
57
|
+
self.cell = new_cell
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"No cell in direction {direction}")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class FixedCell(HasCell):
|
|
63
|
+
"""Mixin for agents that are fixed to a cell."""
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def cell(self) -> Cell | None: # noqa: D102
|
|
67
|
+
return self._mesa_cell
|
|
68
|
+
|
|
69
|
+
@cell.setter
|
|
70
|
+
def cell(self, cell: Cell) -> None:
|
|
71
|
+
if self.cell is not None:
|
|
72
|
+
raise ValueError("Cannot move agent in FixedCell")
|
|
73
|
+
self._mesa_cell = cell
|
|
74
|
+
|
|
42
75
|
cell.add_agent(self)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CellAgent(Agent, HasCell, BasicMovement):
|
|
79
|
+
"""Cell Agent is an extension of the Agent class and adds behavior for moving in discrete spaces.
|
|
80
|
+
|
|
81
|
+
Attributes:
|
|
82
|
+
cell (Cell): The cell the agent is currently in.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def remove(self):
|
|
86
|
+
"""Remove the agent from the model."""
|
|
87
|
+
super().remove()
|
|
88
|
+
self.cell = None # ensures that we are also removed from cell
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class FixedAgent(Agent, FixedCell):
|
|
92
|
+
"""A patch in a 2D grid."""
|
|
93
|
+
|
|
94
|
+
def remove(self):
|
|
95
|
+
"""Remove the agent from the model."""
|
|
96
|
+
super().remove()
|
|
97
|
+
|
|
98
|
+
# fixme we leave self._mesa_cell on the original value
|
|
99
|
+
# so you cannot hijack remove() to move patches
|
|
100
|
+
self.cell.remove_agent(self)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Grid2DMovingAgent(CellAgent):
|
|
104
|
+
"""Mixin for moving agents in 2D grids."""
|
|
105
|
+
|
|
106
|
+
# fmt: off
|
|
107
|
+
DIRECTION_MAP = {
|
|
108
|
+
"n": (-1, 0), "north": (-1, 0), "up": (-1, 0),
|
|
109
|
+
"s": (1, 0), "south": (1, 0), "down": (1, 0),
|
|
110
|
+
"e": (0, 1), "east": (0, 1), "right": (0, 1),
|
|
111
|
+
"w": (0, -1), "west": (0, -1), "left": (0, -1),
|
|
112
|
+
"ne": (-1, 1), "northeast": (-1, 1), "upright": (-1, 1),
|
|
113
|
+
"nw": (-1, -1), "northwest": (-1, -1), "upleft": (-1, -1),
|
|
114
|
+
"se": (1, 1), "southeast": (1, 1), "downright": (1, 1),
|
|
115
|
+
"sw": (1, -1), "southwest": (1, -1), "downleft": (1, -1)
|
|
116
|
+
}
|
|
117
|
+
# fmt: on
|
|
118
|
+
|
|
119
|
+
def move(self, direction: str, distance: int = 1):
|
|
120
|
+
"""Move the agent in a cardinal direction.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
direction: The cardinal direction to move in.
|
|
124
|
+
distance: The distance to move.
|
|
125
|
+
"""
|
|
126
|
+
direction = direction.lower() # Convert direction to lowercase
|
|
127
|
+
|
|
128
|
+
if direction not in self.DIRECTION_MAP:
|
|
129
|
+
raise ValueError(f"Invalid direction: {direction}")
|
|
130
|
+
|
|
131
|
+
move_vector = self.DIRECTION_MAP[direction]
|
|
132
|
+
for _ in range(distance):
|
|
133
|
+
self.move_relative(move_vector)
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from functools import cached_property
|
|
6
7
|
from random import Random
|
|
7
|
-
from typing import Generic, TypeVar
|
|
8
|
+
from typing import Any, Generic, TypeVar
|
|
8
9
|
|
|
9
10
|
from mesa.experimental.cell_space.cell import Cell
|
|
10
11
|
from mesa.experimental.cell_space.cell_collection import CellCollection
|
|
12
|
+
from mesa.space import PropertyLayer
|
|
11
13
|
|
|
12
14
|
T = TypeVar("T", bound=Cell)
|
|
13
15
|
|
|
@@ -21,7 +23,7 @@ class DiscreteSpace(Generic[T]):
|
|
|
21
23
|
random (Random): The random number generator
|
|
22
24
|
cell_klass (Type) : the type of cell class
|
|
23
25
|
empties (CellCollection) : collecction of all cells that are empty
|
|
24
|
-
|
|
26
|
+
property_layers (dict[str, PropertyLayer]): the property layers of the discrete space
|
|
25
27
|
"""
|
|
26
28
|
|
|
27
29
|
def __init__(
|
|
@@ -47,6 +49,7 @@ class DiscreteSpace(Generic[T]):
|
|
|
47
49
|
|
|
48
50
|
self._empties: dict[tuple[int, ...], None] = {}
|
|
49
51
|
self._empties_initialized = False
|
|
52
|
+
self.property_layers: dict[str, PropertyLayer] = {}
|
|
50
53
|
|
|
51
54
|
@property
|
|
52
55
|
def cutoff_empties(self): # noqa
|
|
@@ -73,3 +76,61 @@ class DiscreteSpace(Generic[T]):
|
|
|
73
76
|
def select_random_empty_cell(self) -> T:
|
|
74
77
|
"""Select random empty cell."""
|
|
75
78
|
return self.random.choice(list(self.empties))
|
|
79
|
+
|
|
80
|
+
# PropertyLayer methods
|
|
81
|
+
def add_property_layer(
|
|
82
|
+
self, property_layer: PropertyLayer, add_to_cells: bool = True
|
|
83
|
+
):
|
|
84
|
+
"""Add a property layer to the grid.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
property_layer: the property layer to add
|
|
88
|
+
add_to_cells: whether to add the property layer to all cells (default: True)
|
|
89
|
+
"""
|
|
90
|
+
if property_layer.name in self.property_layers:
|
|
91
|
+
raise ValueError(f"Property layer {property_layer.name} already exists.")
|
|
92
|
+
self.property_layers[property_layer.name] = property_layer
|
|
93
|
+
if add_to_cells:
|
|
94
|
+
for cell in self._cells.values():
|
|
95
|
+
cell._mesa_property_layers[property_layer.name] = property_layer
|
|
96
|
+
|
|
97
|
+
def remove_property_layer(self, property_name: str, remove_from_cells: bool = True):
|
|
98
|
+
"""Remove a property layer from the grid.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
property_name: the name of the property layer to remove
|
|
102
|
+
remove_from_cells: whether to remove the property layer from all cells (default: True)
|
|
103
|
+
"""
|
|
104
|
+
del self.property_layers[property_name]
|
|
105
|
+
if remove_from_cells:
|
|
106
|
+
for cell in self._cells.values():
|
|
107
|
+
del cell._mesa_property_layers[property_name]
|
|
108
|
+
|
|
109
|
+
def set_property(
|
|
110
|
+
self, property_name: str, value, condition: Callable[[T], bool] | None = None
|
|
111
|
+
):
|
|
112
|
+
"""Set the value of a property for all cells in the grid.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
property_name: the name of the property to set
|
|
116
|
+
value: the value to set
|
|
117
|
+
condition: a function that takes a cell and returns a boolean
|
|
118
|
+
"""
|
|
119
|
+
self.property_layers[property_name].set_cells(value, condition)
|
|
120
|
+
|
|
121
|
+
def modify_properties(
|
|
122
|
+
self,
|
|
123
|
+
property_name: str,
|
|
124
|
+
operation: Callable,
|
|
125
|
+
value: Any = None,
|
|
126
|
+
condition: Callable[[T], bool] | None = None,
|
|
127
|
+
):
|
|
128
|
+
"""Modify the values of a specific property for all cells in the grid.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
property_name: the name of the property to modify
|
|
132
|
+
operation: the operation to perform
|
|
133
|
+
value: the value to use in the operation
|
|
134
|
+
condition: a function that takes a cell and returns a boolean (used to filter cells)
|
|
135
|
+
"""
|
|
136
|
+
self.property_layers[property_name].modify_cells(operation, value, condition)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Example of using ABM simulator for Wolf-Sheep Predation Model."""
|
|
2
2
|
|
|
3
3
|
import mesa
|
|
4
|
+
from mesa.experimental.cell_space import FixedAgent
|
|
4
5
|
from mesa.experimental.devs.simulator import ABMSimulator
|
|
5
6
|
|
|
6
7
|
|
|
@@ -90,7 +91,7 @@ class Wolf(Animal):
|
|
|
90
91
|
sheep_to_eat.die()
|
|
91
92
|
|
|
92
93
|
|
|
93
|
-
class GrassPatch(
|
|
94
|
+
class GrassPatch(FixedAgent):
|
|
94
95
|
"""A patch of grass that grows at a fixed rate and it is eaten by sheep."""
|
|
95
96
|
|
|
96
97
|
@property
|
mesa/model.py
CHANGED
|
@@ -48,8 +48,8 @@ class Model:
|
|
|
48
48
|
seed: the seed for the random number generator
|
|
49
49
|
kwargs: keyword arguments to pass onto super
|
|
50
50
|
"""
|
|
51
|
+
super().__init__(*args, **kwargs)
|
|
51
52
|
self.running = True
|
|
52
|
-
self.schedule = None
|
|
53
53
|
self.steps: int = 0
|
|
54
54
|
|
|
55
55
|
self._setup_agent_registration()
|
|
@@ -210,14 +210,13 @@ class Model:
|
|
|
210
210
|
tables: tables to collect
|
|
211
211
|
|
|
212
212
|
"""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
213
|
+
warnings.warn(
|
|
214
|
+
"initialize_data_collector() is deprecated. Please use the DataCollector class directly. "
|
|
215
|
+
"by using `self.datacollector = DataCollector(...)`.",
|
|
216
|
+
DeprecationWarning,
|
|
217
|
+
stacklevel=2,
|
|
218
|
+
)
|
|
219
|
+
|
|
221
220
|
self.datacollector = DataCollector(
|
|
222
221
|
model_reporters=model_reporters,
|
|
223
222
|
agent_reporters=agent_reporters,
|
mesa/time.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
.. warning::
|
|
4
4
|
The time module and all its Schedulers are deprecated and will be removed in a future version.
|
|
5
5
|
They can be replaced with AgentSet functionality. See the migration guide for details:
|
|
6
|
-
https://mesa.readthedocs.io/
|
|
6
|
+
https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers
|
|
7
7
|
|
|
8
8
|
Objects for handling the time component of a model. In particular, this module
|
|
9
9
|
contains Schedulers, which handle agent activation. A Scheduler is an object
|
|
@@ -65,7 +65,7 @@ class BaseScheduler:
|
|
|
65
65
|
warnings.warn(
|
|
66
66
|
"The time module and all its Schedulers are deprecated and will be removed in a future version. "
|
|
67
67
|
"They can be replaced with AgentSet functionality. See the migration guide for details. "
|
|
68
|
-
"https://mesa.readthedocs.io/
|
|
68
|
+
"https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers",
|
|
69
69
|
DeprecationWarning,
|
|
70
70
|
stacklevel=2,
|
|
71
71
|
)
|
|
@@ -1,116 +1,190 @@
|
|
|
1
1
|
"""Matplotlib based solara components for visualization MESA spaces and plots."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import warnings
|
|
4
4
|
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
5
6
|
import networkx as nx
|
|
7
|
+
import numpy as np
|
|
6
8
|
import solara
|
|
9
|
+
from matplotlib.cm import ScalarMappable
|
|
10
|
+
from matplotlib.colors import LinearSegmentedColormap, Normalize, to_rgba
|
|
7
11
|
from matplotlib.figure import Figure
|
|
8
|
-
from matplotlib.ticker import MaxNLocator
|
|
9
12
|
|
|
10
13
|
import mesa
|
|
11
14
|
from mesa.experimental.cell_space import VoronoiGrid
|
|
15
|
+
from mesa.space import PropertyLayer
|
|
12
16
|
from mesa.visualization.utils import update_counter
|
|
13
17
|
|
|
14
18
|
|
|
15
|
-
def make_space_matplotlib(agent_portrayal=None):
|
|
19
|
+
def make_space_matplotlib(agent_portrayal=None, propertylayer_portrayal=None):
|
|
20
|
+
"""Create a Matplotlib-based space visualization component.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
agent_portrayal (function): Function to portray agents
|
|
24
|
+
propertylayer_portrayal (dict): Dictionary of PropertyLayer portrayal specifications
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
function: A function that creates a SpaceMatplotlib component
|
|
28
|
+
"""
|
|
16
29
|
if agent_portrayal is None:
|
|
17
30
|
|
|
18
31
|
def agent_portrayal(a):
|
|
19
32
|
return {"id": a.unique_id}
|
|
20
33
|
|
|
21
34
|
def MakeSpaceMatplotlib(model):
|
|
22
|
-
return SpaceMatplotlib(model, agent_portrayal)
|
|
35
|
+
return SpaceMatplotlib(model, agent_portrayal, propertylayer_portrayal)
|
|
23
36
|
|
|
24
37
|
return MakeSpaceMatplotlib
|
|
25
38
|
|
|
26
39
|
|
|
27
40
|
@solara.component
|
|
28
|
-
def SpaceMatplotlib(
|
|
41
|
+
def SpaceMatplotlib(
|
|
42
|
+
model,
|
|
43
|
+
agent_portrayal,
|
|
44
|
+
propertylayer_portrayal,
|
|
45
|
+
dependencies: list[any] | None = None,
|
|
46
|
+
):
|
|
47
|
+
"""Create a Matplotlib-based space visualization component."""
|
|
29
48
|
update_counter.get()
|
|
30
49
|
space_fig = Figure()
|
|
31
50
|
space_ax = space_fig.subplots()
|
|
32
51
|
space = getattr(model, "grid", None)
|
|
33
52
|
if space is None:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if isinstance(space, mesa.space.
|
|
37
|
-
|
|
53
|
+
space = getattr(model, "space", None)
|
|
54
|
+
|
|
55
|
+
if isinstance(space, mesa.space._Grid):
|
|
56
|
+
_draw_grid(space, space_ax, agent_portrayal, propertylayer_portrayal, model)
|
|
38
57
|
elif isinstance(space, mesa.space.ContinuousSpace):
|
|
39
|
-
_draw_continuous_space(space, space_ax, agent_portrayal)
|
|
58
|
+
_draw_continuous_space(space, space_ax, agent_portrayal, model)
|
|
59
|
+
elif isinstance(space, mesa.space.NetworkGrid):
|
|
60
|
+
_draw_network_grid(space, space_ax, agent_portrayal)
|
|
40
61
|
elif isinstance(space, VoronoiGrid):
|
|
41
62
|
_draw_voronoi(space, space_ax, agent_portrayal)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
solara.FigureMatplotlib(space_fig, format="png", dependencies=dependencies)
|
|
45
|
-
|
|
63
|
+
elif space is None and propertylayer_portrayal:
|
|
64
|
+
draw_property_layers(space_ax, space, propertylayer_portrayal, model)
|
|
46
65
|
|
|
47
|
-
|
|
48
|
-
def _split_and_scatter(portray_data, space_ax):
|
|
49
|
-
grouped_data = defaultdict(lambda: {"x": [], "y": [], "s": [], "c": []})
|
|
50
|
-
|
|
51
|
-
# Extract data from the dictionary
|
|
52
|
-
x = portray_data["x"]
|
|
53
|
-
y = portray_data["y"]
|
|
54
|
-
s = portray_data["s"]
|
|
55
|
-
c = portray_data["c"]
|
|
56
|
-
m = portray_data["m"]
|
|
57
|
-
|
|
58
|
-
if not (len(x) == len(y) == len(s) == len(c) == len(m)):
|
|
59
|
-
raise ValueError(
|
|
60
|
-
"Length mismatch in portrayal data lists: "
|
|
61
|
-
f"x: {len(x)}, y: {len(y)}, size: {len(s)}, "
|
|
62
|
-
f"color: {len(c)}, marker: {len(m)}"
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
# Group the data by marker
|
|
66
|
-
for i in range(len(x)):
|
|
67
|
-
marker = m[i]
|
|
68
|
-
grouped_data[marker]["x"].append(x[i])
|
|
69
|
-
grouped_data[marker]["y"].append(y[i])
|
|
70
|
-
grouped_data[marker]["s"].append(s[i])
|
|
71
|
-
grouped_data[marker]["c"].append(c[i])
|
|
66
|
+
solara.FigureMatplotlib(space_fig, format="png", dependencies=dependencies)
|
|
72
67
|
|
|
73
|
-
# Plot each group with the same marker
|
|
74
|
-
for marker, data in grouped_data.items():
|
|
75
|
-
space_ax.scatter(data["x"], data["y"], s=data["s"], c=data["c"], marker=marker)
|
|
76
68
|
|
|
69
|
+
def draw_property_layers(ax, space, propertylayer_portrayal, model):
|
|
70
|
+
"""Draw PropertyLayers on the given axes.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
ax (matplotlib.axes.Axes): The axes to draw on.
|
|
74
|
+
space (mesa.space._Grid): The space containing the PropertyLayers.
|
|
75
|
+
propertylayer_portrayal (dict): Dictionary of PropertyLayer portrayal specifications.
|
|
76
|
+
model (mesa.Model): The model instance.
|
|
77
|
+
"""
|
|
78
|
+
for layer_name, portrayal in propertylayer_portrayal.items():
|
|
79
|
+
layer = getattr(model, layer_name, None)
|
|
80
|
+
if not isinstance(layer, PropertyLayer):
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
data = layer.data.astype(float) if layer.data.dtype == bool else layer.data
|
|
84
|
+
width, height = data.shape if space is None else (space.width, space.height)
|
|
85
|
+
|
|
86
|
+
if space and data.shape != (width, height):
|
|
87
|
+
warnings.warn(
|
|
88
|
+
f"Layer {layer_name} dimensions ({data.shape}) do not match space dimensions ({width}, {height}).",
|
|
89
|
+
UserWarning,
|
|
90
|
+
stacklevel=2,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Get portrayal properties, or use defaults
|
|
94
|
+
alpha = portrayal.get("alpha", 1)
|
|
95
|
+
vmin = portrayal.get("vmin", np.min(data))
|
|
96
|
+
vmax = portrayal.get("vmax", np.max(data))
|
|
97
|
+
colorbar = portrayal.get("colorbar", True)
|
|
98
|
+
|
|
99
|
+
# Draw the layer
|
|
100
|
+
if "color" in portrayal:
|
|
101
|
+
rgba_color = to_rgba(portrayal["color"])
|
|
102
|
+
normalized_data = (data - vmin) / (vmax - vmin)
|
|
103
|
+
rgba_data = np.full((*data.shape, 4), rgba_color)
|
|
104
|
+
rgba_data[..., 3] *= normalized_data * alpha
|
|
105
|
+
rgba_data = np.clip(rgba_data, 0, 1)
|
|
106
|
+
cmap = LinearSegmentedColormap.from_list(
|
|
107
|
+
layer_name, [(0, 0, 0, 0), (*rgba_color[:3], alpha)]
|
|
108
|
+
)
|
|
109
|
+
im = ax.imshow(
|
|
110
|
+
rgba_data.transpose(1, 0, 2),
|
|
111
|
+
extent=(0, width, 0, height),
|
|
112
|
+
origin="lower",
|
|
113
|
+
)
|
|
114
|
+
if colorbar:
|
|
115
|
+
norm = Normalize(vmin=vmin, vmax=vmax)
|
|
116
|
+
sm = ScalarMappable(norm=norm, cmap=cmap)
|
|
117
|
+
sm.set_array([])
|
|
118
|
+
ax.figure.colorbar(sm, ax=ax, orientation="vertical")
|
|
119
|
+
|
|
120
|
+
elif "colormap" in portrayal:
|
|
121
|
+
cmap = portrayal.get("colormap", "viridis")
|
|
122
|
+
if isinstance(cmap, list):
|
|
123
|
+
cmap = LinearSegmentedColormap.from_list(layer_name, cmap)
|
|
124
|
+
im = ax.imshow(
|
|
125
|
+
data.T,
|
|
126
|
+
cmap=cmap,
|
|
127
|
+
alpha=alpha,
|
|
128
|
+
vmin=vmin,
|
|
129
|
+
vmax=vmax,
|
|
130
|
+
extent=(0, width, 0, height),
|
|
131
|
+
origin="lower",
|
|
132
|
+
)
|
|
133
|
+
if colorbar:
|
|
134
|
+
plt.colorbar(im, ax=ax, label=layer_name)
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"PropertyLayer {layer_name} portrayal must include 'color' or 'colormap'."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _draw_grid(space, space_ax, agent_portrayal, propertylayer_portrayal, model):
|
|
142
|
+
if propertylayer_portrayal:
|
|
143
|
+
draw_property_layers(space_ax, space, propertylayer_portrayal, model)
|
|
144
|
+
|
|
145
|
+
agent_data = _get_agent_data(space, agent_portrayal)
|
|
146
|
+
|
|
147
|
+
space_ax.set_xlim(0, space.width)
|
|
148
|
+
space_ax.set_ylim(0, space.height)
|
|
149
|
+
_split_and_scatter(agent_data, space_ax)
|
|
150
|
+
|
|
151
|
+
# Draw grid lines
|
|
152
|
+
for x in range(space.width + 1):
|
|
153
|
+
space_ax.axvline(x, color="gray", linestyle=":")
|
|
154
|
+
for y in range(space.height + 1):
|
|
155
|
+
space_ax.axhline(y, color="gray", linestyle=":")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _get_agent_data(space, agent_portrayal):
|
|
159
|
+
"""Helper function to get agent data for visualization."""
|
|
160
|
+
x, y, s, c, m = [], [], [], [], []
|
|
161
|
+
for agents, pos in space.coord_iter():
|
|
162
|
+
if not agents:
|
|
163
|
+
continue
|
|
164
|
+
if not isinstance(agents, list):
|
|
165
|
+
agents = [agents] # noqa PLW2901
|
|
166
|
+
for agent in agents:
|
|
167
|
+
data = agent_portrayal(agent)
|
|
168
|
+
x.append(pos[0] + 0.5) # Center the agent in the cell
|
|
169
|
+
y.append(pos[1] + 0.5) # Center the agent in the cell
|
|
170
|
+
default_size = (180 / max(space.width, space.height)) ** 2
|
|
171
|
+
s.append(data.get("size", default_size))
|
|
172
|
+
c.append(data.get("color", "tab:blue"))
|
|
173
|
+
m.append(data.get("shape", "o"))
|
|
174
|
+
return {"x": x, "y": y, "s": s, "c": c, "m": m}
|
|
77
175
|
|
|
78
|
-
def _draw_grid(space, space_ax, agent_portrayal):
|
|
79
|
-
def portray(g):
|
|
80
|
-
x = []
|
|
81
|
-
y = []
|
|
82
|
-
s = [] # size
|
|
83
|
-
c = [] # color
|
|
84
|
-
m = [] # shape
|
|
85
|
-
for i in range(g.width):
|
|
86
|
-
for j in range(g.height):
|
|
87
|
-
content = g._grid[i][j]
|
|
88
|
-
if not content:
|
|
89
|
-
continue
|
|
90
|
-
if not hasattr(content, "__iter__"):
|
|
91
|
-
# Is a single grid
|
|
92
|
-
content = [content]
|
|
93
|
-
for agent in content:
|
|
94
|
-
data = agent_portrayal(agent)
|
|
95
|
-
x.append(i)
|
|
96
|
-
y.append(j)
|
|
97
|
-
|
|
98
|
-
# This is the default value for the marker size, which auto-scales
|
|
99
|
-
# according to the grid area.
|
|
100
|
-
default_size = (180 / max(g.width, g.height)) ** 2
|
|
101
|
-
# establishing a default prevents misalignment if some agents are not given size, color, etc.
|
|
102
|
-
size = data.get("size", default_size)
|
|
103
|
-
s.append(size)
|
|
104
|
-
color = data.get("color", "tab:blue")
|
|
105
|
-
c.append(color)
|
|
106
|
-
mark = data.get("shape", "o")
|
|
107
|
-
m.append(mark)
|
|
108
|
-
out = {"x": x, "y": y, "s": s, "c": c, "m": m}
|
|
109
|
-
return out
|
|
110
176
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
177
|
+
def _split_and_scatter(portray_data, space_ax):
|
|
178
|
+
"""Helper function to split and scatter agent data."""
|
|
179
|
+
for marker in set(portray_data["m"]):
|
|
180
|
+
mask = [m == marker for m in portray_data["m"]]
|
|
181
|
+
space_ax.scatter(
|
|
182
|
+
[x for x, show in zip(portray_data["x"], mask) if show],
|
|
183
|
+
[y for y, show in zip(portray_data["y"], mask) if show],
|
|
184
|
+
s=[s for s, show in zip(portray_data["s"], mask) if show],
|
|
185
|
+
c=[c for c, show in zip(portray_data["c"], mask) if show],
|
|
186
|
+
marker=marker,
|
|
187
|
+
)
|
|
114
188
|
|
|
115
189
|
|
|
116
190
|
def _draw_network_grid(space, space_ax, agent_portrayal):
|
|
@@ -124,7 +198,7 @@ def _draw_network_grid(space, space_ax, agent_portrayal):
|
|
|
124
198
|
)
|
|
125
199
|
|
|
126
200
|
|
|
127
|
-
def _draw_continuous_space(space, space_ax, agent_portrayal):
|
|
201
|
+
def _draw_continuous_space(space, space_ax, agent_portrayal, model):
|
|
128
202
|
def portray(space):
|
|
129
203
|
x = []
|
|
130
204
|
y = []
|
|
@@ -139,15 +213,13 @@ def _draw_continuous_space(space, space_ax, agent_portrayal):
|
|
|
139
213
|
|
|
140
214
|
# This is matplotlib's default marker size
|
|
141
215
|
default_size = 20
|
|
142
|
-
# establishing a default prevents misalignment if some agents are not given size, color, etc.
|
|
143
216
|
size = data.get("size", default_size)
|
|
144
217
|
s.append(size)
|
|
145
218
|
color = data.get("color", "tab:blue")
|
|
146
219
|
c.append(color)
|
|
147
220
|
mark = data.get("shape", "o")
|
|
148
221
|
m.append(mark)
|
|
149
|
-
|
|
150
|
-
return out
|
|
222
|
+
return {"x": x, "y": y, "s": s, "c": c, "m": m}
|
|
151
223
|
|
|
152
224
|
# Determine border style based on space.torus
|
|
153
225
|
border_style = "solid" if not space.torus else (0, (5, 10))
|
|
@@ -186,8 +258,6 @@ def _draw_voronoi(space, space_ax, agent_portrayal):
|
|
|
186
258
|
if "color" in data:
|
|
187
259
|
c.append(data["color"])
|
|
188
260
|
out = {"x": x, "y": y}
|
|
189
|
-
# This is the default value for the marker size, which auto-scales
|
|
190
|
-
# according to the grid area.
|
|
191
261
|
out["s"] = s
|
|
192
262
|
if len(c) > 0:
|
|
193
263
|
out["c"] = c
|
|
@@ -216,10 +286,19 @@ def _draw_voronoi(space, space_ax, agent_portrayal):
|
|
|
216
286
|
alpha=min(1, cell.properties[space.cell_coloring_property]),
|
|
217
287
|
c="red",
|
|
218
288
|
) # Plot filled polygon
|
|
219
|
-
space_ax.plot(*zip(*polygon), color="black") # Plot polygon edges in
|
|
289
|
+
space_ax.plot(*zip(*polygon), color="black") # Plot polygon edges in black
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def make_plot_measure(measure: str | dict[str, str] | list[str] | tuple[str]):
|
|
293
|
+
"""Create a plotting function for a specified measure.
|
|
220
294
|
|
|
295
|
+
Args:
|
|
296
|
+
measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
|
|
297
|
+
|
|
298
|
+
Returns:
|
|
299
|
+
function: A function that creates a PlotMatplotlib component.
|
|
300
|
+
"""
|
|
221
301
|
|
|
222
|
-
def make_plot_measure(measure: str | dict[str, str] | list[str] | tuple[str]): # noqa: D103
|
|
223
302
|
def MakePlotMeasure(model):
|
|
224
303
|
return PlotMatplotlib(model, measure)
|
|
225
304
|
|
|
@@ -227,7 +306,17 @@ def make_plot_measure(measure: str | dict[str, str] | list[str] | tuple[str]):
|
|
|
227
306
|
|
|
228
307
|
|
|
229
308
|
@solara.component
|
|
230
|
-
def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
|
|
309
|
+
def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
|
|
310
|
+
"""Create a Matplotlib-based plot for a measure or measures.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
model (mesa.Model): The model instance.
|
|
314
|
+
measure (str | dict[str, str] | list[str] | tuple[str]): Measure(s) to plot.
|
|
315
|
+
dependencies (list[any] | None): Optional dependencies for the plot.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
solara.FigureMatplotlib: A component for rendering the plot.
|
|
319
|
+
"""
|
|
231
320
|
update_counter.get()
|
|
232
321
|
fig = Figure()
|
|
233
322
|
ax = fig.subplots()
|
|
@@ -244,5 +333,5 @@ def PlotMatplotlib(model, measure, dependencies: list[any] | None = None): # no
|
|
|
244
333
|
ax.plot(df.loc[:, m], label=m)
|
|
245
334
|
fig.legend()
|
|
246
335
|
# Set integer x axis
|
|
247
|
-
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
|
336
|
+
ax.xaxis.set_major_locator(plt.MaxNLocator(integer=True))
|
|
248
337
|
solara.FigureMatplotlib(fig, dependencies=dependencies)
|
mesa/visualization/solara_viz.py
CHANGED
|
@@ -225,6 +225,7 @@ def ModelController(model: solara.Reactive[Model], play_interval=100):
|
|
|
225
225
|
play_interval (int, optional): Interval for playing the model steps in milliseconds.
|
|
226
226
|
"""
|
|
227
227
|
playing = solara.use_reactive(False)
|
|
228
|
+
running = solara.use_reactive(True)
|
|
228
229
|
original_model = solara.use_reactive(None)
|
|
229
230
|
|
|
230
231
|
def save_initial_model():
|
|
@@ -236,19 +237,23 @@ def ModelController(model: solara.Reactive[Model], play_interval=100):
|
|
|
236
237
|
solara.use_effect(save_initial_model, [model.value])
|
|
237
238
|
|
|
238
239
|
async def step():
|
|
239
|
-
while playing.value:
|
|
240
|
+
while playing.value and running.value:
|
|
240
241
|
await asyncio.sleep(play_interval / 1000)
|
|
241
242
|
do_step()
|
|
242
243
|
|
|
243
|
-
solara.lab.use_task(
|
|
244
|
+
solara.lab.use_task(
|
|
245
|
+
step, dependencies=[playing.value, running.value], prefer_threaded=False
|
|
246
|
+
)
|
|
244
247
|
|
|
245
248
|
def do_step():
|
|
246
249
|
"""Advance the model by one step."""
|
|
247
250
|
model.value.step()
|
|
251
|
+
running.value = model.value.running
|
|
248
252
|
|
|
249
253
|
def do_reset():
|
|
250
254
|
"""Reset the model to its initial state."""
|
|
251
255
|
playing.value = False
|
|
256
|
+
running.value = True
|
|
252
257
|
model.value = copy.deepcopy(original_model.value)
|
|
253
258
|
|
|
254
259
|
def do_play_pause():
|
|
@@ -261,9 +266,13 @@ def ModelController(model: solara.Reactive[Model], play_interval=100):
|
|
|
261
266
|
label="▶" if not playing.value else "❚❚",
|
|
262
267
|
color="primary",
|
|
263
268
|
on_click=do_play_pause,
|
|
269
|
+
disabled=not running.value,
|
|
264
270
|
)
|
|
265
271
|
solara.Button(
|
|
266
|
-
label="Step",
|
|
272
|
+
label="Step",
|
|
273
|
+
color="primary",
|
|
274
|
+
on_click=do_step,
|
|
275
|
+
disabled=playing.value or not running.value,
|
|
267
276
|
)
|
|
268
277
|
|
|
269
278
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: Mesa
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b0
|
|
4
4
|
Summary: Agent-based modeling (ABM) in Python
|
|
5
5
|
Project-URL: homepage, https://github.com/projectmesa/mesa
|
|
6
6
|
Project-URL: repository, https://github.com/projectmesa/mesa
|
|
7
7
|
Author-email: Project Mesa Team <projectmesa@googlegroups.com>
|
|
8
8
|
License: Apache 2.0
|
|
9
9
|
License-File: LICENSE
|
|
10
|
+
License-File: NOTICE
|
|
10
11
|
Keywords: ABM,agent,based,model,modeling,multi-agent,simulation
|
|
11
12
|
Classifier: Development Status :: 3 - Alpha
|
|
12
13
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -22,30 +23,60 @@ Classifier: Topic :: Scientific/Engineering
|
|
|
22
23
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
24
|
Classifier: Topic :: Scientific/Engineering :: Artificial Life
|
|
24
25
|
Requires-Python: >=3.10
|
|
25
|
-
Requires-Dist: click
|
|
26
|
-
Requires-Dist: cookiecutter
|
|
27
|
-
Requires-Dist: matplotlib
|
|
28
|
-
Requires-Dist: networkx
|
|
29
26
|
Requires-Dist: numpy
|
|
30
27
|
Requires-Dist: pandas
|
|
31
|
-
Requires-Dist: solara
|
|
32
28
|
Requires-Dist: tqdm
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: cookiecutter; extra == 'all'
|
|
31
|
+
Requires-Dist: ipython; extra == 'all'
|
|
32
|
+
Requires-Dist: matplotlib; extra == 'all'
|
|
33
|
+
Requires-Dist: myst-nb; extra == 'all'
|
|
34
|
+
Requires-Dist: myst-parser; extra == 'all'
|
|
35
|
+
Requires-Dist: networkx; extra == 'all'
|
|
36
|
+
Requires-Dist: pydata-sphinx-theme; extra == 'all'
|
|
37
|
+
Requires-Dist: pytest; extra == 'all'
|
|
38
|
+
Requires-Dist: pytest-cov; extra == 'all'
|
|
39
|
+
Requires-Dist: pytest-mock; extra == 'all'
|
|
40
|
+
Requires-Dist: ruff; extra == 'all'
|
|
41
|
+
Requires-Dist: scipy; extra == 'all'
|
|
42
|
+
Requires-Dist: seaborn; extra == 'all'
|
|
43
|
+
Requires-Dist: solara; extra == 'all'
|
|
44
|
+
Requires-Dist: sphinx; extra == 'all'
|
|
33
45
|
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: cookiecutter; extra == 'dev'
|
|
47
|
+
Requires-Dist: matplotlib; extra == 'dev'
|
|
48
|
+
Requires-Dist: networkx; extra == 'dev'
|
|
49
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
34
50
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
35
51
|
Requires-Dist: pytest-mock; extra == 'dev'
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
52
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
53
|
+
Requires-Dist: solara; extra == 'dev'
|
|
38
54
|
Requires-Dist: sphinx; extra == 'dev'
|
|
39
55
|
Provides-Extra: docs
|
|
40
56
|
Requires-Dist: ipython; extra == 'docs'
|
|
57
|
+
Requires-Dist: matplotlib; extra == 'docs'
|
|
41
58
|
Requires-Dist: myst-nb; extra == 'docs'
|
|
42
59
|
Requires-Dist: myst-parser; extra == 'docs'
|
|
60
|
+
Requires-Dist: networkx; extra == 'docs'
|
|
43
61
|
Requires-Dist: pydata-sphinx-theme; extra == 'docs'
|
|
44
62
|
Requires-Dist: seaborn; extra == 'docs'
|
|
63
|
+
Requires-Dist: solara; extra == 'docs'
|
|
45
64
|
Requires-Dist: sphinx; extra == 'docs'
|
|
46
65
|
Provides-Extra: examples
|
|
47
|
-
Requires-Dist:
|
|
66
|
+
Requires-Dist: matplotlib; extra == 'examples'
|
|
67
|
+
Requires-Dist: networkx; extra == 'examples'
|
|
68
|
+
Requires-Dist: pytest; extra == 'examples'
|
|
48
69
|
Requires-Dist: scipy; extra == 'examples'
|
|
70
|
+
Requires-Dist: solara; extra == 'examples'
|
|
71
|
+
Provides-Extra: network
|
|
72
|
+
Requires-Dist: networkx; extra == 'network'
|
|
73
|
+
Provides-Extra: rec
|
|
74
|
+
Requires-Dist: matplotlib; extra == 'rec'
|
|
75
|
+
Requires-Dist: networkx; extra == 'rec'
|
|
76
|
+
Requires-Dist: solara; extra == 'rec'
|
|
77
|
+
Provides-Extra: viz
|
|
78
|
+
Requires-Dist: matplotlib; extra == 'viz'
|
|
79
|
+
Requires-Dist: solara; extra == 'viz'
|
|
49
80
|
Description-Content-Type: text/markdown
|
|
50
81
|
|
|
51
82
|
# Mesa: Agent-based modeling in Python
|
|
@@ -78,7 +109,7 @@ can be displayed in browser windows or Jupyter.*
|
|
|
78
109
|
|
|
79
110
|
## Using Mesa
|
|
80
111
|
|
|
81
|
-
To install our latest stable release (2.
|
|
112
|
+
To install our latest stable release (2.4.x), run:
|
|
82
113
|
|
|
83
114
|
``` bash
|
|
84
115
|
pip install -U mesa
|
|
@@ -89,6 +120,17 @@ To install our latest pre-release (3.0.0 alpha), run:
|
|
|
89
120
|
``` bash
|
|
90
121
|
pip install -U --pre mesa
|
|
91
122
|
```
|
|
123
|
+
With Mesa 3.0, we don't install all our dependencies anymore by default.
|
|
124
|
+
```bash
|
|
125
|
+
# You can customize the additional dependencies you need, if you want. Available are:
|
|
126
|
+
pip install -U --pre mesa[network,viz]
|
|
127
|
+
|
|
128
|
+
# This is equivalent to our recommended dependencies:
|
|
129
|
+
pip install -U --pre mesa[rec]
|
|
130
|
+
|
|
131
|
+
# To install all, including developer, dependencies:
|
|
132
|
+
pip install -U --pre mesa[all]
|
|
133
|
+
```
|
|
92
134
|
|
|
93
135
|
You can also use `pip` to install the latest GitHub version:
|
|
94
136
|
|
|
@@ -107,13 +149,13 @@ For resources or help on using Mesa, check out the following:
|
|
|
107
149
|
|
|
108
150
|
- [Intro to Mesa Tutorial](http://mesa.readthedocs.org/en/stable/tutorials/intro_tutorial.html) (An introductory model, the Boltzmann
|
|
109
151
|
Wealth Model, for beginners or those new to Mesa.)
|
|
110
|
-
- [Visualization Tutorial](https://mesa.readthedocs.io/
|
|
152
|
+
- [Visualization Tutorial](https://mesa.readthedocs.io/stable/tutorials/visualization_tutorial.html) (An introduction into our Solara visualization)
|
|
111
153
|
- [Complexity Explorer Tutorial](https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa) (An advanced-beginner model,
|
|
112
154
|
SugarScape with Traders, with instructional videos)
|
|
113
|
-
- [Mesa Examples](https://github.com/projectmesa/mesa-examples
|
|
155
|
+
- [Mesa Examples](https://github.com/projectmesa/mesa-examples) (A repository of seminal ABMs using Mesa and
|
|
114
156
|
examples of employing specific Mesa Features)
|
|
115
157
|
- [Docs](http://mesa.readthedocs.org/) (Mesa's documentation, API and useful snippets)
|
|
116
|
-
- [Development version docs](https://mesa.readthedocs.io/
|
|
158
|
+
- [Development version docs](https://mesa.readthedocs.io/latest/) (the latest version docs if you're using a pre-release Mesa version)
|
|
117
159
|
- [Discussions](https://github.com/projectmesa/mesa/discussions) (GitHub threaded discussions about Mesa)
|
|
118
160
|
- [Matrix Chat](https://matrix.to/#/#project-mesa:matrix.org) (Chat Forum via Matrix to talk about Mesa)
|
|
119
161
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
mesa/__init__.py,sha256=
|
|
2
|
-
mesa/agent.py,sha256
|
|
3
|
-
mesa/batchrunner.py,sha256=
|
|
1
|
+
mesa/__init__.py,sha256=GM836SFDxNMX8ePHbGQt5Yi4G3jiYLyOb3f-AUDLmaQ,618
|
|
2
|
+
mesa/agent.py,sha256=XeBMwbq7Ruhbt6xj9PPEDuDMtO185Bff9dOFagUxiq8,24254
|
|
3
|
+
mesa/batchrunner.py,sha256=0AqTcvjWNPp1aqn7zuUKSovx6Rnkk4M-KouCZ4Guqy0,6419
|
|
4
4
|
mesa/datacollection.py,sha256=xyb07aBpd-HSDh5bk-XcVqGiDu5bfaLlxj5eDlGIwqY,16138
|
|
5
5
|
mesa/main.py,sha256=_KgeVGbi0znzezjjoM09vhGdyaqcuDEwb9M7vH2c_O4,1668
|
|
6
|
-
mesa/model.py,sha256=
|
|
6
|
+
mesa/model.py,sha256=zMs5_ZlLity7hYcrwhz1BY1ysK3y3aQa9hf1KIoxryM,7845
|
|
7
7
|
mesa/space.py,sha256=1sVl78o5lYP6aEg32QIb9-tcv3V3UeFdC7A_h_8CgO8,62838
|
|
8
|
-
mesa/time.py,sha256=
|
|
8
|
+
mesa/time.py,sha256=FaVqRwWWdFH6iagfXrdmCN7YSvRpDmikyzfz6GJ8yU4,14977
|
|
9
9
|
mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
|
|
10
10
|
mesa/cookiecutter-mesa/hooks/post_gen_project.py,sha256=UKz12l6mKc7fILK0MvV5djsTKwkmD4DlH8LYjFO8ehI,316
|
|
11
11
|
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md,sha256=Yji4lGY-NtQSnW-oBj0_Jhs-XhCfZA8R1mBBM_IllGs,80
|
|
@@ -16,11 +16,11 @@ mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytem
|
|
|
16
16
|
mesa/experimental/UserParam.py,sha256=f32nmFjroe76HpxU75ZCEOqFW2nAsDfmqIf8kQ1zt-E,2086
|
|
17
17
|
mesa/experimental/__init__.py,sha256=1hxkjcZvdJhhQx4iLlTTUAPs_SqRyN-us4qR8U6JKkw,209
|
|
18
18
|
mesa/experimental/solara_viz.py,sha256=uWrNQAX3oEWSftmyjNorN839dBCUp86hnhpL704dyGQ,15212
|
|
19
|
-
mesa/experimental/cell_space/__init__.py,sha256
|
|
20
|
-
mesa/experimental/cell_space/cell.py,sha256=
|
|
21
|
-
mesa/experimental/cell_space/cell_agent.py,sha256=
|
|
19
|
+
mesa/experimental/cell_space/__init__.py,sha256=-NtSCT7mA-aszLSAdqbICGqeFe2vBdb-GrcW562legY,999
|
|
20
|
+
mesa/experimental/cell_space/cell.py,sha256=lR1lz9hMlfKsDoRzSPCqb0XTk2VIEFqxIiN_zwUaUUo,6738
|
|
21
|
+
mesa/experimental/cell_space/cell_agent.py,sha256=jvYOV9OIaBaqAsAG0YLV64X_f3BJe_wP7qfos_RXi0Y,3759
|
|
22
22
|
mesa/experimental/cell_space/cell_collection.py,sha256=ZcyuPEevCZEXW7jFnX6StjBMw4UBDQvUspZRcFi2dFg,3426
|
|
23
|
-
mesa/experimental/cell_space/discrete_space.py,sha256=
|
|
23
|
+
mesa/experimental/cell_space/discrete_space.py,sha256=bl8jmYnOLFaF5_0Wh7BFQetXr53cS-SNLzHTjtkOnVY,4845
|
|
24
24
|
mesa/experimental/cell_space/grid.py,sha256=WT9AtQCgPzV8VonX0oSPRXgZNXoOR_mVgEAEOp5I4YA,7319
|
|
25
25
|
mesa/experimental/cell_space/network.py,sha256=hzhxGipRyM1PWOQFlPz--tc3pA_RRBT8Xq4pXHx5sD8,1252
|
|
26
26
|
mesa/experimental/cell_space/voronoi.py,sha256=lSY8zQhELvOy0RfDyZIek09UMwY9_20UY9SPqFWsNoM,10014
|
|
@@ -30,15 +30,16 @@ mesa/experimental/devs/__init__.py,sha256=EByaC66ikUIu9G9p1geLm6ESEMWZOPTO9r9627
|
|
|
30
30
|
mesa/experimental/devs/eventlist.py,sha256=Trvc5S-NG5B792uuk_cY8Q_5Rw99zioUYDQXcXWVuCo,5972
|
|
31
31
|
mesa/experimental/devs/simulator.py,sha256=wvqkLIDgbJNaem9nwMacyEYRp0W3ai5Oxptw3-QmbSw,10595
|
|
32
32
|
mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=E8YSV3O5ihKsntGtnltHM-4IyS8eg2DSRUqmIiw_1iU,10916
|
|
33
|
-
mesa/experimental/devs/examples/wolf_sheep.py,sha256=
|
|
33
|
+
mesa/experimental/devs/examples/wolf_sheep.py,sha256=1eb1CfYNQoprqSJat-LPYPvwWH1ENQdj39viEqwSk0s,8103
|
|
34
34
|
mesa/visualization/UserParam.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
|
|
35
35
|
mesa/visualization/__init__.py,sha256=sa8lqeLcDtte19SMzFiKP6K4CrVLxAPwrhDu_AsDWTs,395
|
|
36
|
-
mesa/visualization/solara_viz.py,sha256=
|
|
36
|
+
mesa/visualization/solara_viz.py,sha256=6Gq4r4-XFt9AG6G-v2Gvz_W-ykTC4gP0OQnqtYIhAxw,16717
|
|
37
37
|
mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
|
|
38
38
|
mesa/visualization/components/altair.py,sha256=E-iblqpWhx72qrjkNz4Ie9c66Hh1OFpLVjuDIg9m2sA,2804
|
|
39
|
-
mesa/visualization/components/matplotlib.py,sha256=
|
|
40
|
-
mesa-3.0.
|
|
41
|
-
mesa-3.0.
|
|
42
|
-
mesa-3.0.
|
|
43
|
-
mesa-3.0.
|
|
44
|
-
mesa-3.0.
|
|
39
|
+
mesa/visualization/components/matplotlib.py,sha256=6ttFOxfNj3YdNu7c1NB0YP6-F7Chy0Qq0kkMe02TF8c,11697
|
|
40
|
+
mesa-3.0.0b0.dist-info/METADATA,sha256=iqC2Ea-eu6tKAs5lwnnHS7Zoimp6ifPO8cJBb4o65UQ,9937
|
|
41
|
+
mesa-3.0.0b0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
42
|
+
mesa-3.0.0b0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
43
|
+
mesa-3.0.0b0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
44
|
+
mesa-3.0.0b0.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
|
|
45
|
+
mesa-3.0.0b0.dist-info/RECORD,,
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Copyright
|
|
1
|
+
Copyright 2014-2024 Core Mesa Team and contributors
|
|
2
2
|
|
|
3
3
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
you may not use this file except in compliance with the License.
|
|
5
5
|
You may obtain a copy of the License at
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
8
8
|
|
|
9
9
|
Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
File without changes
|
|
File without changes
|