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"].astype(float)))10issues = []1112# --- validation ---13assert set(end["agent"]) == {"A", "B"} and len(end) == 214E = end.set_index("agent")[["good1", "good2"]].astype(float)15if (E.values <= 0).any():16 issues.append({"issue": "non-positive endowment", "handling": "closed form requires positive wealth; flagged"})17aA, aB = P["alpha_A"], P["alpha_B"]18for k, a in (("alpha_A", aA), ("alpha_B", aB)):19 if not (0 < a < 1):20 issues.append({"issue": f"{k} outside (0,1)", "handling": "flagged"})21sum1, sum2 = E["good1"].sum(), E["good2"].sum()22sheet1, sheet2 = P["aggregate_endowment_good1"], P["aggregate_endowment_good2"]23if not np.isclose(sum1, sheet1):24 issues.append({"issue": f"parameters.csv aggregate_endowment_good1 = {sheet1:g} but individual endowments in endowments.csv sum to {sum1:g} (A {E.loc['A','good1']:g} + B {E.loc['B','good1']:g}).",25 "handling": f"Used the sum of individual endowments ({sum1:g}) as the aggregate, because in an exchange economy aggregate supply is by definition the sum of agents' endowments and endowments.csv is designated primary data; the summary-sheet value {sheet1:g} is treated as a transcription error. Using {sheet1:g} would put {sheet1-sum1:g} units of good 1 that no agent owns into the market-clearing condition (inconsistent with Walras' law); a sensitivity check is reported in report.md."})26if not np.isclose(sum2, sheet2):27 issues.append({"issue": f"aggregate_endowment_good2 sheet {sheet2:g} != sum {sum2:g}", "handling": "used sum of individual endowments"})2829e1A, e2A, e1B, e2B = E.loc["A", "good1"], E.loc["A", "good2"], E.loc["B", "good1"], E.loc["B", "good2"]30W1, W2 = sum1, sum23132def demands(p):33 mA, mB = p * e1A + e2A, p * e1B + e2B34 return aA * mA / p, (1 - aA) * mA, aB * mB / p, (1 - aB) * mB3536def z(p, W1=W1, W2=W2):37 x1A, x2A, x1B, x2B = demands(p)38 return x1A + x1B - W1, x2A + x2B - W23940# --- closed form: p*W1 = aA*(p e1A+e2A) + aB*(p e1B+e2B) ---41p_cf = (aA * e2A + aB * e2B) / ((1 - aA) * e1A + (1 - aB) * e1B)42x1A, x2A, x1B, x2B = demands(p_cf)43z1, z2 = z(p_cf)44walras = p_cf * z1 + z24546# --- independent numerical root of z1(p) ---47p_num = brentq(lambda p: z(p)[0], 1e-6, 1e6, xtol=1e-14, rtol=1e-15, maxiter=500)4849# Walras' law holds identically: check on a grid50grid = np.logspace(-3, 3, 61)51walras_grid_max = float(max(abs(p * z(p)[0] + z(p)[1]) for p in grid))5253# sensitivity: summary-sheet aggregate (inconsistent)54p_sheet = brentq(lambda p: z(p, sheet1, sheet2)[0], 1e-6, 1e6, xtol=1e-14)55z_sheet = z(p_sheet, sheet1, sheet2)56sens = {"p_root_using_sheet_aggregate": p_sheet,57 "z2_at_that_price_using_sheet_aggregate": z_sheet[1],58 "walras_residual_using_sheet_aggregate": p_sheet * z_sheet[0] + z_sheet[1],59 "walras_grid_max_abs_residual": walras_grid_max,60 "abs_diff_closed_form_vs_numerical": abs(p_cf - p_num),61 "excess_demand_good2": z2}6263res = {64 "task_id": "F5-conf-01",65 "price_ratio_p1_over_p2": p_cf,66 "x1_A": x1A, "x2_A": x2A, "x1_B": x1B, "x2_B": x2B,67 "excess_demand_good1": z1,68 "walras_law_residual": walras,69 "aggregate_endowment_good1": W1,70 "aggregate_endowment_good2": W2,71 "price_ratio_numerical": p_num,72 "feasibility": "complete",73 "feasibility_reason": None,74 "data_issues": issues,75 "n_input_rows": {"endowments.csv": int(len(end)), "parameters.csv": int(len(par))},76 "n_analysed": 2,77 "random_seed": None,78}79os.makedirs("out", exist_ok=True)80json.dump(res, open("out/results.json", "w"), indent=2)81json.dump(sens, open("out/diagnostics.json", "w"), indent=2)82print(json.dumps(res, indent=2)); print(json.dumps(sens, indent=2))83