SPB Git

spb/wp29_uqo

Public
4.4 KB · 99 lines python
1"""F5-conf-04: VFI vs closed-form policy, optimal growth with log utility.2Reads only data/model.csv; writes out/results.json (deterministic, no randomness)."""3import json, os4import numpy as np5import pandas as pd67ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))8df = pd.read_csv(os.path.join(ROOT, "data", "model.csv"))9p = dict(zip(df["parameter"], df["value"]))10alpha = float(p["alpha"]); beta = float(p["beta"])11y_min = float(p["y_min"]); y_max = float(p["y_max"])12N = int(float(p["grid_points"])); spacing = str(p["grid_spacing"]).strip()13tol = float(p["vfi_tolerance"])14evals = [float(p["eval_y_1"]), float(p["eval_y_2"]), float(p["eval_y_3"])]15assert spacing == "log"1617data_issues = []1819# ---- Closed form (guess V = A + B ln y, verified in report) ----20ab = alpha * beta21s_closed = ab                                   # savings rate (y-c)/y22B = 1.0 / (1.0 - ab)23A = (np.log(1.0 - ab) + (ab / (1.0 - ab)) * np.log(ab)) / (1.0 - beta)24c_closed = lambda y: (1.0 - ab) * y25V_closed = lambda y: A + B * np.log(y)2627# ---- VFI on the prescribed log grid; choice of next-period output y' on the same grid ----28y = np.exp(np.linspace(np.log(y_min), np.log(y_max), N))29k_next = y ** (1.0 / alpha)                     # capital needed to produce y' = k^alpha30C = y[:, None] - k_next[None, :]                # consumption for (y, y')31feasible = C > 032U = np.full(C.shape, -np.inf)33U[feasible] = np.log(C[feasible])34V = np.zeros(N)35n_iter = 0; sup_change = np.inf36while sup_change >= tol:37    RHS = U + beta * V[None, :]38    V_new = RHS.max(axis=1)39    sup_change = float(np.max(np.abs(V_new - V)))40    V = V_new; n_iter += 141    if n_iter > 100000:42        raise RuntimeError("VFI did not converge")43pol_idx = (U + beta * V[None, :]).argmax(axis=1)44c_grid = y - k_next[pol_idx]45# check that the upper/lower bound of the choice grid never binds46n_bind_upper = int(np.sum(pol_idx == N - 1)); n_bind_lower = int(np.sum(pol_idx == 0))47if n_bind_upper or n_bind_lower:48    data_issues.append({"issue": f"choice grid bound binds at {n_bind_lower} (lower) / {n_bind_upper} (upper) grid points",49                        "handling": "reported; affects policy near grid edges only"})5051lny = np.log(y)52interp = lambda f, x: float(np.interp(np.log(x), lny, f))   # linear in ln y53on_grid = [bool(np.any(np.isclose(y, e, rtol=0, atol=1e-12))) for e in evals]54off = [e for e, g in zip(evals, on_grid) if not g]55if off:56    data_issues.append({"issue": f"evaluation points {off} are not nodes of the prescribed log grid",57                        "handling": "VFI policy and value interpolated linearly in ln y between adjacent grid nodes (as allowed by TASK.md)"})5859c_num = [interp(c_grid, e) for e in evals]60c_cl = [float(c_closed(e)) for e in evals]61max_err = float(max(abs(a - b) for a, b in zip(c_num, c_cl)))6263res = {64    "task_id": "F5-conf-04",65    "c_closed_y05": c_cl[0], "c_closed_y1": c_cl[1], "c_closed_y2": c_cl[2],66    "c_num_y05": c_num[0], "c_num_y1": c_num[1], "c_num_y2": c_num[2],67    "max_abs_error_policy": max_err,68    "value_closed_y1": float(V_closed(1.0)),69    "value_num_y1": interp(V, 1.0),70    "savings_rate_closed_form": float(s_closed),71    "contraction_modulus": beta,72    "grid_points_used": N,73    "n_iterations": n_iter,74    "final_sup_change": sup_change,75    "feasibility": "complete",76    "feasibility_reason": None,77    "data_issues": data_issues,78    "n_input_rows": {"model.csv": int(len(df))},79    "n_analysed": N,80    "random_seed": None,81    # supplementary diagnostics82    "baseline_savings_rate_closed_form": float(s_closed),83    "diag_closed_form_A": float(A), "diag_closed_form_B": float(B),84    "diag_grid_log_step": float(lny[1] - lny[0]),85    "diag_max_abs_value_error_grid": float(np.max(np.abs(V - V_closed(y)))),86    "diag_max_abs_policy_error_grid": float(np.max(np.abs(c_grid - c_closed(y)))),87    "diag_mean_savings_rate_num_grid": float(np.mean(1 - c_grid / y)),88    "diag_stopping_error_bound": float(beta * sup_change / (1 - beta)),89    "diag_choice_bound_binds": n_bind_lower + n_bind_upper,90}91os.makedirs(os.path.join(ROOT, "out"), exist_ok=True)92with open(os.path.join(ROOT, "out", "results.json"), "w") as f:93    json.dump(res, f, indent=2)94pd.DataFrame([{"step": "none", "rule": "no filtering applied; all 10 parameter rows used",95               "n_before": len(df), "n_after": len(df), "n_removed": 0,96               "rationale": "inputs are model parameters, not observations"}]).to_csv(97    os.path.join(ROOT, "out", "exclusions.csv"), index=False)98print(json.dumps(res, indent=2))99
Files · main
Menu

Theme