SPB Git

spb/wp29_uqo

Public
4.3 KB · 89 lines python
1"""F5-conf-04: VFI vs closed form for Brock-Mirman growth with log utility.2Run from workspace root: ~/wp29/venv/bin/python out/analysis.py3Deterministic; no random numbers used."""4import json, os5import numpy as np6import pandas as pd78os.makedirs("out", exist_ok=True)9df = pd.read_csv("data/model.csv")10n_rows = len(df)11p = dict(zip(df["parameter"], df["value"]))12alpha = float(p["alpha"]); beta = float(p["beta"])13y_min = float(p["y_min"]); y_max = float(p["y_max"]); N = int(float(p["grid_points"]))14spacing = str(p["grid_spacing"]).strip(); tol = float(p["vfi_tolerance"])15evals = [float(p["eval_y_1"]), float(p["eval_y_2"]), float(p["eval_y_3"])]16assert spacing == "log" and 0 < alpha < 1 and 0 < beta < 1 and y_min < y_max17data_issues = []1819# ---------- closed form (guess V = A + B ln y, verified in report) ----------20ab = alpha * beta21B = 1.0 / (1.0 - ab)22A = (np.log(1.0 - ab) + ab * B * np.log(ab)) / (1.0 - beta)23c_closed = lambda y: (1.0 - ab) * y24V_closed = lambda y: A + B * np.log(y)25savings_rate = ab2627# ---------- VFI on prescribed grid ----------28# State y on log grid; control = next-period output y' chosen on the same grid.29# Consumption implied: c = y - y'^(1/alpha) (capital k = y'^(1/alpha)).30grid = np.exp(np.linspace(np.log(y_min), np.log(y_max), N))31k_needed = grid ** (1.0 / alpha)                     # capital required for each y'32C = grid[:, None] - k_needed[None, :]                # rows: y, cols: y'33feasible = C > 034assert feasible.any(axis=1).all(), "some grid node has no feasible choice"35U = np.full_like(C, -np.inf); U[feasible] = np.log(C[feasible])36V = np.zeros(N); n_iter = 0; sup = np.inf37while sup >= tol:38    V_new = np.max(U + beta * V[None, :], axis=1)39    sup = float(np.max(np.abs(V_new - V))); V = V_new; n_iter += 140    if n_iter > 100000:41        raise RuntimeError("VFI did not converge")42idx = np.argmax(U + beta * V[None, :], axis=1)43c_pol = C[np.arange(N), idx]44lg = np.log(grid)45interp = lambda f, y: float(np.interp(np.log(y), lg, f))   # linear in ln y46c_num = [interp(c_pol, y) for y in evals]47c_cl = [float(c_closed(y)) for y in evals]48max_err = float(max(abs(a - b) for a, b in zip(c_num, c_cl)))49v_num_1 = interp(V, 1.0)50for y in evals:51    if not np.any(np.isclose(grid, y, rtol=0, atol=1e-12)):52        data_issues.append({"issue": f"evaluation point y={y} is not a grid node",53                            "handling": "VFI policy/value linearly interpolated in ln y between adjacent grid nodes"})54data_issues.append({"issue": "VFI control is discretised: next-period output y' restricted to the 400-point grid",55                    "handling": "standard discrete-choice VFI on the prescribed grid; no off-grid optimisation, so policy error reflects grid spacing"})5657# diagnostics (not required keys)58diag = {59    "grid_log_step": float(lg[1] - lg[0]),60    "max_abs_error_policy_on_grid": float(np.max(np.abs(c_pol - c_closed(grid)))),61    "max_abs_error_value_on_grid": float(np.max(np.abs(V - V_closed(grid)))),62    "vfi_error_bound_beta_over_1mbeta_times_sup": float(beta / (1 - beta) * sup),63    "steady_state_y": float(ab ** (alpha / (1 - alpha))),64    "choice_index_min_max": [int(idx.min()), int(idx.max())],65    "closed_form_A": float(A), "closed_form_B": float(B),66}6768res = {69    "task_id": "F5-conf-04",70    "c_closed_y05": c_cl[0], "c_closed_y1": c_cl[1], "c_closed_y2": c_cl[2],71    "c_num_y05": c_num[0], "c_num_y1": c_num[1], "c_num_y2": c_num[2],72    "max_abs_error_policy": max_err,73    "value_closed_y1": float(V_closed(1.0)), "value_num_y1": v_num_1,74    "savings_rate_closed_form": float(savings_rate),75    "baseline_savings_rate": float(savings_rate),76    "contraction_modulus": beta,77    "grid_points_used": int(N), "n_iterations": int(n_iter), "final_sup_change": sup,78    "feasibility": "complete", "feasibility_reason": None,79    "data_issues": data_issues,80    "n_input_rows": {"model.csv": int(n_rows)},81    "n_analysed": int(N), "random_seed": None,82    "diagnostics": diag,83}84with open("out/results.json", "w") as f:85    json.dump(res, f, indent=2)86pd.DataFrame([{"step": "parse", "rule": "read all parameter rows", "n_before": n_rows,87               "n_after": n_rows, "n_removed": 0, "rationale": "no rows excluded"}]).to_csv("out/exclusions.csv", index=False)88print(json.dumps({k: v for k, v in res.items() if k not in ("data_issues", "diagnostics")}, indent=1))89
Files · main
Menu

Theme