Mesa 3.0.0b0__py3-none-any.whl → 3.0.0b1__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.
- examples/README.md +37 -0
- examples/__init__.py +0 -0
- examples/advanced/__init__.py +0 -0
- examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +116 -0
- examples/advanced/epstein_civil_violence/Readme.md +33 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/__init__.py +0 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/agent.py +158 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/model.py +146 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/portrayal.py +33 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/server.py +81 -0
- examples/advanced/epstein_civil_violence/requirements.txt +3 -0
- examples/advanced/epstein_civil_violence/run.py +3 -0
- examples/advanced/pd_grid/analysis.ipynb +228 -0
- examples/advanced/pd_grid/pd_grid/__init__.py +0 -0
- examples/advanced/pd_grid/pd_grid/agent.py +50 -0
- examples/advanced/pd_grid/pd_grid/model.py +72 -0
- examples/advanced/pd_grid/pd_grid/portrayal.py +19 -0
- examples/advanced/pd_grid/pd_grid/server.py +21 -0
- examples/advanced/pd_grid/readme.md +42 -0
- examples/advanced/pd_grid/requirements.txt +3 -0
- examples/advanced/pd_grid/run.py +3 -0
- examples/advanced/sugarscape_g1mt/Readme.md +87 -0
- examples/advanced/sugarscape_g1mt/app.py +61 -0
- examples/advanced/sugarscape_g1mt/requirements.txt +6 -0
- examples/advanced/sugarscape_g1mt/run.py +105 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/__init__.py +0 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/model.py +180 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +26 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/server.py +61 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/sugar-map.txt +50 -0
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py +321 -0
- examples/advanced/sugarscape_g1mt/tests.py +72 -0
- examples/advanced/wolf_sheep/Readme.md +57 -0
- examples/advanced/wolf_sheep/__init__.py +0 -0
- examples/advanced/wolf_sheep/requirements.txt +1 -0
- examples/advanced/wolf_sheep/run.py +3 -0
- examples/advanced/wolf_sheep/wolf_sheep/__init__.py +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/agents.py +102 -0
- examples/advanced/wolf_sheep/wolf_sheep/model.py +136 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/sheep.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/wolf.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/server.py +78 -0
- examples/basic/__init__.py +13 -0
- examples/basic/boid_flockers/Readme.md +43 -0
- examples/basic/boid_flockers/agents.py +71 -0
- examples/basic/boid_flockers/app.py +59 -0
- examples/basic/boid_flockers/model.py +70 -0
- examples/basic/boltzmann_wealth_model/Readme.md +60 -0
- examples/basic/boltzmann_wealth_model/agents.py +31 -0
- examples/basic/boltzmann_wealth_model/app.py +66 -0
- examples/basic/boltzmann_wealth_model/model.py +44 -0
- examples/basic/boltzmann_wealth_model/st_app.py +115 -0
- examples/basic/conways_game_of_life/Readme.md +35 -0
- examples/basic/conways_game_of_life/agents.py +47 -0
- examples/basic/conways_game_of_life/model.py +32 -0
- examples/basic/conways_game_of_life/portrayal.py +18 -0
- examples/basic/conways_game_of_life/requirements.txt +1 -0
- examples/basic/conways_game_of_life/server.py +11 -0
- examples/basic/conways_game_of_life/st_app.py +71 -0
- examples/basic/schelling/README.md +47 -0
- examples/basic/schelling/agents.py +26 -0
- examples/basic/schelling/analysis.ipynb +205 -0
- examples/basic/schelling/app.py +43 -0
- examples/basic/schelling/model.py +60 -0
- examples/basic/virus_on_network/README.md +61 -0
- examples/basic/virus_on_network/agents.py +69 -0
- examples/basic/virus_on_network/app.py +133 -0
- examples/basic/virus_on_network/model.py +99 -0
- mesa/__init__.py +4 -1
- mesa/agent.py +14 -19
- mesa/examples.py +3 -0
- mesa/experimental/__init__.py +8 -2
- mesa/experimental/cell_space/cell.py +9 -0
- mesa/experimental/cell_space/discrete_space.py +7 -1
- mesa/experimental/cell_space/grid.py +13 -0
- mesa/experimental/cell_space/network.py +3 -0
- mesa/model.py +63 -12
- mesa/time.py +5 -3
- mesa/visualization/components/matplotlib.py +9 -4
- mesa/visualization/solara_viz.py +13 -58
- {mesa-3.0.0b0.dist-info → mesa-3.0.0b1.dist-info}/METADATA +1 -1
- mesa-3.0.0b1.dist-info/RECORD +114 -0
- mesa-3.0.0b0.dist-info/RECORD +0 -45
- {mesa-3.0.0b0.dist-info → mesa-3.0.0b1.dist-info}/WHEEL +0 -0
- {mesa-3.0.0b0.dist-info → mesa-3.0.0b1.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b0.dist-info → mesa-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b0.dist-info → mesa-3.0.0b1.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Sugarscape Constant Growback Model with Traders
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This is Epstein & Axtell's Sugarscape model with Traders, a detailed description is in Chapter four of
|
|
6
|
+
*Growing Artificial Societies: Social Science from the Bottom Up (1996)*. The model shows an emergent price equilibrium can happen via a decentralized dynamics.
|
|
7
|
+
|
|
8
|
+
This code generally matches the code in the Complexity Explorer Tutorial, but in `.py` instead of `.ipynb` format.
|
|
9
|
+
|
|
10
|
+
### Agents:
|
|
11
|
+
|
|
12
|
+
- **Resource**: Resource agents grow back at one unit of sugar and spice per time step up to a specified max amount and can be harvested and traded by the trader agents.
|
|
13
|
+
(if you do the interactive run, the color will be green if the resource agent has a bigger amount of sugar, or yellow if it has a bigger amount of spice)
|
|
14
|
+
- **Traders**: Trader agents have the following attributes: (1) metabolism for sugar, (2) metabolism for spice, (3) vision,
|
|
15
|
+
(4) initial sugar endowment and (5) initial spice endowment. The traverse the landscape harvesting sugar and spice and
|
|
16
|
+
trading with other agents. If they run out of sugar or spice then they are removed from the model. (red circle if you do the interactive run)
|
|
17
|
+
|
|
18
|
+
The trader agents traverse the landscape according to rule **M**:
|
|
19
|
+
- Look out as far as vision permits in the four principal lattice directions and identify the unoccupied site(s).
|
|
20
|
+
- Considering only unoccupied sites find the nearest position that produces the most welfare using the Cobb-Douglas function.
|
|
21
|
+
- Move to the new position
|
|
22
|
+
- Collect all the resources (sugar and spice) at that location
|
|
23
|
+
(Epstein and Axtell, 1996, p. 99)
|
|
24
|
+
|
|
25
|
+
The traders trade according to rule **T**:
|
|
26
|
+
- Agents and potential trade partner compute their marginal rates of substitution (MRS), if they are equal *end*.
|
|
27
|
+
- Exchange resources, with spice flowing from the agent with the higher MRS to the agent with the lower MRS and sugar
|
|
28
|
+
flowing the opposite direction.
|
|
29
|
+
- The price (p) is calculated by taking the geometric mean of the agents' MRS.
|
|
30
|
+
- If p > 1 then p units of spice are traded for 1 unit of sugar; if p < 1 then 1/p units of sugar for 1 unit of spice
|
|
31
|
+
- The trade occurs if it will (a) make both agent better off (increases MRS) and (b) does not cause the agents' MRS to
|
|
32
|
+
cross over one another otherwise *end*.
|
|
33
|
+
- This process then repeats until an *end* condition is met.
|
|
34
|
+
(Epstein and Axtell, 1996, p. 105)
|
|
35
|
+
|
|
36
|
+
The model demonstrates several Mesa concepts and features:
|
|
37
|
+
- MultiGrid
|
|
38
|
+
- Multiple agent types (traders, sugar, spice)
|
|
39
|
+
- Dynamically removing agents from the grid and schedule when they die
|
|
40
|
+
- Data Collection at the model and agent level
|
|
41
|
+
- Batchrunner (i.e. parameter sweeps)
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
To install the dependencies use pip and the requirements.txt in this directory. e.g.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
$ pip install -r requirements.txt
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## How to Run
|
|
52
|
+
|
|
53
|
+
To run the model a single instance of the model:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
$ python run.py -s
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
To run the model with BatchRunner:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
$ python run.py -b
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
To run the model interactively:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
$ mesa runserver
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
|
|
72
|
+
|
|
73
|
+
## Files
|
|
74
|
+
|
|
75
|
+
* `sugarscape_g1mt/trader_agents.py`: Defines the Trader agent class.
|
|
76
|
+
* `sugarscape_g1mt/resource_agents.py`: Defines the Resource agent class which contains an amount of sugar and spice.
|
|
77
|
+
* `sugarscape_g1mt/model.py`: Manages the Sugarscape Constant Growback with Traders model.
|
|
78
|
+
* `sugarscape_g1mt/sugar_map.txt`: Provides sugar and spice landscape in raster type format.
|
|
79
|
+
* `server.py`: Sets up an interactive visualization server.
|
|
80
|
+
* `run.py`: Runs Server, Single Run or Batch Run with data collection and basic analysis.
|
|
81
|
+
* `app.py`: Runs a visualization server via Solara (`solara run app.py`).
|
|
82
|
+
* `tests.py`: Has tests to ensure that the model reproduces the results in shown in Growing Artificial Societies.
|
|
83
|
+
|
|
84
|
+
## Additional Resources
|
|
85
|
+
|
|
86
|
+
- [Growing Artificial Societies](https://mitpress.mit.edu/9780262550253/growing-artificial-societies/)
|
|
87
|
+
- [Complexity Explorer Sugarscape with Traders Tutorial](https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import solara
|
|
3
|
+
from matplotlib.figure import Figure
|
|
4
|
+
from mesa.visualization import SolaraViz, make_plot_measure
|
|
5
|
+
from sugarscape_g1mt.model import SugarscapeG1mt
|
|
6
|
+
from sugarscape_g1mt.trader_agents import Trader
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def SpaceDrawer(model):
|
|
10
|
+
def portray(g):
|
|
11
|
+
layers = {
|
|
12
|
+
"sugar": [[np.nan for j in range(g.height)] for i in range(g.width)],
|
|
13
|
+
"spice": [[np.nan for j in range(g.height)] for i in range(g.width)],
|
|
14
|
+
"trader": {"x": [], "y": [], "c": "tab:red", "marker": "o", "s": 10},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
for content, (i, j) in g.coord_iter():
|
|
18
|
+
for agent in content:
|
|
19
|
+
if isinstance(agent, Trader):
|
|
20
|
+
layers["trader"]["x"].append(i)
|
|
21
|
+
layers["trader"]["y"].append(j)
|
|
22
|
+
else:
|
|
23
|
+
# Don't visualize resource with value <= 1.
|
|
24
|
+
layers["sugar"][i][j] = (
|
|
25
|
+
agent.sugar_amount if agent.sugar_amount > 1 else np.nan
|
|
26
|
+
)
|
|
27
|
+
layers["spice"][i][j] = (
|
|
28
|
+
agent.spice_amount if agent.spice_amount > 1 else np.nan
|
|
29
|
+
)
|
|
30
|
+
return layers
|
|
31
|
+
|
|
32
|
+
fig = Figure()
|
|
33
|
+
ax = fig.subplots()
|
|
34
|
+
out = portray(model.grid)
|
|
35
|
+
# Sugar
|
|
36
|
+
# Important note: imshow by default draws from upper left. You have to
|
|
37
|
+
# always explicitly specify origin="lower".
|
|
38
|
+
im = ax.imshow(out["sugar"], cmap="spring", origin="lower")
|
|
39
|
+
fig.colorbar(im, orientation="vertical")
|
|
40
|
+
# Spice
|
|
41
|
+
ax.imshow(out["spice"], cmap="winter", origin="lower")
|
|
42
|
+
# Trader
|
|
43
|
+
ax.scatter(**out["trader"])
|
|
44
|
+
ax.set_axis_off()
|
|
45
|
+
return solara.FigureMatplotlib(fig)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
model_params = {
|
|
49
|
+
"width": 50,
|
|
50
|
+
"height": 50,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
model1 = SugarscapeG1mt(50, 50)
|
|
54
|
+
|
|
55
|
+
page = SolaraViz(
|
|
56
|
+
model1,
|
|
57
|
+
components=[SpaceDrawer, make_plot_measure(["Trader", "Price"])],
|
|
58
|
+
name="Sugarscape {G1, M, T}",
|
|
59
|
+
play_interval=1500,
|
|
60
|
+
)
|
|
61
|
+
page # noqa
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
import mesa
|
|
5
|
+
import networkx as nx
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from sugarscape_g1mt.model import SugarscapeG1mt
|
|
8
|
+
from sugarscape_g1mt.server import server
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Analysis
|
|
12
|
+
def assess_results(results, single_agent):
|
|
13
|
+
# Make dataframe of results
|
|
14
|
+
results_df = pd.DataFrame(results)
|
|
15
|
+
# Plot and show mean price
|
|
16
|
+
plt.scatter(results_df["Step"], results_df["Price"], s=0.75)
|
|
17
|
+
plt.show()
|
|
18
|
+
|
|
19
|
+
if single_agent is not None:
|
|
20
|
+
plt.plot(results_df["Step"], results_df["Trader"])
|
|
21
|
+
plt.show()
|
|
22
|
+
else:
|
|
23
|
+
n = max(results_df["RunId"])
|
|
24
|
+
# Plot number of Traders
|
|
25
|
+
for i in range(n):
|
|
26
|
+
results_explore = results_df[results_df["RunId"] == i]
|
|
27
|
+
plt.plot(results_explore["Step"], results_explore["Trader"])
|
|
28
|
+
plt.show()
|
|
29
|
+
|
|
30
|
+
if single_agent is not None:
|
|
31
|
+
results_df = single_agent
|
|
32
|
+
|
|
33
|
+
# Show Trade Networks
|
|
34
|
+
# create graph object
|
|
35
|
+
print("Making Network")
|
|
36
|
+
G = nx.Graph()
|
|
37
|
+
trade = results_df.dropna(subset=["Trade Network"])
|
|
38
|
+
# add agent keys to make initial node set
|
|
39
|
+
G.add_nodes_from(list(trade["AgentID"].unique()))
|
|
40
|
+
|
|
41
|
+
# create edge list
|
|
42
|
+
for idx, row in trade.iterrows():
|
|
43
|
+
if len(row["Trade Network"]) > 0:
|
|
44
|
+
for agent in row["Trade Network"]:
|
|
45
|
+
G.add_edge(row["AgentID"], agent)
|
|
46
|
+
|
|
47
|
+
# Get Basic Network Statistics
|
|
48
|
+
print(f"Node Connectivity {nx.node_connectivity(G)}")
|
|
49
|
+
print(f"Average Clustering {nx.average_clustering(G)}")
|
|
50
|
+
print(f"Global Efficiency {nx.global_efficiency(G)}")
|
|
51
|
+
|
|
52
|
+
# Plot histogram of degree distribution
|
|
53
|
+
degree_sequence = sorted((d for n, d in G.degree()), reverse=True)
|
|
54
|
+
degree_sequence = [d for n, d in G.degree()]
|
|
55
|
+
plt.hist(degree_sequence)
|
|
56
|
+
plt.show()
|
|
57
|
+
|
|
58
|
+
# Plot network
|
|
59
|
+
nx.draw(G)
|
|
60
|
+
plt.show()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Run the model
|
|
64
|
+
def main():
|
|
65
|
+
args = sys.argv[1:]
|
|
66
|
+
|
|
67
|
+
if len(args) == 0:
|
|
68
|
+
server.launch()
|
|
69
|
+
|
|
70
|
+
elif args[0] == "-s":
|
|
71
|
+
print("Running Single Model")
|
|
72
|
+
model = SugarscapeG1mt()
|
|
73
|
+
model.run_model()
|
|
74
|
+
model_results = model.datacollector.get_model_vars_dataframe()
|
|
75
|
+
model_results["Step"] = model_results.index
|
|
76
|
+
agent_results = model.datacollector.get_agent_vars_dataframe()
|
|
77
|
+
agent_results = agent_results.reset_index()
|
|
78
|
+
assess_results(model_results, agent_results)
|
|
79
|
+
|
|
80
|
+
elif args[0] == "-b":
|
|
81
|
+
print("Conducting a Batch Run")
|
|
82
|
+
params = {
|
|
83
|
+
"width": 50,
|
|
84
|
+
"height": 50,
|
|
85
|
+
"vision_min": range(1, 4),
|
|
86
|
+
"metabolism_max": [2, 3, 4, 5],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
results_batch = mesa.batch_run(
|
|
90
|
+
SugarscapeG1mt,
|
|
91
|
+
parameters=params,
|
|
92
|
+
iterations=1,
|
|
93
|
+
number_processes=1,
|
|
94
|
+
data_collection_period=1,
|
|
95
|
+
display_progress=True,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assess_results(results_batch, None)
|
|
99
|
+
|
|
100
|
+
else:
|
|
101
|
+
raise Exception("Option not found")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import mesa
|
|
4
|
+
import numpy as np
|
|
5
|
+
from mesa.experimental.cell_space import OrthogonalVonNeumannGrid
|
|
6
|
+
|
|
7
|
+
from .resource_agents import Resource
|
|
8
|
+
from .trader_agents import Trader
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Helper Functions
|
|
12
|
+
def flatten(list_of_lists):
|
|
13
|
+
"""
|
|
14
|
+
helper function for model datacollector for trade price
|
|
15
|
+
collapses agent price list into one list
|
|
16
|
+
"""
|
|
17
|
+
return [item for sublist in list_of_lists for item in sublist]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def geometric_mean(list_of_prices):
|
|
21
|
+
"""
|
|
22
|
+
find the geometric mean of a list of prices
|
|
23
|
+
"""
|
|
24
|
+
return np.exp(np.log(list_of_prices).mean())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_trade(agent):
|
|
28
|
+
"""
|
|
29
|
+
For agent reporters in data collector
|
|
30
|
+
|
|
31
|
+
return list of trade partners and None for other agents
|
|
32
|
+
"""
|
|
33
|
+
if isinstance(agent, Trader):
|
|
34
|
+
return agent.trade_partners
|
|
35
|
+
else:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SugarscapeG1mt(mesa.Model):
|
|
40
|
+
"""
|
|
41
|
+
Manager class to run Sugarscape with Traders
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
width=50,
|
|
47
|
+
height=50,
|
|
48
|
+
initial_population=200,
|
|
49
|
+
endowment_min=25,
|
|
50
|
+
endowment_max=50,
|
|
51
|
+
metabolism_min=1,
|
|
52
|
+
metabolism_max=5,
|
|
53
|
+
vision_min=1,
|
|
54
|
+
vision_max=5,
|
|
55
|
+
enable_trade=True,
|
|
56
|
+
):
|
|
57
|
+
super().__init__()
|
|
58
|
+
# Initiate width and height of sugarscape
|
|
59
|
+
self.width = width
|
|
60
|
+
self.height = height
|
|
61
|
+
# Initiate population attributes
|
|
62
|
+
self.initial_population = initial_population
|
|
63
|
+
self.endowment_min = endowment_min
|
|
64
|
+
self.endowment_max = endowment_max
|
|
65
|
+
self.metabolism_min = metabolism_min
|
|
66
|
+
self.metabolism_max = metabolism_max
|
|
67
|
+
self.vision_min = vision_min
|
|
68
|
+
self.vision_max = vision_max
|
|
69
|
+
self.enable_trade = enable_trade
|
|
70
|
+
self.running = True
|
|
71
|
+
|
|
72
|
+
# initiate mesa grid class
|
|
73
|
+
self.grid = OrthogonalVonNeumannGrid((self.width, self.height), torus=False)
|
|
74
|
+
# initiate datacollector
|
|
75
|
+
self.datacollector = mesa.DataCollector(
|
|
76
|
+
model_reporters={
|
|
77
|
+
"Trader": lambda m: len(m.agents_by_type[Trader]),
|
|
78
|
+
"Trade Volume": lambda m: sum(
|
|
79
|
+
len(a.trade_partners) for a in m.agents_by_type[Trader]
|
|
80
|
+
),
|
|
81
|
+
"Price": lambda m: geometric_mean(
|
|
82
|
+
flatten([a.prices for a in m.agents_by_type[Trader]])
|
|
83
|
+
),
|
|
84
|
+
},
|
|
85
|
+
agent_reporters={"Trade Network": lambda a: get_trade(a)},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# read in landscape file from supplmentary material
|
|
89
|
+
sugar_distribution = np.genfromtxt(Path(__file__).parent / "sugar-map.txt")
|
|
90
|
+
spice_distribution = np.flip(sugar_distribution, 1)
|
|
91
|
+
|
|
92
|
+
for cell in self.grid.all_cells:
|
|
93
|
+
max_sugar = sugar_distribution[cell.coordinate]
|
|
94
|
+
max_spice = spice_distribution[cell.coordinate]
|
|
95
|
+
Resource(self, max_sugar, max_spice, cell)
|
|
96
|
+
|
|
97
|
+
for _ in range(self.initial_population):
|
|
98
|
+
# get agent position
|
|
99
|
+
x = self.random.randrange(self.width)
|
|
100
|
+
y = self.random.randrange(self.height)
|
|
101
|
+
# see Growing Artificial Societies p. 108 for initialization
|
|
102
|
+
# give agents initial endowment
|
|
103
|
+
sugar = int(self.random.uniform(self.endowment_min, self.endowment_max + 1))
|
|
104
|
+
spice = int(self.random.uniform(self.endowment_min, self.endowment_max + 1))
|
|
105
|
+
# give agents initial metabolism
|
|
106
|
+
metabolism_sugar = int(
|
|
107
|
+
self.random.uniform(self.metabolism_min, self.metabolism_max + 1)
|
|
108
|
+
)
|
|
109
|
+
metabolism_spice = int(
|
|
110
|
+
self.random.uniform(self.metabolism_min, self.metabolism_max + 1)
|
|
111
|
+
)
|
|
112
|
+
# give agents vision
|
|
113
|
+
vision = int(self.random.uniform(self.vision_min, self.vision_max + 1))
|
|
114
|
+
|
|
115
|
+
cell = self.grid[(x, y)]
|
|
116
|
+
# create Trader object
|
|
117
|
+
Trader(
|
|
118
|
+
self,
|
|
119
|
+
cell,
|
|
120
|
+
sugar=sugar,
|
|
121
|
+
spice=spice,
|
|
122
|
+
metabolism_sugar=metabolism_sugar,
|
|
123
|
+
metabolism_spice=metabolism_spice,
|
|
124
|
+
vision=vision,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def step(self):
|
|
128
|
+
"""
|
|
129
|
+
Unique step function that does staged activation of sugar and spice
|
|
130
|
+
and then randomly activates traders
|
|
131
|
+
"""
|
|
132
|
+
# step Resource agents
|
|
133
|
+
self.agents_by_type[Resource].do("step")
|
|
134
|
+
|
|
135
|
+
# step trader agents
|
|
136
|
+
# to account for agent death and removal we need a separate data structure to
|
|
137
|
+
# iterate
|
|
138
|
+
trader_shuffle = self.agents_by_type[Trader].shuffle()
|
|
139
|
+
|
|
140
|
+
for agent in trader_shuffle:
|
|
141
|
+
agent.prices = []
|
|
142
|
+
agent.trade_partners = []
|
|
143
|
+
agent.move()
|
|
144
|
+
agent.eat()
|
|
145
|
+
agent.maybe_die()
|
|
146
|
+
|
|
147
|
+
if not self.enable_trade:
|
|
148
|
+
# If trade is not enabled, return early
|
|
149
|
+
self.datacollector.collect(self)
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
trader_shuffle = self.agents_by_type[Trader].shuffle()
|
|
153
|
+
|
|
154
|
+
for agent in trader_shuffle:
|
|
155
|
+
agent.trade_with_neighbors()
|
|
156
|
+
|
|
157
|
+
# collect model level data
|
|
158
|
+
self.datacollector.collect(self)
|
|
159
|
+
"""
|
|
160
|
+
Mesa is working on updating datacollector agent reporter
|
|
161
|
+
so it can collect information on specific agents from
|
|
162
|
+
mesa.time.RandomActivationByType.
|
|
163
|
+
|
|
164
|
+
Please see issue #1419 at
|
|
165
|
+
https://github.com/projectmesa/mesa/issues/1419
|
|
166
|
+
(contributions welcome)
|
|
167
|
+
|
|
168
|
+
Below is one way to update agent_records to get specific Trader agent data
|
|
169
|
+
"""
|
|
170
|
+
# Need to remove excess data
|
|
171
|
+
# Create local variable to store trade data
|
|
172
|
+
agent_trades = self.datacollector._agent_records[self.steps]
|
|
173
|
+
# Get rid of all None to reduce data storage needs
|
|
174
|
+
agent_trades = [agent for agent in agent_trades if agent[2] is not None]
|
|
175
|
+
# Reassign the dictionary value with lean trade data
|
|
176
|
+
self.datacollector._agent_records[self.steps] = agent_trades
|
|
177
|
+
|
|
178
|
+
def run_model(self, step_count=1000):
|
|
179
|
+
for i in range(step_count):
|
|
180
|
+
self.step()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from mesa.experimental.cell_space import FixedAgent
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Resource(FixedAgent):
|
|
5
|
+
"""
|
|
6
|
+
Resource:
|
|
7
|
+
- contains an amount of sugar and spice
|
|
8
|
+
- grows 1 amount of sugar at each turn
|
|
9
|
+
- grows 1 amount of spice at each turn
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, model, max_sugar, max_spice, cell):
|
|
13
|
+
super().__init__(model)
|
|
14
|
+
self.sugar_amount = max_sugar
|
|
15
|
+
self.max_sugar = max_sugar
|
|
16
|
+
self.spice_amount = max_spice
|
|
17
|
+
self.max_spice = max_spice
|
|
18
|
+
self.cell = cell
|
|
19
|
+
|
|
20
|
+
def step(self):
|
|
21
|
+
"""
|
|
22
|
+
Growth function, adds one unit of sugar and spice each step up to
|
|
23
|
+
max amount
|
|
24
|
+
"""
|
|
25
|
+
self.sugar_amount = min([self.max_sugar, self.sugar_amount + 1])
|
|
26
|
+
self.spice_amount = min([self.max_spice, self.spice_amount + 1])
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import mesa
|
|
2
|
+
|
|
3
|
+
from .model import SugarscapeG1mt
|
|
4
|
+
from .resource_agents import Resource
|
|
5
|
+
from .trader_agents import Trader
|
|
6
|
+
|
|
7
|
+
sugar_dic = {4: "#005C00", 3: "#008300", 2: "#00AA00", 1: "#00F800"}
|
|
8
|
+
spice_dic = {4: "#acac00", 3: "#c5c500", 2: "#dfdf00", 1: "#f8f800"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def Agent_portrayal(agent):
|
|
12
|
+
if agent is None:
|
|
13
|
+
return
|
|
14
|
+
|
|
15
|
+
if isinstance(agent, Trader):
|
|
16
|
+
return {
|
|
17
|
+
"Shape": "circle",
|
|
18
|
+
"Filled": "true",
|
|
19
|
+
"r": 0.5,
|
|
20
|
+
"Layer": 0,
|
|
21
|
+
"Color": "#FF0A01",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
elif isinstance(agent, Resource):
|
|
25
|
+
resource_type = "sugar" if agent.max_sugar > agent.max_spice else "spice"
|
|
26
|
+
if resource_type == "sugar":
|
|
27
|
+
color = (
|
|
28
|
+
sugar_dic[agent.sugar_amount] if agent.sugar_amount != 0 else "#D6F5D6"
|
|
29
|
+
)
|
|
30
|
+
layer = 1 if agent.sugar_amount > 2 else 0
|
|
31
|
+
else:
|
|
32
|
+
color = (
|
|
33
|
+
spice_dic[agent.spice_amount] if agent.spice_amount != 0 else "#D6F5D6"
|
|
34
|
+
)
|
|
35
|
+
layer = 1 if agent.spice_amount > 2 else 0
|
|
36
|
+
return {
|
|
37
|
+
"Color": color,
|
|
38
|
+
"Shape": "rect",
|
|
39
|
+
"Filled": "true",
|
|
40
|
+
"Layer": layer,
|
|
41
|
+
"w": 1,
|
|
42
|
+
"h": 1,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
canvas_element = mesa.visualization.CanvasGrid(Agent_portrayal, 50, 50, 500, 500)
|
|
49
|
+
chart_element = mesa.visualization.ChartModule(
|
|
50
|
+
[{"Label": "Trader", "Color": "#AA0000"}]
|
|
51
|
+
)
|
|
52
|
+
chart_element2 = mesa.visualization.ChartModule(
|
|
53
|
+
[{"Label": "Price", "Color": "#000000"}]
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
server = mesa.visualization.ModularServer(
|
|
57
|
+
SugarscapeG1mt,
|
|
58
|
+
[canvas_element, chart_element, chart_element2],
|
|
59
|
+
"Sugarscape with Traders",
|
|
60
|
+
)
|
|
61
|
+
# server.launch()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2
|
|
2
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2
|
|
3
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2
|
|
4
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2
|
|
5
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2
|
|
6
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2
|
|
7
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2
|
|
8
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2
|
|
9
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3
|
|
10
|
+
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
|
|
11
|
+
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
|
|
12
|
+
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
|
|
13
|
+
0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3
|
|
14
|
+
0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3
|
|
15
|
+
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2
|
|
16
|
+
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2
|
|
17
|
+
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2
|
|
18
|
+
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2
|
|
19
|
+
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2
|
|
20
|
+
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2
|
|
21
|
+
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2
|
|
22
|
+
1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2
|
|
23
|
+
1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1
|
|
24
|
+
1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1
|
|
25
|
+
1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1
|
|
26
|
+
1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1
|
|
27
|
+
1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
|
|
28
|
+
2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1
|
|
29
|
+
2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1
|
|
30
|
+
2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1
|
|
31
|
+
2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0
|
|
32
|
+
2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
|
|
33
|
+
2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0
|
|
34
|
+
2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
|
|
35
|
+
2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
|
|
36
|
+
2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
|
|
37
|
+
2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
|
38
|
+
2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
|
|
39
|
+
2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0
|
|
40
|
+
2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
|
|
41
|
+
2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
42
|
+
2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
43
|
+
2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
44
|
+
1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
45
|
+
1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
46
|
+
1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
47
|
+
1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
48
|
+
1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
49
|
+
1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
50
|
+
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|