assembly-economics/simulations/gadm02/test_gadm03.py
tegwick c8329c6965 ASSEMBLY-WP-0005-T04: separate wealth from power (GADM-0.3)
- Wealth-concentration stock W with wealth-tax lever; converts into market
  and political channels only, moderated by checks (Q*S)
- wealth_on switch reproduces the T03 model exactly (verified)
- wealth.py: P3 supported and robust to conversion strength (full wealth
  tax lowers P by only 0.01-0.05 because membrane/lineage control does not
  run on wealth); effect on reachable channels depends on k_w
- Tests for wealth switch and tax direction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1494246@bnt-lap001
Assistant-Session: caf6de6f-f9e4-47ce-a584-49baa271fa4b
2026-10-12 07:32:57 +02:00

57 lines
2 KiB
Python

"""Sanity tests for the GADM-0.3 power-channel model.
Usage: python3 -B -m unittest discover -s simulations/gadm02
"""
import unittest
import gadm03 as g
from scenarios import load_all
class TestGADM03(unittest.TestCase):
def test_stocks_stay_bounded_in_all_scenarios(self):
for name, sc in load_all().items():
for row in g.simulate(sc["params"], sc["init"]):
for k in g.STOCKS + ("P",):
self.assertGreaterEqual(row[k], 0.0, (name, k))
self.assertLessEqual(row[k], 1.0, (name, k))
def test_calibration_matches_params(self):
self.assertEqual(g.calibrate(), {c: g.PARAMS[c] for c in g.COEF.values()})
def test_initial_channels_are_stable_equilibria(self):
# Guards against calibrating a channel onto its unstable (threshold) root.
d, _ = g.derivatives(dict(g.INIT), g.PARAMS)
for k in g.CHANNELS:
self.assertAlmostEqual(d[k], 0.0, places=3, msg=k)
self.assertLess(g.channel_slope(dict(g.INIT), g.PARAMS, k), 0.0, k)
def test_one_dominant_channel_gives_full_domination(self):
s = {k: 0.0 for k in g.CHANNELS}
s["Plin"] = 1.0
self.assertAlmostEqual(g.total_p(s), 1.0)
def test_aggregation_does_not_compound_equal_channels(self):
s = {k: 0.5 for k in g.CHANNELS}
self.assertAlmostEqual(g.total_p(s), 0.5)
def test_wealth_off_ignores_wealth(self):
p = g.t03_params()
a = g.simulate(p, {"W": 0.1})[-1]
b = g.simulate(p, {"W": 0.9})[-1]
for k in g.CHANNELS:
self.assertAlmostEqual(a[k], b[k], places=12, msg=k)
def test_wealth_tax_lowers_wealth(self):
lo = g.simulate({"wtax": 0.0})[-1]["W"]
hi = g.simulate({"wtax": 1.0})[-1]["W"]
self.assertLess(hi, lo)
def test_unknown_parameter_rejected(self):
with self.assertRaises(KeyError):
g.simulate({"not_a_param": 1.0})
if __name__ == "__main__":
unittest.main()