Skip to content

Commit 3b5b416

Browse files
rhttpike3
authored andcommitted
Sugarscape {G1,{M,T}}: Add tests
1 parent 63326dc commit 3b5b416

File tree

3 files changed

+81
-1
lines changed

3 files changed

+81
-1
lines changed

examples/sugarscape_g1mt/Readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Summary
44

55
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)
6+
*Growing Artificial Societies: Social Science from the Bottom Up.* (1996) The model shows an emergent price equilibrium can happen via a decentralized dynamics.
77

88
This code generally matches the code in the Complexity Explorer Tutorial, but in `.py` instead of `.ipynb` format.
99

@@ -83,6 +83,7 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
8383
* ``server.py``: Sets up and launches and interactive visualization server.
8484
* ``run.py``: Runs Server, Single Run or Batch Run with data collection and basic analysis.
8585
* `app.py`: Runs a visualization server via Solara (`solara run app.py`).
86+
* `tests.py`: Has tests to ensure that the model reproduces the results in shown in Growing Artificial Societies.
8687

8788
## Additional Resources
8889

examples/sugarscape_g1mt/sugarscape_g1mt/model.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def __init__(
4949
metabolism_max=5,
5050
vision_min=1,
5151
vision_max=5,
52+
enable_trade=True,
5253
):
5354
# Initiate width and heigh of sugarscape
5455
self.width = width
@@ -61,6 +62,7 @@ def __init__(
6162
self.metabolism_max = metabolism_max
6263
self.vision_min = vision_min
6364
self.vision_max = vision_max
65+
self.enable_trade = enable_trade
6466
self.running = True
6567

6668
# initiate activation schedule
@@ -175,6 +177,11 @@ def step(self):
175177
agent.eat()
176178
agent.maybe_die()
177179

180+
if not self.enable_trade:
181+
# If trade is not enabled, return early
182+
self.datacollector.collect(self)
183+
return
184+
178185
trader_shuffle = self.randomize_traders()
179186

180187
for agent in trader_shuffle:

examples/sugarscape_g1mt/tests.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import random
2+
3+
import numpy as np
4+
from scipy import stats
5+
from sugarscape_g1mt.model import SugarscapeG1mt, flatten
6+
from sugarscape_g1mt.trader_agents import Trader
7+
8+
random.seed(1)
9+
10+
11+
def check_slope(y, increasing):
12+
x = range(len(y))
13+
slope, intercept, _, p_value, _ = stats.linregress(x, y)
14+
result = (slope > 0) if increasing else (slope < 0)
15+
# p_value for significance.
16+
assert result and p_value < 0.05, (slope, p_value)
17+
18+
19+
def test_decreasing_price_variance():
20+
# The variance of the average trade price should decrease over time (figure IV-3)
21+
# See Growing Artificial Societies p. 109.
22+
model = SugarscapeG1mt()
23+
model.datacollector._new_model_reporter(
24+
"price_variance",
25+
lambda m: np.var(
26+
flatten([a.prices for a in m.schedule.agents_by_type[Trader].values()])
27+
),
28+
)
29+
model.run_model(step_count=50)
30+
31+
df_model = model.datacollector.get_model_vars_dataframe()
32+
33+
check_slope(df_model.price_variance, increasing=False)
34+
35+
36+
def test_carrying_capacity():
37+
def calculate_carrying_capacities(enable_trade):
38+
carrying_capacities = []
39+
visions = range(1, 10)
40+
for vision_max in visions:
41+
model = SugarscapeG1mt(vision_max=vision_max, enable_trade=enable_trade)
42+
model.run_model(step_count=50)
43+
carrying_capacities.append(len(model.schedule.agents_by_type[Trader]))
44+
return carrying_capacities
45+
46+
# Carrying capacity should increase over mean vision (figure IV-6).
47+
# See Growing Artificial Societies p. 112.
48+
carrying_capacities_with_trade = calculate_carrying_capacities(True)
49+
check_slope(
50+
carrying_capacities_with_trade,
51+
increasing=True,
52+
)
53+
# Carrying capacity should be higher when trade is enabled (figure IV-6).
54+
carrying_capacities_no_trade = calculate_carrying_capacities(False)
55+
check_slope(
56+
carrying_capacities_no_trade,
57+
increasing=True,
58+
)
59+
60+
t_statistic, p_value = stats.ttest_rel(
61+
carrying_capacities_with_trade, carrying_capacities_no_trade
62+
)
63+
# t_statistic > 0 means carrying_capacities_with_trade has larger values
64+
# than carrying_capacities_no_trade.
65+
# p_value for significance.
66+
assert t_statistic > 0 and p_value < 0.05
67+
68+
69+
# TODO:
70+
# 1. Reproduce figure IV-12 that the log of average price should decrease over average agent age
71+
# 2. Reproduce figure IV-13 that the gini coefficient on trade should decrease over mean vision, and should be higher with trade
72+
# 3. a stricter test would be to ensure the amount of variance of the trade price matches figure IV-3

0 commit comments

Comments
 (0)