SPB Git

spb/wp25_uqo

Public
4.1 KB · 88 lines python
1"""Model B scenarios (stationary heterogeneous-agent equilibria) and the channel decomposition.23Scenarios (pre-registered): no_ai; production_only (compute in tasks, no delegation); advisory/delegation only4(information channel, no compute in tasks); full (both) with broad ownership; full with concentrated ownership5(top 10 %); scarce compute (fixed stock, rents) broad vs concentrated; tighter borrowing limit; cheap vs expensive6fees.  Outputs: results/model_b/scenarios.csv, results/model_b/<scenario>.json (incl. Lorenz curves, delegation7by decile), results/model_b/welfare_by_percentile.csv.8Run: python3 scripts/20_model_b.py [--quick]9"""10from __future__ import annotations1112import json13import sys14import time15from pathlib import Path1617import numpy as np18import pandas as pd1920sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'src'))21from age.equilibrium.aiyagari import EconomyB, HAParams  # noqa: E4022223ROOT = Path(__file__).resolve().parents[1]24OUT = ROOT / 'results' / 'model_b'25OUT.mkdir(parents=True, exist_ok=True)26QUICK = '--quick' in sys.argv27NA = 100 if QUICK else 1602829BASE = dict(n_a=NA, n_gh=3 if QUICK else 5, beta=0.95, sigma_e=0.17, rho_e=0.96, m_H=0.01, sigma_rH=0.04,30            sigma_rA=0.01, phi0=0.02, phi1=0.002, qbar=0.02, a_min=-0.5)3132SCENARIOS = {33    'no_ai': dict(delegation_on=False, KQ0=0.0, m_H=0.01, sigma_rH=0.04),34    'production_only': dict(delegation_on=False, KQ0=0.6, eps_Q=1.0),35    'delegation_only': dict(delegation_on=True, KQ0=0.0),36    'full_broad': dict(delegation_on=True, KQ0=0.6, eps_Q=1.0, ownership='broad'),37    'full_concentrated': dict(delegation_on=True, KQ0=0.6, eps_Q=1.0, ownership='concentrated', n_K=0.10),38    'scarce_broad': dict(delegation_on=True, KQ0=0.3, eps_Q=0.0, ownership='broad'),39    'scarce_concentrated': dict(delegation_on=True, KQ0=0.3, eps_Q=0.0, ownership='concentrated', n_K=0.10),40    'full_broad_tight_credit': dict(delegation_on=True, KQ0=0.6, eps_Q=1.0, a_min=0.0, ownership='broad'),41    'full_broad_cheap_fee': dict(delegation_on=True, KQ0=0.6, eps_Q=1.0, ownership='broad', phi0=0.005, phi1=0.0005),42    'full_broad_no_mistakes': dict(delegation_on=True, KQ0=0.6, eps_Q=1.0, ownership='broad', m_H=0.0, sigma_rH=0.01),43}444546def run(name, over):47    kw = {**BASE, **over}48    p = HAParams(**kw)49    e = EconomyB(p)50    t0 = time.time()51    res = e.solve(tol=2e-4)52    res['runtime_s'] = time.time() - t053    # consumption-equivalent welfare by wealth percentile relative to no_ai is computed in post-processing from54    # value functions; store V on the grid and the distribution55    hh = e.hh56    lam = hh.lam57    V = hh.V58    # expected value by wealth percentile (using beginning-of-period cash-on-hand x as the ranking variable)59    mass_x = lam.sum(axis=1)60    cum = np.cumsum(mass_x) / mass_x.sum()61    Vx = (V * lam).sum(axis=1) / np.maximum(mass_x, 1e-300)62    pct = {}63    for q in [0.1, 0.25, 0.5, 0.75, 0.9, 0.99]:64        i = int(np.searchsorted(cum, q))65        pct[f'V_p{int(q*100)}'] = float(Vx[min(i, len(Vx) - 1)])66    res.update(pct)67    res['V_mean'] = float((V * lam).sum())68    res['scenario'] = name69    res['params'] = kw70    lx, ly = res.pop('lorenz')71    res['lorenz_x'] = lx[::max(1, len(lx) // 400)].tolist()72    res['lorenz_y'] = ly[::max(1, len(ly) // 400)].tolist()73    res['deleg_by_decile'] = res['deleg_by_decile'].tolist()74    json.dump(res, open(OUT / f'{name}.json', 'w'), indent=1, default=float)75    print(f"{name:26s} r={res['r']:.4f} w={res['w']:.3f} D={res['D']:.3f} iota={res['iota']:.3f} gini={res['gini']:.3f} "76          f"top1={res['top1']:.3f} top10={res['top10']:.3f} shareL={res['shares']['labor']:.3f} Y={res['Y']:.3f} ({res['runtime_s']:.0f}s)")77    return res787980if __name__ == '__main__':81    names = sys.argv[1:] if len([a for a in sys.argv[1:] if not a.startswith('--')]) else list(SCENARIOS)82    names = [n for n in names if not n.startswith('--')]83    rows = []84    for n in names:85        r = run(n, SCENARIOS[n])86        rows.append({k: v for k, v in r.items() if np.isscalar(v) or isinstance(v, str)} | {f'share_{k}': v for k, v in r['shares'].items()})87    pd.DataFrame(rows).to_csv(OUT / 'scenarios.csv', index=False)88
Files · main
Menu

Theme