1"""F5-conf-01: Walrasian equilibrium of a 2x2 Cobb-Douglas exchange economy.2Run from workspace root: ~/wp29/venv/bin/python out/analysis.py"""3import json, os4import numpy as np, pandas as pd5from scipy.optimize import brentq67end = pd.read_csv("data/endowments.csv")8par = pd.read_csv("data/parameters.csv")9P = dict(zip(par["parameter"], par["value"]))10issues = []1112# --- validation ---13assert set(end["agent"]) == {"A", "B"} and len(end) == 2, "expect one row per agent A,B"14assert end["agent"].is_unique15if (end[["good1", "good2"]] <= 0).any().any():16 raise ValueError("non-positive endowment")17alpha = {"A": float(P["alpha_A"]), "B": float(P["alpha_B"])}18for k, a in alpha.items():19 assert 0 < a < 1, f"alpha_{k} not in (0,1)"20e = {r.agent: (float(r.good1), float(r.good2)) for r in end.itertuples()}21E1 = sum(v[0] for v in e.values()); E2 = sum(v[1] for v in e.values())22E1_sheet = float(P["aggregate_endowment_good1"]); E2_sheet = float(P["aggregate_endowment_good2"])23if not np.isclose(E1, E1_sheet):24 issues.append({"issue": f"parameters.csv aggregate_endowment_good1 = {E1_sheet:g} but the sum of individual "25 f"endowments of good 1 in endowments.csv is {E1:g} (8 + 4).",26 "handling": f"Used {E1:g}, the sum of the primary individual endowments; an aggregate that differs "27 "from the sum of individual endowments is inconsistent with a pure exchange economy "28 "(no production), so the summary-sheet value was treated as a transcription error and "29 "not used. Market clearing against the sheet value is reported in the report only as a diagnostic."})30if not np.isclose(E2, E2_sheet):31 issues.append({"issue": f"aggregate_endowment_good2 sheet {E2_sheet:g} != sum {E2:g}",32 "handling": f"Used {E2:g} (sum of individual endowments)."})3334# --- demands (p2 = 1) ---35def demand(i, p):36 w = p * e[i][0] + e[i][1]37 return alpha[i] * w / p, (1 - alpha[i]) * w3839def z(p, E1=E1, E2=E2):40 x1 = sum(demand(i, p)[0] for i in e); x2 = sum(demand(i, p)[1] for i in e)41 return x1 - E1, x2 - E24243# --- closed form: sum_i alpha_i (p e1_i + e2_i) = p E1 => p = sum alpha_i e2_i / sum (1-alpha_i) e1_i44p_cf = sum(alpha[i] * e[i][1] for i in e) / sum((1 - alpha[i]) * e[i][0] for i in e)4546# --- independent numerical check: root of z1(p) via Brent on a wide bracket (no use of closed form)47p_num = brentq(lambda p: z(p)[0], 1e-6, 1e6, xtol=1e-14, rtol=1e-15, maxiter=500)4849z1, z2 = z(p_cf)50walras = p_cf * z1 + z251# Walras law also checked off-equilibrium on a grid52grid = np.logspace(-2, 2, 41)53walras_grid_max = float(max(abs(p * z(p)[0] + z(p)[1]) for p in grid))54# diagnostic: clearing vs sheet aggregate55z1_sheet = z(p_cf, E1=E1_sheet)[0]5657xA, xB = demand("A", p_cf), demand("B", p_cf)58res = {59 "task_id": "F5-conf-01",60 "price_ratio_p1_over_p2": p_cf,61 "x1_A": xA[0], "x2_A": xA[1], "x1_B": xB[0], "x2_B": xB[1],62 "excess_demand_good1": z1,63 "walras_law_residual": walras,64 "aggregate_endowment_good1": E1,65 "aggregate_endowment_good2": E2,66 "price_ratio_numerical": p_num,67 "feasibility": "complete",68 "feasibility_reason": None,69 "data_issues": issues,70 "n_input_rows": {"endowments.csv": int(len(end)), "parameters.csv": int(len(par))},71 "n_analysed": len(e),72 "random_seed": None,73}74os.makedirs("out", exist_ok=True)75with open("out/results.json", "w") as f:76 json.dump(res, f, indent=2)77print(json.dumps(res, indent=2))78print("excess demand good2 at p_cf:", z2)79print("|p_cf - p_num|:", abs(p_cf - p_num))80print("max |Walras residual| on grid p in [0.01,100]:", walras_grid_max)81print("diagnostic z1 vs sheet E1=14 at p_cf:", z1_sheet)82