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 np5import pandas as pd6from scipy.optimize import brentq78end = pd.read_csv("data/endowments.csv")9par = pd.read_csv("data/parameters.csv")10P = dict(zip(par["parameter"], par["value"].astype(float)))11issues = []1213# --- validation of inputs ---14assert set(end["agent"]) == {"A", "B"} and len(end) == 2, "expected exactly agents A and B"15e = end.set_index("agent")[["good1", "good2"]].astype(float)16if (e.values <= 0).any():17 raise ValueError("non-positive endowment: closed form not valid")18aA, aB = P["alpha_A"], P["alpha_B"]19for k, a in (("alpha_A", aA), ("alpha_B", aB)):20 if not (0 < a < 1):21 raise ValueError(f"{k} outside (0,1)")2223E1 = float(e["good1"].sum()); E2 = float(e["good2"].sum())24S1 = P["aggregate_endowment_good1"]; S2 = P["aggregate_endowment_good2"]25if not np.isclose(E1, S1):26 issues.append({27 "issue": f"parameters.csv aggregate_endowment_good1 = {S1:g} but the sum of individual "28 f"endowments of good 1 in endowments.csv is {E1:g} (A={e.loc['A','good1']:g}, B={e.loc['B','good1']:g}).",29 "handling": f"Used the sum of individual endowments ({E1:g}) as the aggregate endowment. In an exchange economy "30 "aggregate supply is by definition the sum of individual endowments, and endowments.csv is declared the primary "31 "data; the summary-sheet value is treated as a transcription error. Using it would make markets impossible to clear "32 "(excess demand for good 1 at the equilibrium price would be "33 f"{E1 - S1:g}, not 0); this is reported as a sensitivity figure only."})34if not np.isclose(E2, S2):35 issues.append({"issue": f"aggregate_endowment_good2 = {S2:g} differs from sum of individual endowments {E2:g}.",36 "handling": f"Used the sum of individual endowments ({E2:g})."})3738alpha = {"A": aA, "B": aB}3940def income(p, i):41 return p * e.loc[i, "good1"] + e.loc[i, "good2"]4243def demand(p, i):44 m = income(p, i)45 return alpha[i] * m / p, (1 - alpha[i]) * m4647def z(p):48 d = [demand(p, i) for i in ("A", "B")]49 return sum(x[0] for x in d) - E1, sum(x[1] for x in d) - E25051# --- closed form: p*E1 = sum_i alpha_i (p e1_i + e2_i) => p = sum alpha_i e2_i / sum (1-alpha_i) e1_i52p_cf = (aA * e.loc["A", "good2"] + aB * e.loc["B", "good2"]) / \53 ((1 - aA) * e.loc["A", "good1"] + (1 - aB) * e.loc["B", "good1"])54p_cf = float(p_cf)5556# --- independent numerical root of z1(p) (bracketing; z1 -> +inf as p->0, -> negative as p->inf)57p_num = brentq(lambda p: z(p)[0], 1e-8, 1e8, xtol=1e-14, rtol=1e-14, maxiter=500)5859xA = demand(p_cf, "A"); xB = demand(p_cf, "B")60z1, z2 = z(p_cf)61walras = p_cf * z1 + z262# Walras' law at arbitrary off-equilibrium prices (identity check)63grid = np.logspace(-2, 2, 41)64walras_grid_max = float(max(abs(p * z(p)[0] + z(p)[1]) for p in grid))65z1_if_summary = float(xA[0] + xB[0] - S1)6667res = {68 "task_id": "F5-conf-01",69 "price_ratio_p1_over_p2": p_cf,70 "x1_A": float(xA[0]), "x2_A": float(xA[1]),71 "x1_B": float(xB[0]), "x2_B": float(xB[1]),72 "excess_demand_good1": float(z1),73 "excess_demand_good2": float(z2),74 "walras_law_residual": float(walras),75 "walras_law_max_abs_residual_offequilibrium_grid": walras_grid_max,76 "aggregate_endowment_good1": E1,77 "aggregate_endowment_good2": E2,78 "price_ratio_numerical": float(p_num),79 "abs_diff_closed_form_vs_numerical": abs(p_cf - p_num),80 "sensitivity_excess_demand_good1_vs_summary_sheet_aggregate": z1_if_summary,81 "feasibility": "complete",82 "feasibility_reason": None,83 "data_issues": issues,84 "n_input_rows": {"endowments.csv": int(len(end)), "parameters.csv": int(len(par))},85 "n_analysed": 2,86 "random_seed": None,87}88os.makedirs("out", exist_ok=True)89with open("out/results.json", "w") as f:90 json.dump(res, f, indent=2)91print(json.dumps(res, indent=2))92