1"""Competitive equilibrium of a 2-agent, 2-good Cobb-Douglas exchange economy (F5-conf-01).2Run from workspace root: ~/wp29/venv/bin/python out/analysis.py3Deterministic; no randomness used."""4import json, os5import pandas as pd6from scipy.optimize import brentq78ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))9end = pd.read_csv(os.path.join(ROOT, "data", "endowments.csv"))10par = pd.read_csv(os.path.join(ROOT, "data", "parameters.csv"))11P = dict(zip(par["parameter"], par["value"].astype(float)))1213issues = []14# --- validation ---15assert set(end["agent"]) == {"A", "B"} and len(end) == 2, "need exactly agents A and B"16assert not end.isna().any().any() and not end["agent"].duplicated().any()17assert (end[["good1", "good2"]] > 0).all().all(), "endowments must be strictly positive"18alpha = {"A": P["alpha_A"], "B": P["alpha_B"]}19assert all(0 < a < 1 for a in alpha.values())20e = {r.agent: (float(r.good1), float(r.good2)) for r in end.itertuples()}21agents = ["A", "B"]2223E1 = sum(e[i][0] for i in agents)24E2 = sum(e[i][1] for i in agents)25for g, Eg, key in [(1, E1, "aggregate_endowment_good1"), (2, E2, "aggregate_endowment_good2")]:26 rec = P[key]27 if abs(rec - Eg) > 1e-12:28 issues.append({29 "issue": f"parameters.csv {key}={rec} does not equal the sum of individual endowments in endowments.csv ({Eg}).",30 "handling": f"Used the sum of individual endowments ({Eg}) because endowments.csv is the primary data and "31 f"market clearing/Walras' law require aggregates consistent with agents' budgets; "32 f"the summary-sheet value {rec} was not used in the equilibrium computation."})3334# --- demands and excess demand ---35def demand(i, p):36 m = p * e[i][0] + e[i][1]37 return alpha[i] * m / p, (1 - alpha[i]) * m3839def z(p):40 d = [demand(i, p) for i in agents]41 return sum(x[0] for x in d) - E1, sum(x[1] for x in d) - E24243# --- closed form: sum_i alpha_i (p e1_i + e2_i) = p E1 ---44num = sum(alpha[i] * e[i][1] for i in agents)45den = E1 - sum(alpha[i] * e[i][0] for i in agents)46assert den > 047p_cf = num / den4849# --- independent numerical root of z1(p) = 0 (bracketing; z1 strictly decreasing for CD) ---50z1 = lambda p: z(p)[0]51lo, hi = 1e-6, 1.052while z1(hi) > 0:53 hi *= 2.054p_num = brentq(z1, lo, hi, xtol=1e-14, rtol=1e-14, maxiter=500)5556xA, xB = demand("A", p_cf), demand("B", p_cf)57z1_cf, z2_cf = z(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_cf,63 "walras_law_residual": p_cf * z1_cf + z2_cf,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(agents),72 "random_seed": None,73}74# diagnostic only (not a results key): price if summary-sheet E1 were used75rec1 = P["aggregate_endowment_good1"]76p_alt = num / (rec1 - sum(alpha[i] * e[i][0] for i in agents))77with open(os.path.join(ROOT, "out", "results.json"), "w") as f:78 json.dump(res, f, indent=2)79print(json.dumps(res, indent=2))80print(f"[diagnostic] |p_cf - p_num| = {abs(p_cf - p_num):.3e}; z2 at p_cf = {z2_cf:.3e}; "81 f"price if summary-sheet E1={rec1} were used: {p_alt:.6f} (not used)")82