Skip to content

Commit

Permalink
Sugarscape {G1,{M,T}}: Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rht committed Nov 26, 2023
1 parent 10985d4 commit 5002741
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
3 changes: 2 additions & 1 deletion examples/sugarscape_g1mt/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Summary

This is Epstein & Axtell's Sugarscape model with Traders, a detailed description is in Chapter four of
*Growing Artificial Societies: Social Science from the Bottom Up.* (1996)
*Growing Artificial Societies: Social Science from the Bottom Up.* (1996) The model shows an emergent price equilibrium can happen via a decentralized dynamics.

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

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

## Additional Resources

Expand Down
7 changes: 7 additions & 0 deletions examples/sugarscape_g1mt/sugarscape_g1mt/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def __init__(
metabolism_max=5,
vision_min=1,
vision_max=5,
enable_trade=True,
):
# Initiate width and heigh of sugarscape
self.width = width
Expand All @@ -61,6 +62,7 @@ def __init__(
self.metabolism_max = metabolism_max
self.vision_min = vision_min
self.vision_max = vision_max
self.enable_trade = enable_trade
self.running = True

# initiate activation schedule
Expand Down Expand Up @@ -175,6 +177,11 @@ def step(self):
agent.eat()
agent.maybe_die()

if not self.enable_trade:
# If trade is not enabled, return early
self.datacollector.collect(self)
return

trader_shuffle = self.randomize_traders()

for agent in trader_shuffle:
Expand Down
72 changes: 72 additions & 0 deletions examples/sugarscape_g1mt/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import random

import numpy as np
from scipy import stats
from sugarscape_g1mt.model import SugarscapeG1mt, flatten
from sugarscape_g1mt.trader_agents import Trader

random.seed(1)


def check_slope(y, increasing):
x = range(len(y))
slope, intercept, _, p_value, _ = stats.linregress(x, y)
result = (slope > 0) if increasing else (slope < 0)
# p_value for significance.
assert result and p_value < 0.05, (slope, p_value)


def test_decreasing_variance():
# The variance of the average trade price should decrease over time (figure IV-3)
# See Growing Artificial Societies p. 109.
model = SugarscapeG1mt()
model.datacollector._new_model_reporter(
"price_variance",
lambda m: np.var(
flatten([a.prices for a in m.schedule.agents_by_type[Trader].values()])
),
)
model.run_model(step_count=50)

df_model = model.datacollector.get_model_vars_dataframe()

check_slope(df_model.price_variance, increasing=False)


def test_carrying_capacity():
def calculate_carrying_capacities(enable_trade):
carrying_capacities = []
visions = range(1, 10)
for vision_max in visions:
model = SugarscapeG1mt(vision_max=vision_max, enable_trade=enable_trade)
model.run_model(step_count=50)
carrying_capacities.append(len(model.schedule.agents_by_type[Trader]))
return carrying_capacities

# Carrying capacity should increase over mean vision (figure IV-6).
# See Growing Artificial Societies p. 112.
carrying_capacities_with_trade = calculate_carrying_capacities(True)
check_slope(
carrying_capacities_with_trade,
increasing=True,
)
# Carrying capacity should be higher when trade is enabled (figure IV-6).
carrying_capacities_no_trade = calculate_carrying_capacities(False)
check_slope(
carrying_capacities_no_trade,
increasing=True,
)

t_statistic, p_value = stats.ttest_rel(
carrying_capacities_with_trade, carrying_capacities_no_trade
)
# t_statistic > 0 means carrying_capacities_with_trade has larger values
# than carrying_capacities_no_trade.
# p_value for significance.
assert t_statistic > 0 and p_value < 0.05


# TODO:
# 1. Reproduce figure IV-12 that the log of average price should decrease over average agent age
# 2. Reproduce figure IV-13 that the gini coefficient on trade should decrease over mean vision, and should be higher with trade
# 3. a stricter test would be to ensure the amount of variance of the trade price matches figure IV-3

0 comments on commit 5002741

Please sign in to comment.