From 2c602d01c2b9178f633e06b70c814c2bb61f449f Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 10:14:54 +0000 Subject: [PATCH 01/19] Import capped log Aiyagari equilibrium and dynamic programming --- LeanPool/AiyagariEquilibrium.lean | 21 + .../Analysis/DecreasingIncrements.lean | 161 +++ .../Analysis/DifferentiableSandwich.lean | 136 +++ .../AiyagariEquilibrium/Analysis/HARA.lean | 151 +++ .../Analysis/PowerMean.lean | 324 +++++ .../Analysis/RelativeRiskAversion.lean | 303 +++++ .../Distribution/Feller.lean | 280 +++++ .../Distribution/Stationary.lean | 175 +++ .../Distribution/Uniqueness.lean | 560 +++++++++ .../DynamicProgramming/Bellman.lean | 336 +++++ .../DynamicProgramming/Blackwell.lean | 478 ++++++++ .../DynamicProgramming/Extended.lean | 370 ++++++ .../ExtendedStochastic.lean | 455 +++++++ .../DynamicProgramming/Stochastic.lean | 232 ++++ .../Equilibrium/Aiyagari.lean | 151 +++ .../Equilibrium/AiyagariContinuity.lean | 106 ++ .../Equilibrium/AiyagariUniqueness.lean | 511 ++++++++ .../Equilibrium/Bisection.lean | 277 +++++ .../Equilibrium/CapitalSupplyMonotone.lean | 223 ++++ .../AiyagariEquilibrium/Equilibrium/Firm.lean | 207 ++++ .../Equilibrium/PositiveCapital.lean | 544 +++++++++ .../Equilibrium/SignChange.lean | 451 +++++++ .../Equilibrium/Uniqueness.lean | 114 ++ LeanPool/AiyagariEquilibrium/Imports.lean | 64 + .../Models/AiyagariTauchenWitness.lean | 130 ++ .../Models/BoundedIncomeFluctuation.lean | 252 ++++ LeanPool/AiyagariEquilibrium/Models/CRRA.lean | 239 ++++ .../Models/CRRAConstants.lean | 706 +++++++++++ .../Models/ConsumptionFloor.lean | 228 ++++ .../Models/ConsumptionSavingsUnbounded.lean | 542 +++++++++ .../Models/EulerCorner.lean | 179 +++ .../Models/ImpatientDecline.lean | 243 ++++ .../Models/IncomeFluctuation.lean | 741 +++++++++++ .../Models/IncomeFluctuationAugmented.lean | 597 +++++++++ .../Models/IncomeFluctuationBounds.lean | 184 +++ .../IncomeFluctuationCarrollKimball.lean | 571 +++++++++ .../Models/IncomeFluctuationConsumption.lean | 252 ++++ .../Models/IncomeFluctuationCrossRate.lean | 323 +++++ .../Models/IncomeFluctuationEnvelope.lean | 201 +++ .../Models/IncomeFluctuationEuler.lean | 737 +++++++++++ .../Models/IncomeFluctuationHARA.lean | 872 +++++++++++++ .../Models/IncomeFluctuationIterate.lean | 1083 +++++++++++++++++ .../Models/IncomeFluctuationLipschitz.lean | 406 ++++++ .../Models/IncomeFluctuationMonotone.lean | 159 +++ .../Models/IncomeFluctuationRate.lean | 335 +++++ .../Models/IncomeFluctuationRateMonotone.lean | 209 ++++ .../Models/IncomeFluctuationRateStep.lean | 752 ++++++++++++ .../Models/IncomeFluctuationRegion.lean | 296 +++++ .../Models/LargeBetaLight.lean | 216 ++++ .../Models/LogAiyagari.lean | 218 ++++ .../Models/MinimalMPC.lean | 687 +++++++++++ .../Models/MonotoneTransitions.lean | 322 +++++ .../Models/NaturalAssetBound.lean | 351 ++++++ .../AiyagariEquilibrium/Models/Tauchen.lean | 200 +++ .../AiyagariEquilibrium/Topology/Berge.lean | 274 +++++ .../Topology/BergeEReal.lean | 179 +++ .../Topology/IccCorrespondence.lean | 101 ++ LeanPool/projects/aiyagari-equilibrium.yaml | 62 + 58 files changed, 19477 insertions(+) create mode 100644 LeanPool/AiyagariEquilibrium.lean create mode 100644 LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean create mode 100644 LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean create mode 100644 LeanPool/AiyagariEquilibrium/Analysis/HARA.lean create mode 100644 LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean create mode 100644 LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean create mode 100644 LeanPool/AiyagariEquilibrium/Distribution/Feller.lean create mode 100644 LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean create mode 100644 LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean create mode 100644 LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean create mode 100644 LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean create mode 100644 LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean create mode 100644 LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean create mode 100644 LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean create mode 100644 LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean create mode 100644 LeanPool/AiyagariEquilibrium/Imports.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/CRRA.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean create mode 100644 LeanPool/AiyagariEquilibrium/Models/Tauchen.lean create mode 100644 LeanPool/AiyagariEquilibrium/Topology/Berge.lean create mode 100644 LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean create mode 100644 LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean create mode 100644 LeanPool/projects/aiyagari-equilibrium.yaml diff --git a/LeanPool/AiyagariEquilibrium.lean b/LeanPool/AiyagariEquilibrium.lean new file mode 100644 index 000000000..b3e268f4b --- /dev/null +++ b/LeanPool/AiyagariEquilibrium.lean @@ -0,0 +1,21 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module + +public import LeanPool.AiyagariEquilibrium.Models.AiyagariTauchenWitness +public import LeanPool.AiyagariEquilibrium.Equilibrium.Bisection + +/-! +# Capped log Aiyagari equilibrium and dynamic programming + +Source: url:https://github.com/LeanEconomics/LeanEconomics +Authors: Robert Kirkby +Status: verified +Main declarations: `LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo` +Tags: mathematical-economics, dynamic-programming, stationary-equilibrium, markov-processes +MSC: 91B50, 90C39, 60J05 +-/ diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean new file mode 100644 index 000000000..5c0601719 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean @@ -0,0 +1,161 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Analysis.Convex.Function +public import Mathlib.Analysis.Convex.SpecificFunctions.Basic +public import Mathlib.Analysis.Convex.Slope + + +/-! +# Decreasing increments of a concave function + +A concave function's increment over an interval of fixed length shrinks as the interval +slides right. In economics this is the increasing-differences (supermodularity) condition +that monotone-comparative-statics arguments run on, usually stated as a cross partial +`-u'' > 0`. Stated this way it needs no derivatives and no smoothness at all. + +The proof is two applications of concavity. Writing `L = (c₂ + Δ) - c₁`, the two interior +points `c₁ + Δ` and `c₂` are *complementary* convex combinations of the endpoints `c₁` and +`c₂ + Δ`: the weights on `c₂ + Δ` are `Δ / L` and `(c₂ - c₁) / L`, which sum to one. Adding +the two concavity inequalities makes the weights cancel. +-/ + +@[expose] public section + +open Set + +/-- **Decreasing increments.** For a concave `u`, sliding an interval to the right by `Δ` +cannot increase the increment of `u` across it. -/ +theorem ConcaveOn.sub_le_sub_of_shift {s : Set ℝ} {u : ℝ → ℝ} (hu : ConcaveOn ℝ s u) + {c₁ c₂ Δ : ℝ} (h₁ : c₁ ∈ s) (h₂ : c₂ + Δ ∈ s) (hc : c₁ ≤ c₂) (hΔ : 0 ≤ Δ) : + u (c₂ + Δ) - u (c₁ + Δ) ≤ u c₂ - u c₁ := by + rcases eq_or_lt_of_le (show c₁ ≤ c₂ + Δ by linarith) with hdeg | hpos + · -- `c₁ = c₂ + Δ` forces `Δ = 0` and `c₁ = c₂`, and the claim is an equality + have hΔ0 : Δ = 0 := by linarith + have hcc : c₁ = c₂ := by linarith + subst hΔ0 + simp [hcc] + · have hL : 0 < c₂ + Δ - c₁ := by linarith + set θ : ℝ := Δ / (c₂ + Δ - c₁) with hθdef + have hθ0 : 0 ≤ θ := div_nonneg hΔ hL.le + have hθ1 : 0 ≤ 1 - θ := by + rw [hθdef, sub_nonneg, div_le_one hL] + linarith + -- `c₁ + Δ` and `c₂` are complementary convex combinations of `c₁` and `c₂ + Δ` + have e1 : (1 - θ) * c₁ + θ * (c₂ + Δ) = c₁ + Δ := by + rw [hθdef]; field_simp; ring + have e2 : θ * c₁ + (1 - θ) * (c₂ + Δ) = c₂ := by + rw [hθdef]; field_simp; ring + have i1 := hu.2 h₁ h₂ hθ1 hθ0 (by ring) + have i2 := hu.2 h₁ h₂ hθ0 hθ1 (by ring) + rw [smul_eq_mul, smul_eq_mul, smul_eq_mul, smul_eq_mul, e1] at i1 + rw [smul_eq_mul, smul_eq_mul, smul_eq_mul, smul_eq_mul, e2] at i2 + nlinarith [i1, i2] + +/-! ### Lipschitz away from the left endpoint + +A concave function on `Ioi 0` need not be Lipschitz there -- utility functions used in +economics are exactly the ones that are not, diverging at zero -- but it IS Lipschitz on +`[δ, ∞)` for any `δ > 0`, with a constant read off a single secant. No differentiability is +needed: the bound comes from antitonicity of secant slopes. + +This is what lets a marginal argument be run without a derivative. Where the textbook +argument bounds the marginal utility of consumption by `u'(δ)`, this bounds it by the slope +of `u` across `[δ/2, δ]`, which is available from concavity alone. -/ + +/-- The secant slope of `u` across `[δ/2, δ]`, which bounds every slope further right. -/ +noncomputable def slopeBound (u : ℝ → ℝ) (δ : ℝ) : ℝ := (u δ - u (δ / 2)) / (δ / 2) + +theorem slopeBound_nonneg {u : ℝ → ℝ} (hmono : MonotoneOn u (Ioi 0)) {δ : ℝ} (hδ : 0 < δ) : + 0 ≤ slopeBound u δ := by + refine div_nonneg (sub_nonneg.mpr (hmono ?_ ?_ (by linarith))) (by linarith) + · exact mem_Ioi.mpr (by linarith) + · exact mem_Ioi.mpr hδ + +/-- The one-sided bound: moving right from `a ≥ δ` raises `u` by at most the slope bound +times the distance. -/ +theorem ConcaveOn.sub_le_slopeBound_mul {u : ℝ → ℝ} (hu : ConcaveOn ℝ (Ioi 0) u) {δ : ℝ} + (hδ : 0 < δ) {a b : ℝ} (ha : δ ≤ a) (hab : a ≤ b) : + u b - u a ≤ slopeBound u δ * (b - a) := by + rcases eq_or_lt_of_le hab with rfl | hlt + · simp + have hhalf : (0 : ℝ) < δ / 2 := by linarith + have hmemh : δ / 2 ∈ Ioi (0 : ℝ) := mem_Ioi.mpr hhalf + have hmemd : δ ∈ Ioi (0 : ℝ) := mem_Ioi.mpr hδ + have hmema : a ∈ Ioi (0 : ℝ) := mem_Ioi.mpr (by linarith) + have hmemb : b ∈ Ioi (0 : ℝ) := mem_Ioi.mpr (by linarith) + -- the slope across `[a, b]` is at most the slope bound + have hkey : (u b - u a) / (b - a) ≤ slopeBound u δ := by + rcases eq_or_lt_of_le ha with rfl | hgt + · -- `a = δ`: one step + have h := hu.slope_anti_adjacent hmemh hmemb (by linarith) hlt + simpa only [slopeBound, show δ - δ / 2 = δ / 2 by ring] using h + · -- `a > δ`: chain two steps through `δ` + have h1 := hu.slope_anti_adjacent hmemd hmemb hgt hlt + have h2 := hu.slope_anti_adjacent hmemh hmema (by linarith) hgt + simp only [slopeBound, show δ - δ / 2 = δ / 2 by ring] at h2 ⊢ + linarith + rw [div_le_iff₀ (by linarith)] at hkey + linarith + +/-- **A concave function is Lipschitz away from the left endpoint**, with an explicit +constant and no appeal to differentiability. -/ +theorem ConcaveOn.abs_sub_le_slopeBound_mul {u : ℝ → ℝ} (hu : ConcaveOn ℝ (Ioi 0) u) + (hmono : MonotoneOn u (Ioi 0)) {δ : ℝ} (hδ : 0 < δ) {x y : ℝ} + (hx : δ ≤ x) (hy : δ ≤ y) : + |u x - u y| ≤ slopeBound u δ * |x - y| := by + rcases le_total y x with h | h + · rw [abs_of_nonneg (sub_nonneg.mpr (hmono (mem_Ioi.mpr (by linarith)) + (mem_Ioi.mpr (by linarith)) h)), abs_of_nonneg (by linarith)] + exact hu.sub_le_slopeBound_mul hδ hy h + · rw [abs_sub_comm, abs_sub_comm x y, + abs_of_nonneg (sub_nonneg.mpr (hmono (mem_Ioi.mpr (by linarith)) + (mem_Ioi.mpr (by linarith)) h)), abs_of_nonneg (by linarith)] + exact hu.sub_le_slopeBound_mul hδ hx h + +/-! ### Scaling a concave function + +For a concave, strictly positive `z`, the ratio `α / z (α x)` rises with `α`. This is Lemma 3 +of Light (2018), one of the three ingredients of his Theorem 1 (savings rise with the interest +rate when relative risk aversion is at most one). + +Like the increments lemma above, it needs no derivative: `α₁ x` is a convex combination of `0` +and `α₂ x` with weight `α₁ / α₂`, so concavity gives the inequality directly, and strict +positivity at the origin is what turns it from weak into the stated form. +-/ + +/-- **Light (2018), Lemma 3**, on any convex set containing the origin. In the application `z` +is the consumption function, which is concave on the asset region and nowhere else, so the +original statement over `[0, ∞)` would not reach it. Positivity is needed only at the origin +and at the far point: concavity carries it to everything in between. -/ +theorem ConcaveOn.div_le_div_of_scale {z : ℝ → ℝ} {s : Set ℝ} (hz : ConcaveOn ℝ s z) + (h0 : (0 : ℝ) ∈ s) (hz0 : 0 < z 0) {α₁ α₂ x : ℝ} (h1 : 0 < α₁) (h12 : α₁ ≤ α₂) (hx : 0 < x) + (hm2 : α₂ * x ∈ s) (hp2 : 0 < z (α₂ * x)) : + α₁ / z (α₁ * x) ≤ α₂ / z (α₂ * x) := by + have h2 : 0 < α₂ := lt_of_lt_of_le h1 h12 + -- `α₁ x` is a convex combination of `0` and `α₂ x` + have hcomb : (1 - α₁ / α₂) • (0 : ℝ) + (α₁ / α₂) • (α₂ * x) = α₁ * x := by + simp only [smul_eq_mul, mul_zero, zero_add] + field_simp + have hw1 : (0 : ℝ) ≤ 1 - α₁ / α₂ := by + rw [sub_nonneg, div_le_one h2]; exact h12 + have hw2 : (0 : ℝ) < α₁ / α₂ := div_pos h1 h2 + have hcc := hz.2 h0 hm2 hw1 hw2.le (by ring) + rw [hcomb] at hcc + simp only [smul_eq_mul] at hcc + -- concavity carries positivity from the endpoints to `α₁ x` + have hp1 : 0 < z (α₁ * x) := by + nlinarith [mul_nonneg hw1 hz0.le, mul_pos hw2 hp2] + rw [div_le_div_iff₀ hp1 hp2] + have hkey : α₁ * z (α₂ * x) ≤ α₂ * z (α₁ * x) := by + have hmul := mul_le_mul_of_nonneg_left hcc h2.le + have hexp : α₂ * ((1 - α₁ / α₂) * z 0 + α₁ / α₂ * z (α₂ * x)) + = (α₂ - α₁) * z 0 + α₁ * z (α₂ * x) := by field_simp + rw [hexp] at hmul + have hz0' : 0 ≤ (α₂ - α₁) * z 0 := mul_nonneg (by linarith) hz0.le + linarith + linarith diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean b/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean new file mode 100644 index 000000000..8b17b9953 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean @@ -0,0 +1,136 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Analysis.Calculus.LocalExtr.Basic +public import Mathlib.Analysis.Calculus.Deriv.Add +public import Mathlib.Analysis.Convex.Deriv + + +/-! +# The Differentiable Sandwich Lemma + +A function squeezed between two functions that are differentiable at a point, and agreeing +with both at that point, is itself differentiable there, with the same derivative. + +This is Lemma 1 of Clausen and Strub (2020), "Reverse Calculus and Nested Optimization", +*Journal of Economic Theory* 187, 105019, and it is the foundation of their approach to +envelope theorems. Their point is that it needs neither concavity nor interiority, so it +applies where Benveniste–Scheinkman does not — in particular where a constraint binds, which +in a Bewley economy is exactly the states that matter, since the borrowing constraint is where +the whole ergodic argument lives. + +The proof is two moves. The gap `U - L` is nonnegative near the point and vanishes at it, so +it has a local minimum there and its derivative is zero, which forces the two derivatives to +agree. Then the difference quotients of `F` are squeezed between those of `L` and `U`. + +Mathlib has the local-minimum step (`IsLocalMin.hasDerivAt_eq_zero`) but not this lemma. +-/ + +@[expose] public section + +open Filter Topology Asymptotics + +/-- **Differentiable Sandwich Lemma** (Clausen–Strub 2020, Lemma 1). If `F` is squeezed +between `L` and `U` near `c`, all three agree at `c`, and `L` and `U` are differentiable at +`c`, then the two derivatives coincide and `F` is differentiable at `c` with that derivative. -/ +theorem hasDerivAt_of_sandwich {F L U : ℝ → ℝ} {c l u : ℝ} {N : Set ℝ} (hN : N ∈ 𝓝 c) + (hLF : ∀ x ∈ N, L x ≤ F x) (hFU : ∀ x ∈ N, F x ≤ U x) + (hLc : L c = F c) (hUc : U c = F c) + (hL : HasDerivAt L l c) (hU : HasDerivAt U u c) : + l = u ∧ HasDerivAt F l c := by + -- the gap has a local minimum at `c`, so the derivatives agree + have hd : HasDerivAt (fun x => U x - L x) (u - l) c := hU.sub hL + have hmin : IsLocalMin (fun x => U x - L x) c := by + filter_upwards [hN] with x hx + have h1 := hLF x hx + have h2 := hFU x hx + simp only [hLc, hUc] + linarith + have hul : u - l = 0 := hmin.hasDerivAt_eq_zero hd + have hlu : l = u := by linarith + refine ⟨hlu, ?_⟩ + -- and the difference quotients of `F` are squeezed + have hU' : HasDerivAt U l c := by rwa [hlu] + rw [hasDerivAt_iff_isLittleO, isLittleO_iff] + intro ε hε + have hA := isLittleO_iff.mp (hasDerivAt_iff_isLittleO.mp hL) hε + have hB := isLittleO_iff.mp (hasDerivAt_iff_isLittleO.mp hU') hε + filter_upwards [hA, hB, hN] with y hya hyb hyn + rw [Real.norm_eq_abs] at hya hyb ⊢ + have h1 := hLF y hyn + have h2 := hFU y hyn + rw [abs_le] at hya hyb ⊢ + simp only [smul_eq_mul] at hya hyb ⊢ + rw [hLc] at hya + rw [hUc] at hyb + constructor <;> linarith [hya.1, hya.2, hyb.1, hyb.2] + +/-! ### The supporting line + +The upper half of the sandwich, for a concave function of one real variable. Mathlib has no +subgradient or supporting-hyperplane theory for convex or concave FUNCTIONS, but in one +dimension the slope API suffices: `ConcaveOn.slope_anti` says secant slopes through a point +are antitone, so every slope to the left is at least every slope to the right, and the +infimum of the left slopes separates them. + +Clausen and Strub note that concavity is needed only for this half — the lower bound in the +sandwich need not be concave, which is where they improve on Rockafellar's antecedent. For a +value function that matters: concavity is available here, and the lazy agent's lower bound +would not have to be concave in the state. +-/ + +/-- **A concave function has a supporting line at any point with neighbours on both sides.** +The slope is the infimum of the secant slopes to the left. -/ +theorem ConcaveOn.exists_supportingLine {f : ℝ → ℝ} {s : Set ℝ} (hf : ConcaveOn ℝ s f) + {x : ℝ} (hx : x ∈ s) (hlt : ∃ y ∈ s, y < x) (hgt : ∃ y ∈ s, x < y) : + ∃ m : ℝ, ∀ y ∈ s, f y ≤ f x + m * (y - x) := by + classical + have hanti := hf.slope_anti hx + obtain ⟨y₁, hy₁s, hy₁⟩ := hlt + obtain ⟨y₂, hy₂s, hy₂⟩ := hgt + -- every slope to the left dominates every slope to the right + have hcross : ∀ z ∈ s, z < x → ∀ y ∈ s, x < y → slope f x y ≤ slope f x z := by + intro z hzs hz y hys hy + exact hanti ⟨hzs, by simp [ne_of_lt hz]⟩ ⟨hys, by simp [ne_of_gt hy]⟩ (le_of_lt (hz.trans hy)) + set Lset : Set ℝ := slope f x '' {y ∈ s | y < x} with hLdef + have hne : Lset.Nonempty := ⟨slope f x y₁, ⟨y₁, ⟨hy₁s, hy₁⟩, rfl⟩⟩ + have hbdd : BddBelow Lset := by + refine ⟨slope f x y₂, ?_⟩ + rintro _ ⟨z, ⟨hzs, hz⟩, rfl⟩ + exact hcross z hzs hz y₂ hy₂s hy₂ + refine ⟨sInf Lset, fun y hy => ?_⟩ + rcases lt_trichotomy y x with h | h | h + · -- to the left: the slope is at least the infimum + have hmem : slope f x y ∈ Lset := ⟨y, ⟨hy, h⟩, rfl⟩ + have hle : sInf Lset ≤ slope f x y := csInf_le hbdd hmem + rw [slope_def_field, le_div_iff_of_neg (by linarith)] at hle + linarith + · subst h; simp + · -- to the right: the slope is a lower bound for the left slopes, hence below the infimum + have hle : slope f x y ≤ sInf Lset := by + refine le_csInf hne ?_ + rintro _ ⟨z, ⟨hzs, hz⟩, rfl⟩ + exact hcross z hzs hz y hy h + rw [slope_def_field, div_le_iff₀ (by linarith)] at hle + linarith + +/-- **A concave function with a differentiable lower bound touching at a point is +differentiable there**, with the lower bound's derivative. + +This is the form the envelope theorem uses: concavity supplies the upper half of the sandwich +automatically, so only the lower bound has to be produced, and Clausen and Strub produce it +with the lazy agent — freeze the optimal choice and vary the state, which is differentiable +because the state then enters only through the reward. -/ +theorem ConcaveOn.hasDerivAt_of_lowerBound {f L : ℝ → ℝ} {s : Set ℝ} {x l : ℝ} + (hf : ConcaveOn ℝ s f) (hx : x ∈ s) (hs : s ∈ 𝓝 x) + (hlt : ∃ y ∈ s, y < x) (hgt : ∃ y ∈ s, x < y) + (hLf : ∀ y ∈ s, L y ≤ f y) (hLx : L x = f x) (hL : HasDerivAt L l x) : + HasDerivAt f l x := by + obtain ⟨m, hm⟩ := hf.exists_supportingLine hx hlt hgt + have hU : HasDerivAt (fun y => f x + m * (y - x)) m x := by + simpa using (((hasDerivAt_id x).sub_const x).const_mul m).const_add (f x) + exact (hasDerivAt_of_sandwich hs hLf hm hLx (by simp) hL hU).2 diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean new file mode 100644 index 000000000..c297d64c7 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -0,0 +1,151 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion +public import LeanPool.AiyagariEquilibrium.Models.CRRA + + +/-! +# HARA utility + +Hyperbolic absolute risk aversion: absolute risk tolerance `-u' / u''` is affine in consumption. +Toda (2021) shows HARA is NECESSARY for the consumption function to be concave under regularity +conditions, so it is the class in which Carroll and Kimball (1996) -- and hence Light's +uniqueness argument -- can possibly hold. + +## The definition + +Stating "risk tolerance is affine" needs second derivatives. Since risk tolerance `a + c` scaled +by `1/γ` integrates to a shifted CRRA, the family can instead be given by its members, which is +derivative-free and is what every proof below actually uses: + + `u c = k · (a + c) ^ (1 - γ) / (1 - γ) + l`, and the `γ = 1` logarithm, + +with `k > 0`. This is the `b ≠ 0` branch of HARA -- the one that contains CRRA, and the one +macroeconomics uses. The quadratic branch (`b < 0`) is excluded because it satiates, and that +exclusion is now a theorem rather than a remark: `Models.Quadratic.no_incomeFluctuation_quadUtility` +shows no economy in this development can have quadratic utility, for any bliss point, since the +structure asks for monotone utility on the whole of `dom` and a quadratic falls beyond its bliss +point. `Models.Quadratic` also records what the branch would give if it could be hosted — Hall's +certainty equivalence, the aggregator being the plain weighted mean. + +CARA (`b = 0`) is NOT excluded, and an earlier version of this docstring was wrong about why it +might be: `u c = -exp (-α c) / α` is bounded above by `0` and equals `-1/α` at zero consumption, +so a capped model admits it -- `Models.IncomeFluctuationCARA.caraWitness` is one. What CARA does +not have is Light's condition, since relative risk aversion `α c` is unbounded +(`not_monotoneOn_mul_deriv_caraUtility`). It is covered separately because its Carroll--Kimball +step needs a different aggregator, the soft minimum of `Analysis.SoftMin` rather than a power +mean; see `Models.IncomeFluctuationCARA`. + +The shift `a` is the subsistence level of Stone and Geary. `a = 0` is CRRA exactly. + +## The result that matters + +`relativeRiskAversionLeOne_haraUtility`: HARA with a NONNEGATIVE shift and `γ ≤ 1` satisfies +Light's condition. Relative risk aversion of the shifted family is `γ c / (a + c)`, which for +`a ≥ 0` is at most `γ`; a negative shift (genuine subsistence) pushes it above `γ` and can break +the condition however small `γ` is, so the sign of the shift is not a technicality. + +The proof is derivative-free in the sense that matters: it factors + + `c · u' c = c ^ (1 - γ) · (c / (a + c)) ^ γ` + +into two nonnegative nondecreasing factors, which is exactly `u' c · c` nondecreasing, which is +relative risk aversion at most one. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +/-- Shifted CRRA: the `b ≠ 0` branch of HARA, with subsistence level `a`. -/ +noncomputable def haraUtility (γ a c : ℝ) : ℝ := crraUtility γ (a + c) + +@[simp] theorem haraUtility_zero_shift (γ : ℝ) : haraUtility γ 0 = crraUtility γ := by + funext c; simp [haraUtility] + +/-- **HARA**, as a class of functions: a positive affine transformation of a shifted CRRA. -/ +def IsHARA (u : ℝ → ℝ) : Prop := + ∃ γ a k l : ℝ, 0 < γ ∧ 0 < k ∧ ∀ c, u c = k * haraUtility γ a c + l + +theorem isHARA_haraUtility {γ : ℝ} (hγ : 0 < γ) (a : ℝ) : IsHARA (haraUtility γ a) := + ⟨γ, a, 1, 0, hγ, one_pos, fun c => by ring⟩ + +/-- **CRRA is HARA**, with no shift. -/ +theorem isHARA_crraUtility {γ : ℝ} (hγ : 0 < γ) : IsHARA (crraUtility γ) := by + simpa using isHARA_haraUtility hγ 0 + +/-- **Log utility is HARA**, as the `γ = 1` member. -/ +theorem isHARA_log : IsHARA Real.log := by + refine ⟨1, 0, 1, 0, one_pos, one_pos, fun c => ?_⟩ + simp [haraUtility] + +theorem hasDerivAt_haraUtility (γ : ℝ) {a c : ℝ} (hac : 0 < a + c) : + HasDerivAt (haraUtility γ a) ((a + c) ^ (-γ)) c := by + have h := (hasDerivAt_crraUtility γ hac).comp c ((hasDerivAt_id c).const_add a) + rw [mul_one] at h + exact h + +/-! ### Light's condition -/ + +/-- The factorisation that does the work: `c · u' c` splits into two nonnegative nondecreasing +pieces exactly when the shift is nonnegative and `γ ≤ 1`. -/ +theorem mul_rpow_neg_eq {γ a c : ℝ} (hc : 0 < c) (hac : 0 < a + c) : + c * (a + c) ^ (-γ) = c ^ (1 - γ) * (c / (a + c)) ^ γ := by + rw [Real.div_rpow hc.le hac.le, Real.rpow_neg hac.le] + rw [show c ^ (1 - γ) * (c ^ γ / (a + c) ^ γ) + = (c ^ (1 - γ) * c ^ γ) / (a + c) ^ γ from by ring] + rw [← Real.rpow_add hc, show (1 : ℝ) - γ + γ = 1 from by ring, Real.rpow_one] + field_simp + +/-- **HARA with a nonnegative shift and `γ ≤ 1` satisfies Light's condition.** -/ +theorem relativeRiskAversionLeOne_haraUtility {γ a : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) + (ha : 0 ≤ a) : RelativeRiskAversionLeOne (haraUtility γ a) := by + refine relativeRiskAversionLeOne_of_monotoneOn + (du := fun c => (a + c) ^ (-γ)) + (fun c hc => hasDerivAt_haraUtility γ (by have := mem_Ioi.mp hc; linarith)) ?_ + intro c₁ h₁ c₂ h₂ h₁₂ + have hc₁ : (0 : ℝ) < c₁ := h₁ + have hc₂ : (0 : ℝ) < c₂ := h₂ + have hac₁ : (0 : ℝ) < a + c₁ := by linarith + have hac₂ : (0 : ℝ) < a + c₂ := by linarith + change c₁ * (a + c₁) ^ (-γ) ≤ c₂ * (a + c₂) ^ (-γ) + rw [mul_rpow_neg_eq hc₁ hac₁, mul_rpow_neg_eq hc₂ hac₂] + -- the first factor rises because `1 - γ ≥ 0` + have hfst : c₁ ^ (1 - γ) ≤ c₂ ^ (1 - γ) := Real.rpow_le_rpow hc₁.le h₁₂ (by linarith) + -- the second because `c / (a + c)` rises when the shift is nonnegative + have hratio : c₁ / (a + c₁) ≤ c₂ / (a + c₂) := by + rw [div_le_div_iff₀ hac₁ hac₂] + nlinarith [ha, hc₁, h₁₂] + have hsnd : (c₁ / (a + c₁)) ^ γ ≤ (c₂ / (a + c₂)) ^ γ := + Real.rpow_le_rpow (by positivity) hratio hγ0.le + exact mul_le_mul hfst hsnd (by positivity) (by positivity) + +/-- The same for the whole class, since a positive affine transformation preserves convexity in +log consumption. -/ +theorem IsHARA.relativeRiskAversionLeOne {u : ℝ → ℝ} {γ a k l : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) + (ha : 0 ≤ a) (hk : 0 < k) (hu : ∀ c, u c = k * haraUtility γ a c + l) : + RelativeRiskAversionLeOne u := by + have hbase := relativeRiskAversionLeOne_haraUtility hγ0 hγ1 ha + have hfun : (fun t => u (Real.exp t)) + = fun t => k * haraUtility γ a (Real.exp t) + l := by + funext t; exact hu _ + simp only [RelativeRiskAversionLeOne, hfun] + exact (hbase.smul hk.le).add_const l + +/-- **A negative shift breaks it**, however small `γ` is: relative risk aversion `γ c / (a + c)` +exceeds one as consumption approaches the subsistence level from above. Recorded to show that +`0 ≤ a` in the theorem above is doing work. -/ +theorem haraUtility_rra_gt_one_of_neg_shift {γ a c : ℝ} (hac : 0 < a + c) + (hnear : (a + c) < γ * c) : + 1 < γ * c / (a + c) := by + rw [lt_div_iff₀ hac] + linarith + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean b/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean new file mode 100644 index 000000000..15f9d29b1 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean @@ -0,0 +1,324 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Analysis.MeanInequalities +public import Mathlib.Analysis.MeanInequalitiesPow +public import Mathlib.Analysis.Convex.SpecificFunctions.Basic + + +/-! +# Weighted power means with a negative exponent + +The analytic engine of Carroll and Kimball (1996). For CRRA utility `u' c = c ^ (-γ)` the +first-order condition of a consumption–saving problem reads + + `c = (β R) ^ (-1/γ) · (∑ π z' · c' z' ^ (-γ)) ^ (-1/γ)`, + +so the consumption chosen against a given stock of end-of-period assets is a positive multiple +of the weighted power mean, with exponent `-γ`, of next period's consumptions. Carroll and +Kimball's induction therefore needs exactly one fact: **a power mean with a negative exponent +is concave and nondecreasing**, so it carries concavity of next period's consumption functions +to concavity of this period's. + +## The proof + +`powerMean w p` is positively homogeneous of degree one, so concavity is the statement that the +set `{x > 0 : powerMean w p x ≥ 1}` is convex. With `p < 0` that set is `{x > 0 : ∑ w x ^ p ≤ 1}` +— a sublevel set of `∑ w x ^ p`, which is convex because `t ↦ t ^ p` is. So the whole thing comes +down to convexity of a single real function of one variable, and the scaling is bookkeeping. + +`convexOn_rpow_of_neg` supplies that one function: Mathlib has `x ^ p` convex for `1 ≤ p` and +concave for `0 < p < 1`, but not the negative case, and it is proved here from concavity of +`log` and convexity of `exp`. + +Kimball's risk-tolerance aggregation theorem is the differential form of the same fact. This is +the integrated form, and it needs no derivatives. +-/ + +@[expose] public section + +open Set Finset + +namespace LeanEconomics + +/-! ### `x ^ p` is convex for a negative exponent -/ + +/-- **A negative power is convex on the positive half-line.** `x ^ p = exp (p log x)`: `log` is +concave, multiplying by `p < 0` flips that to convex, and `exp` is convex and increasing. -/ +theorem convexOn_rpow_of_neg {p : ℝ} (hp : p < 0) : + ConvexOn ℝ (Ioi (0 : ℝ)) fun x : ℝ => x ^ p := by + refine ⟨convex_Ioi 0, fun x hx y hy a b ha hb hab => ?_⟩ + have hx0 : (0 : ℝ) < x := hx + have hy0 : (0 : ℝ) < y := hy + have hm : (0 : ℝ) < a * x + b * y := by + rcases eq_or_lt_of_le ha with h | h + · have hb1 : b = 1 := by linarith + rw [← h, hb1]; simpa using hy0 + · nlinarith + -- concavity of `log` + have hlog : a * Real.log x + b * Real.log y ≤ Real.log (a * x + b * y) := by + have := strictConcaveOn_log_Ioi.concaveOn.2 hx hy ha hb hab + simpa using this + -- multiplying by `p < 0` reverses it + have hstep : p * Real.log (a * x + b * y) ≤ a * (p * Real.log x) + b * (p * Real.log y) := by + nlinarith + calc (a * x + b * y) ^ p + = Real.exp (p * Real.log (a * x + b * y)) := by + rw [Real.rpow_def_of_pos hm]; ring_nf + _ ≤ Real.exp (a * (p * Real.log x) + b * (p * Real.log y)) := Real.exp_le_exp.mpr hstep + _ ≤ a * Real.exp (p * Real.log x) + b * Real.exp (p * Real.log y) := by + simpa using convexOn_exp.2 (mem_univ (p * Real.log x)) (mem_univ (p * Real.log y)) ha hb hab + _ = a • x ^ p + b • y ^ p := by + rw [Real.rpow_def_of_pos hx0, Real.rpow_def_of_pos hy0] + simp only [smul_eq_mul] + ring_nf + +/-! ### The power mean -/ + +variable {ι : Type*} [Fintype ι] + +/-- A convex combination of two positive numbers is positive. -/ +theorem pos_of_convex_comb {u v θ φ : ℝ} (hu : 0 < u) (hv : 0 < v) (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) + (hθφ : θ + φ = 1) : 0 < θ * u + φ * v := by + rcases eq_or_lt_of_le hθ with h | h + · have hφ1 : φ = 1 := by linarith + rw [← h, hφ1]; simpa using hv + · nlinarith + +/-- The weighted power mean of `x` with exponent `p`. -/ +noncomputable def powerMean (w : ι → ℝ) (p : ℝ) (x : ι → ℝ) : ℝ := + (∑ i, w i * x i ^ p) ^ (1 / p) + +variable {w : ι → ℝ} {p : ℝ} {x y : ι → ℝ} + +theorem sum_rpow_pos (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) (hx : ∀ i, 0 < x i) : + 0 < ∑ i, w i * x i ^ p := by + obtain ⟨i₀, -, hi₀⟩ : ∃ i ∈ Finset.univ, 0 < w i := by + by_contra h + simp only [not_exists, not_and, not_lt] at h + have hzero : ∑ i, w i = 0 := + Finset.sum_eq_zero fun i hi => le_antisymm (h i hi) (hw i) + rw [hw1] at hzero + norm_num at hzero + refine Finset.sum_pos' (fun i _ => mul_nonneg (hw i) (Real.rpow_nonneg (hx i).le _)) + ⟨i₀, Finset.mem_univ _, mul_pos hi₀ (Real.rpow_pos_of_pos (hx i₀) _)⟩ + +theorem powerMean_pos (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) (hx : ∀ i, 0 < x i) : + 0 < powerMean w p x := + Real.rpow_pos_of_pos (sum_rpow_pos hw hw1 hx) _ + +/-- The mean raised back to the exponent recovers the weighted sum. -/ +theorem powerMean_rpow (hp : p ≠ 0) (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) + (hx : ∀ i, 0 < x i) : powerMean w p x ^ p = ∑ i, w i * x i ^ p := by + have hS : 0 < ∑ i, w i * x i ^ p := sum_rpow_pos hw hw1 hx + simp only [powerMean] + rw [← Real.rpow_mul hS.le, one_div_mul_cancel hp, Real.rpow_one] + +/-- **The power mean is positively homogeneous of degree one.** -/ +theorem powerMean_smul (hp : p ≠ 0) {c : ℝ} (hc : 0 < c) (hw : ∀ i, 0 ≤ w i) + (hw1 : ∑ i, w i = 1) (hx : ∀ i, 0 < x i) : + powerMean w p (fun i => c * x i) = c * powerMean w p x := by + have hS : 0 < ∑ i, w i * x i ^ p := sum_rpow_pos hw hw1 hx + have hrw : ∑ i, w i * (c * x i) ^ p = c ^ p * ∑ i, w i * x i ^ p := by + rw [Finset.mul_sum] + refine Finset.sum_congr rfl fun i _ => ?_ + rw [Real.mul_rpow hc.le (hx i).le]; ring + simp only [powerMean, hrw] + rw [Real.mul_rpow (Real.rpow_nonneg hc.le _) hS.le, ← Real.rpow_mul hc.le, + mul_one_div_cancel hp, Real.rpow_one] + +/-- **Normalising by the mean makes the weighted sum exactly one.** This is the statement that +the level set `{powerMean = 1}` is where the scaling argument lands. -/ +theorem sum_rpow_div_powerMean (hp : p ≠ 0) (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) + (hx : ∀ i, 0 < x i) : ∑ i, w i * (x i / powerMean w p x) ^ p = 1 := by + have hS : 0 < ∑ i, w i * x i ^ p := sum_rpow_pos hw hw1 hx + have hM : 0 < powerMean w p x := powerMean_pos hw hw1 hx + have hrw : ∀ i, w i * (x i / powerMean w p x) ^ p + = (powerMean w p x ^ p)⁻¹ * (w i * x i ^ p) := by + intro i + rw [Real.div_rpow (hx i).le hM.le]; ring + rw [Finset.sum_congr rfl fun i _ => hrw i, ← Finset.mul_sum, powerMean_rpow hp hw hw1 hx] + exact inv_mul_cancel₀ hS.ne' + +/-- **A power mean with a negative exponent is nondecreasing.** -/ +theorem powerMean_mono (hp : p < 0) (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) + (hx : ∀ i, 0 < x i) (hy : ∀ i, 0 < y i) (hxy : ∀ i, x i ≤ y i) : + powerMean w p x ≤ powerMean w p y := by + have h1 : ∑ i, w i * y i ^ p ≤ ∑ i, w i * x i ^ p := + Finset.sum_le_sum fun i _ => mul_le_mul_of_nonneg_left + (Real.rpow_le_rpow_of_nonpos (hx i) (hxy i) hp.le) (hw i) + exact Real.rpow_le_rpow_of_nonpos (sum_rpow_pos hw hw1 hy) h1 + (le_of_lt (div_neg_of_pos_of_neg one_pos hp)) + +/-- **The gradient sum of a power mean is at most `M / min x`.** The quantity +`∑ w x^{-γ-1}`, which is what a common shift costs when propagated through a power mean, is bounded +termwise by `∑ w x^{-γ} / min x`. + +This is worth recording as a WARNING rather than a tool. Normalised, it says the gradient sum +`A = ∑ w (x/M)^{-γ-1}` is at most `M / min x` — and `M / min x` is exactly the multiplier that +bounding the expectation by its smallest term produces. Measured in the overlapping-generations +life cycle, `A` runs from `1.04` to `1.19` while `M / min x` runs from `4.7` to `5.9`, so this bound +discards the entire advantage of keeping the certainty equivalent. A usable bound on `A` has to see +the WEIGHTS — the far states carry little probability — and not merely the support. -/ +theorem sum_mul_rpow_pred_le {γ xmin : ℝ} (hw : ∀ i, 0 ≤ w i) (hx : ∀ i, 0 < x i) + (hmin : 0 < xmin) (hle : ∀ i, xmin ≤ x i) : + ∑ i, w i * x i ^ (-γ - 1) ≤ (∑ i, w i * x i ^ (-γ)) / xmin := by + rw [Finset.sum_div] + refine Finset.sum_le_sum fun i _ => ?_ + have hsplit : x i ^ (-γ - 1) = x i ^ (-γ) * (x i)⁻¹ := by + rw [show (-γ - 1 : ℝ) = (-γ) + (-1) from by ring, Real.rpow_add (hx i), Real.rpow_neg_one] + have hinv : (x i)⁻¹ ≤ xmin⁻¹ := by + simpa [one_div] using one_div_le_one_div_of_le hmin (hle i) + have hnn : 0 ≤ w i * x i ^ (-γ) := mul_nonneg (hw i) (Real.rpow_nonneg (hx i).le _) + calc w i * x i ^ (-γ - 1) = (w i * x i ^ (-γ)) * (x i)⁻¹ := by rw [hsplit]; ring + _ ≤ (w i * x i ^ (-γ)) * xmin⁻¹ := mul_le_mul_of_nonneg_left hinv hnn + _ = w i * x i ^ (-γ) / xmin := by rw [div_eq_mul_inv] + +/-- **The gradient sum is one plus the variance of the risk tilt.** Write `u i = (x i / M) ^ (-γ)` +for the tilt. By the definition of the power mean its weighted mean is exactly one, and then the +gradient sum `∑ w u ^ (1 + 1/γ)` is, at `γ = 1` where the exponent is exactly two, one plus the +weighted variance of `u`. + +This is the bound the minimum cannot give. `sum_mul_rpow_pred_le` sees only the support of `x` and +returns `M / min x`, measured at `4.7` to `5.9`; the variance sees the WEIGHTS, and is measured at +`0.041`. For `γ > 1` the exponent `1 + 1/γ` falls below two and Lyapunov's inequality gives +`∑ w u ^ (1+1/γ) ≤ (1 + Var)^{(γ+1)/(2γ)}`, which is an identity at `γ = 1` and loses accuracy +above it: measured `1.076` against a true `1.050` at `γ = 1.5`, and `1.26` against `1.08` at +`γ = 3`, against an allowance of about `1.06` throughout. So this route closes at logarithmic +utility and not above (`WriteUps/WriteUpOLG/numerics/lyap.m`). -/ +theorem sum_mul_sq_eq_one_add_variance {w u : ι → ℝ} (hw1 : ∑ i, w i = 1) + (hu : ∑ i, w i * u i = 1) : + ∑ i, w i * u i ^ 2 = 1 + ∑ i, w i * (u i - 1) ^ 2 := by + have hexp : ∀ i, w i * (u i - 1) ^ 2 = w i * u i ^ 2 - 2 * (w i * u i) + w i := by + intro i; ring + rw [Finset.sum_congr rfl fun i _ => hexp i] + rw [Finset.sum_add_distrib, Finset.sum_sub_distrib, ← Finset.mul_sum, hu, hw1] + ring + +/-- **The sharp moment bound.** For weights summing to one, `u ≥ 0` with `∑ w u = 1` and +`1 < p < 2`, `∑ w u^p ≤ (∑ w u²)^(p-1)`. It is Jensen for the concave `x ↦ x^(p-1)` under the +tilted weights `w u`, which sum to one by hypothesis, and it is attained by the two-point law on +`{0, ∑ w u²}`. With `p = 1 + 1/γ` it bounds the gradient sum of the two-rate chain by +`(1 + Var)^(1/γ)`, against Lyapunov's `(1 + Var)^((γ+1)/(2γ))`. -/ +theorem sum_mul_rpow_le_rpow_sum_mul_sq {w u : ι → ℝ} {p : ℝ} (hp1 : 1 < p) (hp2 : p < 2) + (hw : ∀ i, 0 ≤ w i) (hu : ∀ i, 0 ≤ u i) (hmean : ∑ i, w i * u i = 1) : + ∑ i, w i * u i ^ p ≤ (∑ i, w i * u i ^ 2) ^ (p - 1) := by + have hp0 : p - 1 ≠ 0 := sub_ne_zero.2 (ne_of_gt hp1) + have hq : (1 : ℝ) ≤ 1 / (p - 1) := by + rw [le_div_iff₀ (by linarith)]; linarith + have h := Real.arith_mean_le_rpow_mean Finset.univ (fun i => w i * u i) + (fun i => u i ^ (p - 1)) (fun i _ => mul_nonneg (hw i) (hu i)) hmean + (fun i _ => Real.rpow_nonneg (hu i) _) hq + have hL : ∀ i, w i * u i * u i ^ (p - 1) = w i * u i ^ p := by + intro i + have e : u i ^ p = u i ^ (1 + (p - 1)) := by congr 1; ring + rw [e, Real.rpow_add' (hu i) (by linarith : (1 : ℝ) + (p - 1) ≠ 0), Real.rpow_one] + ring + have hR : ∀ i, w i * u i * (u i ^ (p - 1)) ^ (1 / (p - 1)) = w i * u i ^ 2 := by + intro i + rw [one_div, Real.rpow_rpow_inv (hu i) hp0] + ring + simp only [hL, hR, one_div_one_div] at h + exact h + +/-- **A power mean with a negative exponent is concave.** + +Homogeneity turns concavity into the convexity of `{x > 0 : ∑ w x ^ p ≤ 1}`, and that is a +sublevel set of a convex function. This is Kimball's risk-tolerance aggregation in integrated +form: no derivative appears anywhere. -/ +theorem powerMean_concave (hp : p < 0) (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) + (hx : ∀ i, 0 < x i) (hy : ∀ i, 0 < y i) + {θ φ : ℝ} (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) (hθφ : θ + φ = 1) : + θ * powerMean w p x + φ * powerMean w p y + ≤ powerMean w p (fun i => θ * x i + φ * y i) := by + have hp0 : p ≠ 0 := ne_of_lt hp + have hA : 0 < powerMean w p x := powerMean_pos hw hw1 hx + have hB : 0 < powerMean w p y := powerMean_pos hw hw1 hy + set A := powerMean w p x with hAdef + set B := powerMean w p y with hBdef + set d : ℝ := θ * A + φ * B with hddef + have hd : 0 < d := pos_of_convex_comb hA hB hθ hφ hθφ + have hz : ∀ i, 0 < θ * x i + φ * y i := fun i => pos_of_convex_comb (hx i) (hy i) hθ hφ hθφ + -- the weights that split the normalised combination + set μ : ℝ := θ * A / d with hμdef + set ν : ℝ := φ * B / d with hνdef + have hμ : 0 ≤ μ := by rw [hμdef]; positivity + have hν : 0 ≤ ν := by rw [hνdef]; positivity + have hμν : μ + ν = 1 := by + have hsum : μ + ν = (θ * A + φ * B) / d := by rw [hμdef, hνdef]; ring + rw [hsum, ← hddef, div_self hd.ne'] + -- convexity of `t ^ p`, one coordinate at a time + have hpt : ∀ i, ((θ * x i + φ * y i) / d) ^ p ≤ μ * (x i / A) ^ p + ν * (y i / B) ^ p := by + intro i + have hkey : (θ * x i + φ * y i) / d = μ * (x i / A) + ν * (y i / B) := by + rw [hμdef, hνdef]; field_simp + rw [hkey] + have := (convexOn_rpow_of_neg hp).2 (show x i / A ∈ Ioi (0 : ℝ) by + simpa using div_pos (hx i) hA) + (show y i / B ∈ Ioi (0 : ℝ) by simpa using div_pos (hy i) hB) hμ hν hμν + simpa using this + -- summing, the normalised combination sits inside the unit sublevel set + have hsum : ∑ i, w i * ((θ * x i + φ * y i) / d) ^ p ≤ 1 := by + calc ∑ i, w i * ((θ * x i + φ * y i) / d) ^ p + ≤ ∑ i, w i * (μ * (x i / A) ^ p + ν * (y i / B) ^ p) := + Finset.sum_le_sum fun i _ => mul_le_mul_of_nonneg_left (hpt i) (hw i) + _ = μ * (∑ i, w i * (x i / A) ^ p) + ν * ∑ i, w i * (y i / B) ^ p := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + exact Finset.sum_congr rfl fun i _ => by ring + _ = 1 := by + rw [hAdef, hBdef, sum_rpow_div_powerMean hp0 hw hw1 hx, + sum_rpow_div_powerMean hp0 hw hw1 hy, mul_one, mul_one, hμν] + -- and the mean of a vector in that set is at least one + have hT : 0 < ∑ i, w i * ((θ * x i + φ * y i) / d) ^ p := + sum_rpow_pos hw hw1 fun i => div_pos (hz i) hd + have hge : (1 : ℝ) ≤ powerMean w p (fun i => (θ * x i + φ * y i) / d) := + Real.one_le_rpow_of_pos_of_le_one_of_nonpos hT hsum + (le_of_lt (div_neg_of_pos_of_neg one_pos hp)) + -- rescale + have hscale : powerMean w p (fun i => θ * x i + φ * y i) + = d * powerMean w p (fun i => (θ * x i + φ * y i) / d) := by + rw [← powerMean_smul hp0 hd hw hw1 fun i => div_pos (hz i) hd] + congr 1 + funext i + field_simp + rw [hscale] + calc d = d * 1 := (mul_one d).symm + _ ≤ d * powerMean w p (fun i => (θ * x i + φ * y i) / d) := + mul_le_mul_of_nonneg_left hge hd.le + +/-- **The Carroll–Kimball step.** A negative power mean of concave positive functions is +concave: concavity of next period's consumption functions carries to the consumption chosen +against a given stock of end-of-period assets. -/ +theorem concaveOn_powerMean_comp {D : Set ℝ} (hD : Convex ℝ D) (hp : p < 0) + (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) {f : ι → ℝ → ℝ} + (hpos : ∀ i, ∀ a ∈ D, 0 < f i a) (hconc : ∀ i, ConcaveOn ℝ D (f i)) : + ConcaveOn ℝ D (fun a => powerMean w p (fun i => f i a)) := by + refine ⟨hD, fun a ha b hb θ φ hθ hφ hθφ => ?_⟩ + have hm : θ • a + φ • b ∈ D := hD ha hb hθ hφ hθφ + have hstep : ∀ i, θ * f i a + φ * f i b ≤ f i (θ • a + φ • b) := by + intro i + have := (hconc i).2 ha hb hθ hφ hθφ + simpa using this + have hmid : ∀ i, 0 < θ * f i a + φ * f i b := fun i => + pos_of_convex_comb (hpos i a ha) (hpos i b hb) hθ hφ hθφ + calc θ • powerMean w p (fun i => f i a) + φ • powerMean w p (fun i => f i b) + = θ * powerMean w p (fun i => f i a) + φ * powerMean w p (fun i => f i b) := by + simp only [smul_eq_mul] + _ ≤ powerMean w p (fun i => θ * f i a + φ * f i b) := + powerMean_concave hp hw hw1 (fun i => hpos i a ha) (fun i => hpos i b hb) hθ hφ hθφ + _ ≤ powerMean w p (fun i => f i (θ • a + φ • b)) := + powerMean_mono hp hw hw1 hmid (fun i => hpos i _ hm) hstep + +/-- **And nondecreasing**, which the endogenous-gridpoint transfer also needs. -/ +theorem monotoneOn_powerMean_comp {D : Set ℝ} (hp : p < 0) + (hw : ∀ i, 0 ≤ w i) (hw1 : ∑ i, w i = 1) {f : ι → ℝ → ℝ} + (hpos : ∀ i, ∀ a ∈ D, 0 < f i a) (hmono : ∀ i, MonotoneOn (f i) D) : + MonotoneOn (fun a => powerMean w p (fun i => f i a)) D := fun a ha b hb hab => + powerMean_mono hp hw hw1 (fun i => hpos i a ha) (fun i => hpos i b hb) + fun i => hmono i ha hb hab + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean new file mode 100644 index 000000000..cea6a6f9f --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean @@ -0,0 +1,303 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements +public import Mathlib.Analysis.Convex.Deriv +public import Mathlib.Analysis.SpecialFunctions.Pow.Deriv + + +/-! +# Relative risk aversion at most one + +Light (2018) proves that individual saving rises with the interest rate — his Theorem 1, the +household half of the uniqueness argument — under the assumption that `u'(c) · c` is +non-decreasing. With `u' > 0` that is exactly relative risk aversion `-c u''/u' ≤ 1`, because + + `d/dc [c u'(c)] = u'(c) · (1 - RRA(c))`. + +Economically it is the condition under which the substitution effect of a higher interest rate +dominates the income effect. It is the boundary of the region where the model is known to have +a unique equilibrium: Açıkgöz (2018) exhibits multiplicity at `γ = 6.5`, and Walsh and Young +(2025) report never seeing multiplicity below about `γ = 1.49`. + +## Stating it without derivatives + +`u'(c) · c` is the derivative of `t ↦ u(eᵗ)`, so the assumption says precisely that **`u` is +convex as a function of log consumption**, and that is how it is defined here. The definition +needs no differentiability, and neither does its main consequence: increments of `u` across a +fixed PROPORTIONAL change in consumption grow as consumption grows, + + `u(α c₁) - u(c₁) ≤ u(α c₂) - u(c₂)` for `0 < c₁ ≤ c₂` and `α ≥ 1`, + +which is `sub_le_sub_of_scale` below. This is the multiplicative twin of +`ConcaveOn.sub_le_sub_of_shift`, and it is the form the comparative-statics argument uses, since +a change in the interest rate scales the return on saving rather than shifting it. + +Where derivatives are available the two formulations agree, and `relativeRiskAversionLeOne_iff` +proves that. The examples at the end pin the boundary down: CRRA satisfies the condition exactly +when `γ ≤ 1`, log utility sits on the boundary (convex in log consumption because it is AFFINE +there), and for `γ > 1` it fails — indeed `u ∘ exp` is then strictly concave, so the condition +fails as badly as it can. +-/ + +@[expose] public section + +open Set + +namespace LeanEconomics + +/-- **Relative risk aversion at most one**, stated without derivatives: `u` is convex as a +function of log consumption. Where `u` is differentiable with `u' > 0` this is equivalent to +`-c u''(c) / u'(c) ≤ 1`; see `relativeRiskAversionLeOne_iff`. -/ +def RelativeRiskAversionLeOne (u : ℝ → ℝ) : Prop := + ConvexOn ℝ univ fun t => u (Real.exp t) + +/-- **The derivative-free content of `u'(c) · c` increasing.** The gain from a fixed +PROPORTIONAL rise in consumption is larger at higher consumption. + +This is the multiplicative counterpart of `ConcaveOn.sub_le_sub_of_shift`, and it is the form +the comparative statics uses: a higher interest rate scales the payoff to saving. -/ +theorem RelativeRiskAversionLeOne.sub_le_sub_of_scale {u : ℝ → ℝ} + (hu : RelativeRiskAversionLeOne u) {c₁ c₂ α : ℝ} (h₁ : 0 < c₁) (h₁₂ : c₁ ≤ c₂) (hα : 1 ≤ α) : + u (α * c₁) - u c₁ ≤ u (α * c₂) - u c₂ := by + have h₂ : 0 < c₂ := lt_of_lt_of_le h₁ h₁₂ + have hα0 : 0 < α := lt_of_lt_of_le one_pos hα + have hconv : ConvexOn ℝ (univ : Set ℝ) fun t => u (Real.exp t) := hu + -- the concave shift lemma applied to the negation, in log consumption + have hcon : ConcaveOn ℝ (univ : Set ℝ) (-fun t => u (Real.exp t)) := hconv.neg + have hlog : Real.log c₁ ≤ Real.log c₂ := Real.log_le_log h₁ h₁₂ + have hΔ : 0 ≤ Real.log α := Real.log_nonneg hα + have key := hcon.sub_le_sub_of_shift (mem_univ (Real.log c₁)) + (mem_univ (Real.log c₂ + Real.log α)) hlog hΔ + have e1 : Real.exp (Real.log c₁ + Real.log α) = α * c₁ := by + rw [Real.exp_add, Real.exp_log h₁, Real.exp_log hα0]; ring + have e2 : Real.exp (Real.log c₂ + Real.log α) = α * c₂ := by + rw [Real.exp_add, Real.exp_log h₂, Real.exp_log hα0]; ring + simp only [Pi.neg_apply, e1, e2, Real.exp_log h₁, Real.exp_log h₂] at key + linarith + +/-! ### The equivalence with `u'(c) · c` increasing -/ + +/-- Where `u` is differentiable on `(0, ∞)`, `t ↦ u(eᵗ)` is differentiable with derivative +`eᵗ u'(eᵗ)` — which is `u'(c) · c` read at `c = eᵗ`. -/ +theorem hasDerivAt_comp_exp {u du : ℝ → ℝ} (hd : ∀ c ∈ Ioi (0 : ℝ), HasDerivAt u (du c) c) + (t : ℝ) : + HasDerivAt (fun s => u (Real.exp s)) (Real.exp t * du (Real.exp t)) t := by + have h := (hd _ (mem_Ioi.mpr (Real.exp_pos t))).comp t (Real.hasDerivAt_exp t) + simpa [Function.comp_def, mul_comm] using h + +/-- If `u'(c) · c` is non-decreasing then relative risk aversion is at most one. -/ +theorem relativeRiskAversionLeOne_of_monotoneOn {u du : ℝ → ℝ} + (hd : ∀ c ∈ Ioi (0 : ℝ), HasDerivAt u (du c) c) + (hm : MonotoneOn (fun c => c * du c) (Ioi 0)) : + RelativeRiskAversionLeOne u := by + refine Monotone.convexOn_univ_of_deriv + (fun t => (hasDerivAt_comp_exp hd t).differentiableAt) ?_ + intro s t hst + rw [(hasDerivAt_comp_exp hd s).deriv, (hasDerivAt_comp_exp hd t).deriv] + exact hm (mem_Ioi.mpr (Real.exp_pos s)) (mem_Ioi.mpr (Real.exp_pos t)) (Real.exp_le_exp.mpr hst) + +/-- Conversely, relative risk aversion at most one makes `u'(c) · c` non-decreasing. -/ +theorem RelativeRiskAversionLeOne.monotoneOn_mul_deriv {u du : ℝ → ℝ} + (hu : RelativeRiskAversionLeOne u) (hd : ∀ c ∈ Ioi (0 : ℝ), HasDerivAt u (du c) c) : + MonotoneOn (fun c => c * du c) (Ioi 0) := by + have hconv : ConvexOn ℝ (univ : Set ℝ) fun t => u (Real.exp t) := hu + intro a ha b hb hab + rcases eq_or_lt_of_le hab with rfl | hlt + · exact le_rfl + have ha0 : 0 < a := mem_Ioi.mp ha + have hb0 : 0 < b := mem_Ioi.mp hb + have hlog : Real.log a < Real.log b := Real.log_lt_log ha0 hlt + -- the derivative at the left end of a secant is below its slope, and at the right end above + have h1 := hconv.le_slope_of_hasDerivAt (mem_univ _) (mem_univ _) hlog + (hasDerivAt_comp_exp hd (Real.log a)) + have h2 := hconv.slope_le_of_hasDerivAt (mem_univ _) (mem_univ _) hlog + (hasDerivAt_comp_exp hd (Real.log b)) + rw [Real.exp_log ha0] at h1 + rw [Real.exp_log hb0] at h2 + exact h1.trans h2 + +/-- **Relative risk aversion at most one is exactly `u'(c) · c` non-decreasing.** -/ +theorem relativeRiskAversionLeOne_iff {u du : ℝ → ℝ} + (hd : ∀ c ∈ Ioi (0 : ℝ), HasDerivAt u (du c) c) : + RelativeRiskAversionLeOne u ↔ MonotoneOn (fun c => c * du c) (Ioi 0) := + ⟨fun h => h.monotoneOn_mul_deriv hd, relativeRiskAversionLeOne_of_monotoneOn hd⟩ + +/-! ### Where the boundary is + +CRRA utility in log consumption is `eᵏᵗ / k` with `k = 1 - γ`, so everything below turns on the +sign of `k`. -/ + +/-- CRRA utility written in log consumption. -/ +theorem crra_comp_exp (k : ℝ) : + (fun t => Real.exp t ^ k / k) = fun t => Real.exp (k * t) / k := by + funext t + rw [← Real.exp_mul, mul_comm] + +theorem hasDerivAt_expScaled {k : ℝ} (hk : k ≠ 0) (t : ℝ) : + HasDerivAt (fun s => Real.exp (k * s) / k) (Real.exp (k * t)) t := by + have h : HasDerivAt (fun s => Real.exp (k * s)) (Real.exp (k * t) * k) t := by + simpa using ((hasDerivAt_id t).const_mul k).exp + simpa [mul_div_assoc, div_self hk] using h.div_const k + +theorem deriv_expScaled {k : ℝ} (hk : k ≠ 0) : + deriv (fun s => Real.exp (k * s) / k) = fun t => Real.exp (k * t) := + funext fun t => (hasDerivAt_expScaled hk t).deriv + +/-- **CRRA with `γ < 1` has relative risk aversion at most one.** In log consumption the +utility is `e^{(1-γ)t} / (1-γ)`, whose derivative `e^{(1-γ)t}` rises because `1 - γ > 0`. -/ +theorem relativeRiskAversionLeOne_crra {γ : ℝ} (hγ : γ < 1) : + RelativeRiskAversionLeOne fun c => c ^ (1 - γ) / (1 - γ) := by + have hk : 0 < 1 - γ := by linarith + change ConvexOn ℝ univ fun t => Real.exp t ^ (1 - γ) / (1 - γ) + rw [crra_comp_exp] + refine Monotone.convexOn_univ_of_deriv + (fun t => (hasDerivAt_expScaled hk.ne' t).differentiableAt) ?_ + rw [deriv_expScaled hk.ne'] + exact fun s t hst => Real.exp_le_exp.mpr (by nlinarith) + +/-- **Log utility sits exactly on the boundary**: in log consumption it is the identity, which +is affine, hence convex — but only barely, since it is concave too. Log utility is `γ = 1`. -/ +theorem relativeRiskAversionLeOne_log : RelativeRiskAversionLeOne Real.log := by + have h : (fun t => Real.log (Real.exp t)) = _root_.id := by funext t; simp + change ConvexOn ℝ univ fun t => Real.log (Real.exp t) + rw [h] + exact convexOn_id convex_univ + +/-- **CRRA with `γ > 1` fails, and fails strictly.** In log consumption the utility is +`e^{(1-γ)t} / (1-γ)` with a negative exponent and a negative divisor, whose derivative +`e^{(1-γ)t}` strictly falls, so it is strictly CONCAVE in log consumption. + +This is the reason the uniqueness literature stops at `γ ≤ 1`: beyond it the household's +response to the interest rate is not signed by this argument, and Açıkgöz (2018) and Walsh and +Young (2025) find calibrations where capital supply genuinely bends back. -/ +theorem strictConcaveOn_crra_comp_exp {γ : ℝ} (hγ : 1 < γ) : + StrictConcaveOn ℝ univ fun t => (Real.exp t : ℝ) ^ (1 - γ) / (1 - γ) := by + have hk : 1 - γ < 0 := by linarith + rw [crra_comp_exp] + refine StrictAnti.strictConcaveOn_univ_of_deriv + (Continuous.div_const (by fun_prop) _) ?_ + rw [deriv_expScaled hk.ne] + exact fun s t hst => Real.exp_lt_exp.mpr (by nlinarith) + +theorem not_relativeRiskAversionLeOne_crra {γ : ℝ} (hγ : 1 < γ) : + ¬ RelativeRiskAversionLeOne fun c => c ^ (1 - γ) / (1 - γ) := by + intro h + have hconv : ConvexOn ℝ (univ : Set ℝ) fun t => Real.exp t ^ (1 - γ) / (1 - γ) := h + have hconc := strictConcaveOn_crra_comp_exp hγ + -- the midpoint of `0` and `1` separates them + have h1 := hconv.2 (mem_univ (0 : ℝ)) (mem_univ (1 : ℝ)) (by norm_num : (0:ℝ) ≤ 1/2) + (by norm_num : (0:ℝ) ≤ 1/2) (by norm_num) + have h2 := hconc.2 (mem_univ (0 : ℝ)) (mem_univ (1 : ℝ)) (by norm_num : (0:ℝ) ≠ 1) + (by norm_num : (0:ℝ) < 1/2) (by norm_num : (0:ℝ) < 1/2) (by norm_num) + simp only [smul_eq_mul] at h1 h2 + linarith + +/-! ### The step of Light's Theorem 1 that all of this serves + +Light's Theorem 1 compares a household facing gross return `α₁` with one facing `α₂ ≥ α₁`, and +its pivotal inequality is that the marginal value of wealth, `α · u'(c)`, is larger at the +higher return. Written out, `α u'(c) = (α / c) · (u'(c) · c)`, and the two factors are exactly +the two conditions: `α / c(α x)` rises with `α` because the consumption function is CONCAVE +(Lemma 3, `ConcaveOn.div_le_div_of_scale`), and `u'(c) c` rises with `c` because relative risk +aversion is at most one. Neither factor alone signs the product; splitting it this way is the +whole trick. + +Note what the consumption function has to supply: concavity, positivity at zero assets, and +monotonicity. The first is Light's Lemma 4 and is the deep one — he cites Jensen (2017), and it +is a Carroll–Kimball result that needs more than concavity of `u` and of the value function. +-/ + +/-- **The pivotal inequality of Light (2018) Theorem 1.** For a concave, positive, increasing +consumption function `c` and marginal utility `du` with relative risk aversion at most one, the +marginal value of wealth `α · u'(c(α x))` is non-decreasing in the gross return `α`. -/ +theorem mul_marginal_le_of_scale {c du : ℝ → ℝ} {s : Set ℝ} + (hc : ConcaveOn ℝ s c) (h0 : (0 : ℝ) ∈ s) (hc0 : 0 < c 0) (hmono : MonotoneOn c s) + (hrra : MonotoneOn (fun y => y * du y) (Ioi 0)) (hdu : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + {α₁ α₂ x : ℝ} (h1 : 0 < α₁) (h12 : α₁ ≤ α₂) (hx : 0 < x) + (hm1 : α₁ * x ∈ s) (hm2 : α₂ * x ∈ s) : + α₁ * du (c (α₁ * x)) ≤ α₂ * du (c (α₂ * x)) := by + have h2 : 0 < α₂ := lt_of_lt_of_le h1 h12 + have hp1 : 0 < c (α₁ * x) := lt_of_lt_of_le hc0 (hmono h0 hm1 (by positivity)) + have hp2 : 0 < c (α₂ * x) := lt_of_lt_of_le hc0 (hmono h0 hm2 (by positivity)) + have hcle : c (α₁ * x) ≤ c (α₂ * x) := + hmono hm1 hm2 (mul_le_mul_of_nonneg_right h12 hx.le) + -- the two factors, each signed by one of the two hypotheses + have hdiv := hc.div_le_div_of_scale h0 hc0 h1 h12 hx hm2 hp2 + have hprod := hrra (mem_Ioi.mpr hp1) (mem_Ioi.mpr hp2) hcle + have hnn : 0 ≤ c (α₁ * x) * du (c (α₁ * x)) := mul_nonneg hp1.le (hdu _ (mem_Ioi.mpr hp1)) + have hmul := mul_le_mul hdiv hprod hnn (div_pos h2 hp2).le + have e1 : α₁ / c (α₁ * x) * (c (α₁ * x) * du (c (α₁ * x))) = α₁ * du (c (α₁ * x)) := by + field_simp + have e2 : α₂ / c (α₂ * x) * (c (α₂ * x) * du (c (α₂ * x))) = α₂ * du (c (α₂ * x)) := by + field_simp + rwa [e1, e2] at hmul + +/-! ### Beyond relative risk aversion one + +For `u'(c) = c^{-γ}` with `γ > 1`, `c · u'(c) = c^{1-γ}` FALLS, so the second factor above goes +the wrong way — but only by the factor `(c(α₂x)/c(α₁x))^{γ-1}`, and the first factor bounds that +ratio: concavity through a positive value at zero gives the chord bound +`c(α₂x)/c(α₁x) ≤ 1 + (1 - c(0)/c(α₁x))(α₂/α₁ - 1)`. So the pivotal inequality survives wherever + + `(1 - c(0)/c(α₁x)) · (ρ - 1) ≤ ρ^{1/γ} - 1`, `ρ = α₂/α₁`, + +which for returns close together says consumption at `α₁x` is at most about `γ/(γ-1)` times +consumption at zero assets. For `γ ≤ 1` the condition is automatic, since `ρ^{1/γ} ≥ ρ`; +for `γ > 1` it is a restriction on the RANGE of the consumption function, not on the utility. -/ + +/-- **The chord bound.** A concave function on a set containing `0` grows past `y₁` at most at +the average rate it had over `[0, y₁]`. -/ +theorem ConcaveOn.le_add_chord {c : ℝ → ℝ} {s : Set ℝ} (hc : ConcaveOn ℝ s c) (h0 : (0 : ℝ) ∈ s) + {y₁ y₂ : ℝ} (hy1 : 0 < y₁) (hy12 : y₁ < y₂) (hm2 : y₂ ∈ s) : + c y₂ ≤ c y₁ + (c y₁ - c 0) * ((y₂ - y₁) / y₁) := by + have h := hc.slope_anti_adjacent h0 hm2 hy1 hy12 + rw [sub_zero] at h + have hy21 : 0 < y₂ - y₁ := by linarith + rw [div_le_div_iff₀ hy21 hy1] at h + have e : (c y₁ - c 0) * ((y₂ - y₁) / y₁) = (c y₁ - c 0) * (y₂ - y₁) / y₁ := by ring + rw [e, ← sub_le_iff_le_add', le_div_iff₀ hy1] + exact h + +/-- **Light's pivotal inequality beyond relative risk aversion one**, for `u'(c) = c^{-γ}` and +any `γ > 0`: `α₁ · c(α₁x)^{-γ} ≤ α₂ · c(α₂x)^{-γ}` for `α₂ = ρ α₁ ≥ α₁`, provided +`(1 - c(0)/c(α₁x))(ρ - 1) ≤ ρ^{1/γ} - 1`. -/ +theorem mul_rpow_neg_le_of_scale {c : ℝ → ℝ} {s : Set ℝ} {γ : ℝ} (hγ : 0 < γ) + (hc : ConcaveOn ℝ s c) (h0 : (0 : ℝ) ∈ s) (hc0 : 0 < c 0) (hmono : MonotoneOn c s) + {α₁ ρ x : ℝ} (h1 : 0 < α₁) (hρ : 1 ≤ ρ) (hx : 0 < x) + (hm1 : α₁ * x ∈ s) (hm2 : ρ * α₁ * x ∈ s) + (hθ : (1 - c 0 / c (α₁ * x)) * (ρ - 1) ≤ ρ ^ (1 / γ) - 1) : + α₁ * (c (α₁ * x)) ^ (-γ) ≤ ρ * α₁ * (c (ρ * α₁ * x)) ^ (-γ) := by + have hρ0 : 0 < ρ := by linarith + have hp1 : 0 < c (α₁ * x) := lt_of_lt_of_le hc0 (hmono h0 hm1 (by positivity)) + have hp2 : 0 < c (ρ * α₁ * x) := lt_of_lt_of_le hc0 (hmono h0 hm2 (by positivity)) + -- the ratio of consumptions is at most `ρ^{1/γ}` + have hratio : c (ρ * α₁ * x) ≤ ρ ^ (1 / γ) * c (α₁ * x) := by + rcases eq_or_lt_of_le hρ with hρ1 | hρ1 + · subst hρ1 + simp + · have hlt : α₁ * x < ρ * α₁ * x := by + have hpos : 0 < (ρ - 1) * (α₁ * x) := mul_pos (by linarith) (mul_pos h1 hx) + linarith [hpos] + have hchord := ConcaveOn.le_add_chord hc h0 (by positivity : 0 < α₁ * x) hlt hm2 + have e : (ρ * α₁ * x - α₁ * x) / (α₁ * x) = ρ - 1 := by field_simp + rw [e] at hchord + have hθ' := mul_le_mul_of_nonneg_right hθ hp1.le + have e2 : (1 - c 0 / c (α₁ * x)) * (ρ - 1) * c (α₁ * x) + = (c (α₁ * x) - c 0) * (ρ - 1) := by field_simp + rw [e2] at hθ' + linarith + -- hence `c₂^γ ≤ ρ c₁^γ`, which is the claim + have hpow : (c (ρ * α₁ * x)) ^ γ ≤ ρ * (c (α₁ * x)) ^ γ := by + have := Real.rpow_le_rpow hp2.le hratio hγ.le + rwa [Real.mul_rpow (by positivity) hp1.le, ← Real.rpow_mul hρ0.le, + one_div_mul_cancel hγ.ne', Real.rpow_one] at this + rw [Real.rpow_neg hp1.le, Real.rpow_neg hp2.le, ← div_eq_mul_inv, ← div_eq_mul_inv, + div_le_div_iff₀ (Real.rpow_pos_of_pos hp1 _) (Real.rpow_pos_of_pos hp2 _)] + nlinarith [mul_le_mul_of_nonneg_left hpow h1.le] + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean b/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean new file mode 100644 index 000000000..90223ef45 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean @@ -0,0 +1,280 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import Mathlib.MeasureTheory.Measure.ProbabilityMeasure +public import Mathlib.MeasureTheory.Integral.Bochner.ContinuousLinearMap +public import Mathlib.MeasureTheory.Integral.Bochner.SumMeasure + + +/-! +# The agent distribution: state space and Markov operator + +Step one towards a stationary agent distribution for the income fluctuation problem. + +## The compact state space + +The model is posed with state space `ℝ × Z`, but a distribution has to live somewhere +compact -- Prokhorov's theorem, which supplies the compactness of the space of probability +measures, needs a compact underlying space. The household never leaves +`[0, assetCap] × Z`, so that is where the distribution lives, and `State` is that region as +a space in its own right rather than as a subset of a larger one. + +Taking it as a subtype is forced. A *subset* of `ℝ × Z` would not be a compact space, and +the earlier concavity work could not use a subtype because it is not a module over `ℝ`. +Both choices are right in their own place: concavity needs the linear structure of the +ambient space, the distribution needs compactness of the region. + +## The Markov operator + +`markovOp h s` is the expected value of `h` at next period's state, given that the household +is at `s` today and follows its optimal policy. The content of the definition is that it +lands in the *bounded continuous* functions again: + +* continuity is the **Feller property**, and comes from continuity of the optimal policy, + which is what the whole concavity and uniqueness development was for; +* boundedness is immediate from `∑ prob = 1`. + +Feller is what will make the induced map on probability measures continuous, and continuity +of that map is what a fixed point argument needs. + +## The operator on measures + +`push μ` is where the distribution `μ` is a period later. It is assembled from +`Measure.withDensity` and `Measure.map` rather than from `Measure.bind`: Mathlib has +`lintegral_bind` but no Bochner `integral_bind`, whereas both of the former have their +Bochner lemmas, and it is the Bochner integral that the weak topology is stated with. + +`integral_push` is the duality `∫ h d(push μ) = ∫ (markovOp h) dμ`, and it is what makes +the whole thing work: weak convergence is tested against bounded continuous functions, so +weak continuity of `push` reduces immediately to `markovOp h` being one. +-/ + +@[expose] public section + +open scoped NNReal ENNReal +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The compact region the agent distribution lives on: assets in `[0, assetCap]` paired +with an income state. -/ +abbrev State (_P : IncomeFluctuation Z assetFloor assetCap) : Type _ := + ↥(Icc assetFloor assetCap) × Z + +instance : CompactSpace P.State := + haveI : CompactSpace ↥(Icc assetFloor assetCap) := isCompact_iff_compactSpace.mp isCompact_Icc + inferInstanceAs (CompactSpace (_ × _)) + +/-- The region, read back as a state of the original model. -/ +def incl (s : P.State) : ℝ × Z := ((s.1 : ℝ), s.2) + +theorem continuous_incl : Continuous P.incl := + (continuous_subtype_val.comp continuous_fst).prodMk continuous_snd + +theorem incl_mem (s : P.State) : (P.incl s).1 ∈ Icc assetFloor assetCap := s.1.2 + +/-- Next period's assets under the optimal policy, as a point of the region. -/ +noncomputable def nextAssets (s : P.State) : ↥(Icc assetFloor assetCap) := + ⟨P.policy (P.incl s), P.policy_mem_region _⟩ + +/-- Next period's state if the income shock `z'` occurs. -/ +noncomputable def nextState (s : P.State) (z' : Z) : P.State := (P.nextAssets s, z') + +/-- **The policy moves the household continuously within the region.** This is the Feller +input, and it is exactly what the concavity and uniqueness development bought. -/ +theorem continuous_nextAssets : Continuous P.nextAssets := + (P.continuousOn_policy.comp_continuous P.continuous_incl P.incl_mem).subtype_mk _ + +theorem continuous_nextState (z' : Z) : Continuous fun s => P.nextState s z' := + P.continuous_nextAssets.prodMk continuous_const + +/-- The probability of moving to income state `z'`, as a function of today's state. -/ +noncomputable def prob (s : P.State) (z' : Z) : ℝ := P.transitionMatrix s.2 z' + +theorem prob_nonneg (s : P.State) (z' : Z) : 0 ≤ P.prob s z' := P.transitionMatrix_nonneg _ _ + +theorem prob_sum (s : P.State) : ∑ z', P.prob s z' = 1 := P.transitionMatrix_sum _ + +theorem continuous_prob (z' : Z) : Continuous fun s => P.prob s z' := + (continuous_of_discreteTopology (f := fun z => P.transitionMatrix z z')).comp continuous_snd + +/-- The expected value of `h` next period, as a bare function. -/ +noncomputable def markovFn (h : P.State →ᵇ ℝ) (s : P.State) : ℝ := + ∑ z', P.prob s z' * h (P.nextState s z') + +theorem continuous_markovFn (h : P.State →ᵇ ℝ) : Continuous (P.markovFn h) := + continuous_finsetSum _ fun z' _ => + (P.continuous_prob z').mul (h.continuous.comp (P.continuous_nextState z')) + +theorem abs_markovFn_le (h : P.State →ᵇ ℝ) (s : P.State) : |P.markovFn h s| ≤ ‖h‖ := by + calc |P.markovFn h s| ≤ ∑ z', |P.prob s z' * h (P.nextState s z')| := + Finset.abs_sum_le_sum_abs _ _ + _ = ∑ z', P.prob s z' * |h (P.nextState s z')| := by + refine Finset.sum_congr rfl fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.prob_nonneg s z')] + _ ≤ ∑ z', P.prob s z' * ‖h‖ := by + refine Finset.sum_le_sum fun z' _ => mul_le_mul_of_nonneg_left ?_ (P.prob_nonneg s z') + simpa [Real.norm_eq_abs] using h.norm_coe_le_norm (P.nextState s z') + _ = ‖h‖ := by rw [← Finset.sum_mul, P.prob_sum, one_mul] + +/-- **The Markov operator.** `markovOp h` is the expected value of `h` next period. That it +is again bounded and *continuous* is the Feller property. -/ +noncomputable def markovOp (h : P.State →ᵇ ℝ) : P.State →ᵇ ℝ := + BoundedContinuousFunction.ofNormedAddCommGroup (P.markovFn h) (P.continuous_markovFn h) ‖h‖ + fun s => by simpa [Real.norm_eq_abs] using P.abs_markovFn_le h s + +@[simp] +theorem markovOp_apply (h : P.State →ᵇ ℝ) (s : P.State) : + P.markovOp h s = ∑ z', P.prob s z' * h (P.nextState s z') := rfl + +theorem norm_markovOp_le (h : P.State →ᵇ ℝ) : ‖P.markovOp h‖ ≤ ‖h‖ := + (norm_le (norm_nonneg h)).2 fun s => by + rw [Real.norm_eq_abs] + exact P.abs_markovFn_le h s + +/-- The operator is monotone: a larger payoff tomorrow is worth more today. -/ +theorem markovOp_mono {h k : P.State →ᵇ ℝ} (hk : ⇑h ≤ ⇑k) : ⇑(P.markovOp h) ≤ ⇑(P.markovOp k) := by + intro s + refine Finset.sum_le_sum fun z' _ => mul_le_mul_of_nonneg_left ?_ (P.prob_nonneg s z') + exact hk _ + +/-- The operator is unital: a constant payoff is worth its own value. This is `∑ prob = 1`, +and it is what will make the induced map send probability measures to probability +measures. -/ +theorem markovOp_const (c : ℝ) : P.markovOp (const P.State c) = const P.State c := by + ext s + simp only [markovOp_apply, const_apply, ← Finset.sum_mul, P.prob_sum, one_mul] + +/-! ### The operator on measures -/ + +open MeasureTheory + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- The reweighting density for shock `z'`, as an `ℝ≥0`-valued function. -/ +noncomputable def dens (z' : Z) (s : P.State) : ℝ≥0 := (P.prob s z').toNNReal + +theorem measurable_dens (z' : Z) : Measurable (P.dens z') := + (continuous_real_toNNReal.comp (P.continuous_prob z')).measurable + +omit [MeasurableSpace Z] [BorelSpace Z] in +@[simp] +theorem coe_dens (z' : Z) (s : P.State) : ((P.dens z' s : ℝ≥0) : ℝ) = P.prob s z' := + Real.coe_toNNReal _ (P.prob_nonneg s z') + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem sum_dens (s : P.State) : ∑ z', ((P.dens z' s : ℝ≥0) : ℝ≥0∞) = 1 := by + have hz : ∀ z' : Z, ((P.dens z' s : ℝ≥0) : ℝ≥0∞) = ENNReal.ofReal (P.prob s z') := + fun _ => rfl + rw [Finset.sum_congr rfl fun z' _ => hz z', + ← ENNReal.ofReal_sum_of_nonneg fun z' _ => P.prob_nonneg s z', P.prob_sum s, + ENNReal.ofReal_one] + +/-- **One period of the agent distribution's motion.** Each income state `z'` reweights the +distribution by the probability of moving there and pushes it along the policy. -/ +noncomputable def push (μ : Measure P.State) : Measure P.State := + Measure.sum fun z' => + (μ.withDensity fun s => (P.dens z' s : ℝ≥0∞)).map fun s => P.nextState s z' + +theorem measurable_nextState (z' : Z) : Measurable fun s => P.nextState s z' := + (P.continuous_nextState z').measurable + +/-- `push` preserves total mass, because the transition probabilities sum to one. -/ +instance isProbabilityMeasure_push (μ : Measure P.State) [IsProbabilityMeasure μ] : + IsProbabilityMeasure (P.push μ) := by + constructor + rw [push, Measure.sum_apply _ MeasurableSet.univ] + have hterm : ∀ z' : Z, + ((μ.withDensity fun s => (P.dens z' s : ℝ≥0∞)).map fun s => P.nextState s z') univ + = ∫⁻ s, (P.dens z' s : ℝ≥0∞) ∂μ := by + intro z' + rw [Measure.map_apply (P.measurable_nextState z') MeasurableSet.univ, + Set.preimage_univ, withDensity_apply _ MeasurableSet.univ, Measure.restrict_univ] + rw [tsum_fintype] + simp only [hterm] + rw [← lintegral_finsetSum _ fun z' _ => ((P.measurable_dens z').coe_nnreal_ennreal)] + simp only [P.sum_dens] + simp + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- Each transition probability is at most one. -/ +theorem prob_le_one (s : P.State) (z' : Z) : P.prob s z' ≤ 1 := by + rw [← P.prob_sum s] + exact Finset.single_le_sum (fun i _ => P.prob_nonneg s i) (Finset.mem_univ z') + +theorem integrable_term (μ : Measure P.State) [IsProbabilityMeasure μ] (h : P.State →ᵇ ℝ) + (z' : Z) : Integrable (fun s => P.prob s z' * h (P.nextState s z')) μ := by + refine (integrable_const ‖h‖).mono' ?_ ?_ + · exact ((P.continuous_prob z').mul + (h.continuous.comp (P.continuous_nextState z'))).measurable.aestronglyMeasurable + · filter_upwards with s + have h1 := P.prob_le_one s z' + have h2 : |h (P.nextState s z')| ≤ ‖h‖ := by + simpa [Real.norm_eq_abs] using h.norm_coe_le_norm (P.nextState s z') + rw [Real.norm_eq_abs, abs_mul, abs_of_nonneg (P.prob_nonneg s z')] + nlinarith [P.prob_nonneg s z', abs_nonneg (h (P.nextState s z'))] + +/-- **Duality.** The integral of `h` against tomorrow's distribution is the integral of the +expected value of `h` against today's. This is the bridge between the two operators, and it +is what turns the Feller property into weak continuity. -/ +theorem integral_push (μ : Measure P.State) [IsProbabilityMeasure μ] (h : P.State →ᵇ ℝ) : + ∫ s, h s ∂(P.push μ) = ∫ s, P.markovOp h s ∂μ := by + have hterm : ∀ z' : Z, + ∫ s, h s ∂((μ.withDensity fun s => (P.dens z' s : ℝ≥0∞)).map fun s => P.nextState s z') + = ∫ s, P.prob s z' * h (P.nextState s z') ∂μ := by + intro z' + rw [integral_map (P.measurable_nextState z').aemeasurable + h.continuous.measurable.aestronglyMeasurable, + integral_withDensity_eq_integral_smul (P.measurable_dens z')] + refine integral_congr_ae (Filter.Eventually.of_forall fun s => ?_) + calc (P.dens z' s : ℝ≥0) • h (P.nextState s z') + = ((P.dens z' s : ℝ≥0) : ℝ) • h (P.nextState s z') := NNReal.smul_def _ _ + _ = P.prob s z' * h (P.nextState s z') := by rw [smul_eq_mul, P.coe_dens z' s] + have hint : Integrable (fun s => h s) (P.push μ) := h.integrable _ + rw [push] + rw [integral_sum_measure hint, tsum_fintype] + simp only [hterm] + rw [← integral_finsetSum _ fun z' _ => P.integrable_term μ h z'] + rfl + +/-- **The agent distribution operator**, on probability measures. -/ +noncomputable def pushProb (μ : ProbabilityMeasure P.State) : ProbabilityMeasure P.State := + ⟨P.push (μ : Measure P.State), P.isProbabilityMeasure_push _⟩ + +@[simp] +theorem coe_pushProb (μ : ProbabilityMeasure P.State) : + ((P.pushProb μ : ProbabilityMeasure P.State) : Measure P.State) + = P.push (μ : Measure P.State) := rfl + +/-- **The distribution operator is weakly continuous.** This is the Feller property in the +form a fixed point argument needs, and it falls straight out of the duality: weak +convergence is tested against bounded continuous functions, and `markovOp h` is one. -/ +theorem continuous_pushProb : Continuous P.pushProb := by + rw [continuous_iff_continuousAt] + intro μ + rw [ContinuousAt, ProbabilityMeasure.tendsto_iff_forall_integral_tendsto] + intro h + have hdual : ∀ ν : ProbabilityMeasure P.State, + ∫ s, h s ∂((P.pushProb ν : ProbabilityMeasure P.State) : Measure P.State) + = ∫ s, P.markovOp h s ∂(ν : Measure P.State) := fun ν => P.integral_push _ h + simp only [hdual] + exact ProbabilityMeasure.tendsto_iff_forall_integral_tendsto.mp + (tendsto_id : Filter.Tendsto id (𝓝 μ) (𝓝 μ)) (P.markovOp h) + +/-- A **stationary agent distribution** is a fixed point of the operator. -/ +def IsStationary (μ : ProbabilityMeasure P.State) : Prop := P.pushProb μ = μ + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean b/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean new file mode 100644 index 000000000..2d52aea41 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean @@ -0,0 +1,175 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Distribution.Feller +public import Mathlib.MeasureTheory.Measure.Prokhorov + + +/-! +# Existence of a stationary agent distribution + +Krylov–Bogolyubov, for the income fluctuation problem: average the iterates of the +distribution operator, and any cluster point of those averages is stationary. + +The two ingredients are both already in place. The space of probability measures over the +compact state space is compact, by Prokhorov; and the operator is weakly continuous, by the +Feller property. What the averaging adds is that the *difference* between an average and +its image telescopes, so it vanishes in the limit even though the individual iterates need +not converge at all. +-/ + +@[expose] public section + +open scoped NNReal ENNReal +open Set Filter Topology BoundedContinuousFunction MeasureTheory + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The state space is nonempty: a household at the borrowing limit is in it. This has to be +given by hand, since `assetFloor ≤ assetCap` is data carried by `P` rather than an instance. -/ +instance instNonemptyState : Nonempty P.State := + ⟨⟨⟨assetFloor, ⟨le_refl assetFloor, P.assetFloor_le_assetCap⟩⟩, Classical.ofNonempty⟩⟩ + +/-- The average of the first `n + 1` iterates of the distribution operator. -/ +noncomputable def cesaro (μ₀ : ProbabilityMeasure P.State) (n : ℕ) : Measure P.State := + (((n : ℝ≥0∞) + 1))⁻¹ • ∑ k ∈ Finset.range (n + 1), + ((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State) + +instance isProbabilityMeasure_cesaro (μ₀ : ProbabilityMeasure P.State) (n : ℕ) : + IsProbabilityMeasure (P.cesaro μ₀ n) := by + constructor + have hsum : (∑ k ∈ Finset.range (n + 1), + ((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State)) univ + = (n : ℝ≥0∞) + 1 := by + rw [Measure.finsetSum_apply] + simp [measure_univ] + rw [cesaro, Measure.smul_apply, hsum, smul_eq_mul] + refine ENNReal.inv_mul_cancel (by positivity) (by simp) + +/-- The integral against a Cesàro average is the average of the integrals. -/ +theorem integral_cesaro (μ₀ : ProbabilityMeasure P.State) (n : ℕ) (h : P.State →ᵇ ℝ) : + ∫ s, h s ∂(P.cesaro μ₀ n) + = (↑(n + 1))⁻¹ * ∑ k ∈ Finset.range (n + 1), + ∫ s, h s ∂((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State) := by + rw [cesaro, integral_smul_measure, integral_finsetSum_measure] + · push_cast + rw [ENNReal.toReal_inv, ENNReal.toReal_add (by simp) (by simp)] + simp [smul_eq_mul] + · intro k _ + exact h.integrable _ + +/-- Integration against a bounded continuous function is weakly continuous. -/ +theorem continuous_integral_bcf (h : P.State →ᵇ ℝ) : + Continuous fun ν : ProbabilityMeasure P.State => ∫ s, h s ∂(ν : Measure P.State) := by + rw [continuous_iff_continuousAt] + intro μ + exact ProbabilityMeasure.tendsto_iff_forall_integral_tendsto.mp + (tendsto_id : Filter.Tendsto id (𝓝 μ) (𝓝 μ)) h + +omit [BorelSpace Z] in +theorem abs_integral_le (ν : Measure P.State) [IsProbabilityMeasure ν] (h : P.State →ᵇ ℝ) : + |∫ s, h s ∂ν| ≤ ‖h‖ := by + rw [← Real.norm_eq_abs] + refine (norm_integral_le_of_norm_le_const (C := ‖h‖) ?_).trans_eq ?_ + · filter_upwards with s using h.norm_coe_le_norm s + · simp + +/-- Applying the operator to the test function advances the measure by one period. -/ +theorem integral_markovOp_iterate (μ₀ : ProbabilityMeasure P.State) (k : ℕ) + (h : P.State →ᵇ ℝ) : + ∫ s, P.markovOp h s ∂((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State) + = ∫ s, h s ∂((P.pushProb^[k + 1] μ₀ : ProbabilityMeasure P.State) : Measure P.State) := by + rw [Function.iterate_succ_apply', P.coe_pushProb] + exact (P.integral_push _ h).symm + +/-- **The telescoping estimate.** The average and its image differ by a single boundary +term over `n + 1`, which is the whole point of averaging: the individual iterates need not +converge, but this difference must vanish. -/ +theorem integral_cesaro_sub (μ₀ : ProbabilityMeasure P.State) (n : ℕ) (h : P.State →ᵇ ℝ) : + ∫ s, P.markovOp h s ∂(P.cesaro μ₀ n) - ∫ s, h s ∂(P.cesaro μ₀ n) + = (↑(n + 1))⁻¹ * + (∫ s, h s ∂((P.pushProb^[n + 1] μ₀ : ProbabilityMeasure P.State) : Measure P.State) + - ∫ s, h s ∂(μ₀ : Measure P.State)) := by + rw [P.integral_cesaro μ₀ n (P.markovOp h), P.integral_cesaro μ₀ n h, ← mul_sub] + congr 1 + rw [← Finset.sum_sub_distrib] + have hstep : ∀ k ∈ Finset.range (n + 1), + (∫ s, P.markovOp h s ∂((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State)) + - ∫ s, h s ∂((P.pushProb^[k] μ₀ : ProbabilityMeasure P.State) : Measure P.State) + = (fun j => ∫ s, h s + ∂((P.pushProb^[j] μ₀ : ProbabilityMeasure P.State) : Measure P.State)) (k + 1) + - (fun j => ∫ s, h s + ∂((P.pushProb^[j] μ₀ : ProbabilityMeasure P.State) : Measure P.State)) k := + fun k _ => by rw [P.integral_markovOp_iterate] + rw [Finset.sum_congr rfl hstep, + Finset.sum_range_sub (fun j => ∫ s, h s + ∂((P.pushProb^[j] μ₀ : ProbabilityMeasure P.State) : Measure P.State))] + simp + +theorem tendsto_cesaro_sub (μ₀ : ProbabilityMeasure P.State) (h : P.State →ᵇ ℝ) : + Tendsto (fun n => ∫ s, P.markovOp h s ∂(P.cesaro μ₀ n) - ∫ s, h s ∂(P.cesaro μ₀ n)) + atTop (𝓝 0) := by + have hbound : ∀ n : ℕ, + ‖∫ s, P.markovOp h s ∂(P.cesaro μ₀ n) - ∫ s, h s ∂(P.cesaro μ₀ n)‖ + ≤ (((n : ℝ) + 1))⁻¹ * (2 * ‖h‖) := by + intro n + rw [Real.norm_eq_abs, P.integral_cesaro_sub μ₀ n h, abs_mul] + push_cast + rw [abs_of_nonneg (by positivity : (0 : ℝ) ≤ ((n : ℝ) + 1)⁻¹)] + refine mul_le_mul_of_nonneg_left ?_ (by positivity) + refine (abs_sub _ _).trans ?_ + have h1 := P.abs_integral_le + ((P.pushProb^[n + 1] μ₀ : ProbabilityMeasure P.State) : Measure P.State) h + have h2 := P.abs_integral_le ((μ₀ : ProbabilityMeasure P.State) : Measure P.State) h + linarith + refine squeeze_zero_norm hbound ?_ + simpa using (tendsto_one_div_add_atTop_nhds_zero_nat.mul_const (2 * ‖h‖)) + +/-- **Krylov–Bogolyubov: a stationary agent distribution exists.** Average the iterates and +take a cluster point, which exists because the space of probability measures over the +compact state space is compact. The telescoping estimate makes that cluster point +stationary. -/ +theorem exists_isStationary (μ₀ : ProbabilityMeasure P.State) : + ∃ μ : ProbabilityMeasure P.State, P.IsStationary μ := by + set ν : ℕ → ProbabilityMeasure P.State := fun n => ⟨P.cesaro μ₀ n, inferInstance⟩ with hν + obtain ⟨μ, hμ⟩ := exists_clusterPt_of_compactSpace (Filter.map ν atTop) + refine ⟨μ, ?_⟩ + have key : ∀ h : P.State →ᵇ ℝ, + ∫ s, h s ∂((P.pushProb μ : ProbabilityMeasure P.State) : Measure P.State) + = ∫ s, h s ∂(μ : Measure P.State) := by + intro h + set g : ProbabilityMeasure P.State → ℝ := fun ρ => + ∫ s, P.markovOp h s ∂(ρ : Measure P.State) - ∫ s, h s ∂(ρ : Measure P.State) with hg + have hgc : Continuous g := + (P.continuous_integral_bcf _).sub (P.continuous_integral_bcf _) + have hgt : Filter.map (g ∘ ν) atTop ≤ 𝓝 0 := P.tendsto_cesaro_sub μ₀ h + have hcl : ClusterPt (g μ) (Filter.map (g ∘ ν) atTop) := by + rw [← Filter.map_map] + exact hμ.map hgc.continuousAt le_rfl + have hz : g μ = 0 := by + by_contra hne + have hdisj : Disjoint (𝓝 (g μ)) (𝓝 (0 : ℝ)) := disjoint_nhds_nhds.mpr hne + have : (𝓝 (g μ) ⊓ Filter.map (g ∘ ν) atTop).NeBot := hcl + exact (this.mono (inf_le_inf_left _ hgt)).ne (_root_.disjoint_iff.mp hdisj) + have := P.integral_push (μ : Measure P.State) h + simp only [hg, sub_eq_zero] at hz + rw [P.coe_pushProb, this, hz] + have hfin : (P.pushProb μ).toFiniteMeasure = μ.toFiniteMeasure := + FiniteMeasure.ext_of_forall_integral_eq key + exact ProbabilityMeasure.toMeasure_injective + (congrArg (fun x : FiniteMeasure P.State => (x : Measure P.State)) hfin) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean new file mode 100644 index 000000000..ed516f470 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean @@ -0,0 +1,560 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Distribution.Stationary +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone + + +/-! +# Uniqueness of the stationary agent distribution + +A Doeblin argument, made available by the borrowing constraint. + +The asset transition is deterministic given the shock, so the laws started from two +different asset levels have disjoint supports and no minorization against a spread-out +measure can hold. An ATOM escapes that obstruction: if a long enough run of bad income +shocks drives every household to exactly the borrowing constraint, then every `N`-step law +charges the single point `s₀ = (0, z₀)` with probability at least `ε`, and that is a genuine +Doeblin condition. + +The contraction is run on the function side, where `markovOp` already lives, rather than on +signed measures. The oscillation `supF h - infF h` contracts by `1 - ε` every `N` steps, so +for two stationary distributions `∫ h dμ - ∫ h dν` is squeezed to zero for every bounded +continuous `h`, and extensionality finishes. +-/ + +@[expose] public section + +open scoped NNReal ENNReal +open Set Filter Topology BoundedContinuousFunction MeasureTheory + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### The range of a bounded continuous function -/ + +theorem bddAbove_range (h : P.State →ᵇ ℝ) : BddAbove (Set.range h) := by + refine ⟨‖h‖, ?_⟩ + rintro _ ⟨s, rfl⟩ + exact (abs_le.mp (h.norm_coe_le_norm s)).2 + +theorem bddBelow_range (h : P.State →ᵇ ℝ) : BddBelow (Set.range h) := by + refine ⟨-‖h‖, ?_⟩ + rintro _ ⟨s, rfl⟩ + exact (abs_le.mp (h.norm_coe_le_norm s)).1 + +/-- The supremum of a bounded continuous function on the state space. -/ +noncomputable def supF (h : P.State →ᵇ ℝ) : ℝ := ⨆ s, h s + +/-- The infimum of a bounded continuous function on the state space. -/ +noncomputable def infF (h : P.State →ᵇ ℝ) : ℝ := ⨅ s, h s + +theorem le_supF (h : P.State →ᵇ ℝ) (s : P.State) : h s ≤ P.supF h := + le_ciSup (f := fun t => h t) (P.bddAbove_range h) s + +theorem infF_le (h : P.State →ᵇ ℝ) (s : P.State) : P.infF h ≤ h s := + ciInf_le (f := fun t => h t) (P.bddBelow_range h) s + +theorem infF_le_supF (h : P.State →ᵇ ℝ) : P.infF h ≤ P.supF h := + le_trans (P.infF_le h Classical.ofNonempty) (P.le_supF h Classical.ofNonempty) + +/-- If every gap is at most `C`, the oscillation is at most `C`. -/ +theorem sup_sub_inf_le {h : P.State →ᵇ ℝ} {C : ℝ} (hC : ∀ s t, h s - h t ≤ C) : + P.supF h - P.infF h ≤ C := by + have hkey : ∀ s, h s ≤ C + P.infF h := by + intro s + have : h s - C ≤ P.infF h := + le_ciInf (f := fun t => h t) fun t => by linarith [hC s t] + linarith + have : P.supF h ≤ C + P.infF h := ciSup_le hkey + linarith + +/-! ### The Markov operator preserves bounds -/ + +theorem infF_le_markovOp (h : P.State →ᵇ ℝ) (s : P.State) : P.infF h ≤ P.markovOp h s := by + rw [P.markovOp_apply] + calc P.infF h = ∑ z', P.prob s z' * P.infF h := by + rw [← Finset.sum_mul, P.prob_sum, one_mul] + _ ≤ ∑ z', P.prob s z' * h (P.nextState s z') := + Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (P.infF_le h _) (P.prob_nonneg s z') + +theorem markovOp_le_supF (h : P.State →ᵇ ℝ) (s : P.State) : P.markovOp h s ≤ P.supF h := by + rw [P.markovOp_apply] + calc ∑ z', P.prob s z' * h (P.nextState s z') + ≤ ∑ z', P.prob s z' * P.supF h := + Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (P.le_supF h _) (P.prob_nonneg s z') + _ = P.supF h := by rw [← Finset.sum_mul, P.prob_sum, one_mul] + +theorem infF_le_iterate (h : P.State →ᵇ ℝ) (n : ℕ) (s : P.State) : + P.infF h ≤ (P.markovOp^[n] h) s := by + induction n generalizing s with + | zero => exact P.infF_le h s + | succ n ih => + rw [Function.iterate_succ_apply'] + exact le_trans (le_ciInf fun t => ih t) (P.infF_le_markovOp _ s) + +theorem iterate_le_supF (h : P.State →ᵇ ℝ) (n : ℕ) (s : P.State) : + (P.markovOp^[n] h) s ≤ P.supF h := by + induction n generalizing s with + | zero => exact P.le_supF h s + | succ n ih => + rw [Function.iterate_succ_apply'] + exact le_trans (P.markovOp_le_supF _ s) (ciSup_le fun t => ih t) + +/-! ### The Doeblin minorization -/ + +/-- One period under the worst income shock `z₀`. -/ +noncomputable def badStep (z₀ : Z) (s : P.State) : P.State := P.nextState s z₀ + +/-- **Minorization, lower half.** After `n` periods, a share `p₀ⁿ` of the mass sits at the +state reached by `n` consecutive bad shocks, and the rest is worth at least `infF h`. -/ +theorem iterate_lower {z₀ : Z} {p₀ : ℝ} (hp0 : 0 ≤ p₀) (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) + (h : P.State →ᵇ ℝ) (n : ℕ) (s : P.State) : + p₀ ^ n * h ((P.badStep z₀)^[n] s) + (1 - p₀ ^ n) * P.infF h ≤ (P.markovOp^[n] h) s := by + classical + induction n generalizing s with + | zero => simp + | succ n ih => + rw [Function.iterate_succ_apply' P.markovOp n h, P.markovOp_apply, + Function.iterate_succ_apply (P.badStep z₀) n s, pow_succ] + have hq0 : 0 ≤ P.prob s z₀ := P.prob_nonneg s z₀ + have hsplit : ∑ z', P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') + = P.prob s z₀ * (P.markovOp^[n] h) (P.badStep z₀ s) + + ∑ z' ∈ Finset.univ.erase z₀, + P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') := + (Finset.add_sum_erase _ _ (Finset.mem_univ z₀)).symm + have hprobrest : ∑ z' ∈ Finset.univ.erase z₀, P.prob s z' = 1 - P.prob s z₀ := by + have hs := P.prob_sum s + rw [← Finset.add_sum_erase _ _ (Finset.mem_univ z₀)] at hs + linarith + have hrest : (1 - P.prob s z₀) * P.infF h + ≤ ∑ z' ∈ Finset.univ.erase z₀, + P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') := by + calc (1 - P.prob s z₀) * P.infF h + = ∑ z' ∈ Finset.univ.erase z₀, P.prob s z' * P.infF h := by + rw [← Finset.sum_mul, hprobrest] + _ ≤ _ := Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (P.infF_le_iterate h n _) (P.prob_nonneg s z') + have hIH := mul_le_mul_of_nonneg_left (ih (P.badStep z₀ s)) hq0 + have hHI : P.infF h ≤ h ((P.badStep z₀)^[n] (P.badStep z₀ s)) := P.infF_le h _ + have hgap : 0 ≤ (P.prob s z₀ - p₀) * p₀ ^ n := + mul_nonneg (sub_nonneg.mpr (hp s)) (pow_nonneg hp0 n) + rw [hsplit] + nlinarith [hIH, hrest, hHI, hgap, pow_nonneg hp0 n, hp s] + +/-- **Minorization, upper half.** -/ +theorem iterate_upper {z₀ : Z} {p₀ : ℝ} (hp0 : 0 ≤ p₀) (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) + (h : P.State →ᵇ ℝ) (n : ℕ) (s : P.State) : + (P.markovOp^[n] h) s ≤ p₀ ^ n * h ((P.badStep z₀)^[n] s) + (1 - p₀ ^ n) * P.supF h := by + classical + induction n generalizing s with + | zero => simp + | succ n ih => + rw [Function.iterate_succ_apply' P.markovOp n h, P.markovOp_apply, + Function.iterate_succ_apply (P.badStep z₀) n s, pow_succ] + have hq0 : 0 ≤ P.prob s z₀ := P.prob_nonneg s z₀ + have hsplit : ∑ z', P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') + = P.prob s z₀ * (P.markovOp^[n] h) (P.badStep z₀ s) + + ∑ z' ∈ Finset.univ.erase z₀, + P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') := + (Finset.add_sum_erase _ _ (Finset.mem_univ z₀)).symm + have hprobrest : ∑ z' ∈ Finset.univ.erase z₀, P.prob s z' = 1 - P.prob s z₀ := by + have hs := P.prob_sum s + rw [← Finset.add_sum_erase _ _ (Finset.mem_univ z₀)] at hs + linarith + have hrest : ∑ z' ∈ Finset.univ.erase z₀, + P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') + ≤ (1 - P.prob s z₀) * P.supF h := by + calc ∑ z' ∈ Finset.univ.erase z₀, + P.prob s z' * (P.markovOp^[n] h) (P.nextState s z') + ≤ ∑ z' ∈ Finset.univ.erase z₀, P.prob s z' * P.supF h := + Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (P.iterate_le_supF h n _) (P.prob_nonneg s z') + _ = (1 - P.prob s z₀) * P.supF h := by rw [← Finset.sum_mul, hprobrest] + have hIH := mul_le_mul_of_nonneg_left (ih (P.badStep z₀ s)) hq0 + have hHI : h ((P.badStep z₀)^[n] (P.badStep z₀ s)) ≤ P.supF h := P.le_supF h _ + have hgap : 0 ≤ (P.prob s z₀ - p₀) * p₀ ^ n := + mul_nonneg (sub_nonneg.mpr (hp s)) (pow_nonneg hp0 n) + rw [hsplit] + nlinarith [hIH, hrest, hHI, hgap, pow_nonneg hp0 n, hp s] + +/-! ### The oscillation contracts -/ + +/-- **One Doeblin cycle contracts the oscillation** by the factor `1 - p₀ᴺ`. Both households +are compared against the *same* value `h s₀`, which is what `hbad` buys. -/ +theorem osc_markovOp_le {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 ≤ p₀) + (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) + (h : P.State →ᵇ ℝ) : + P.supF (P.markovOp^[N] h) - P.infF (P.markovOp^[N] h) + ≤ (1 - p₀ ^ N) * (P.supF h - P.infF h) := by + refine P.sup_sub_inf_le fun s t => ?_ + have hs := P.iterate_upper hp0 hp h N s + have ht := P.iterate_lower hp0 hp h N t + rw [hbad s] at hs + rw [hbad t] at ht + linarith + +theorem prob_le_one' (s : P.State) (z' : Z) : P.prob s z' ≤ 1 := P.prob_le_one s z' + +theorem osc_cycle_le {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 ≤ p₀) + (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) + (k : ℕ) (h : P.State →ᵇ ℝ) : + P.supF ((P.markovOp^[N])^[k] h) - P.infF ((P.markovOp^[N])^[k] h) + ≤ (1 - p₀ ^ N) ^ k * (P.supF h - P.infF h) := by + have hp1 : p₀ ≤ 1 := le_trans (hp Classical.ofNonempty) (P.prob_le_one _ _) + have hfac : (0 : ℝ) ≤ 1 - p₀ ^ N := by + have : p₀ ^ N ≤ 1 := pow_le_one₀ hp0 hp1 + linarith + induction k generalizing h with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply] + calc P.supF ((P.markovOp^[N])^[k] (P.markovOp^[N] h)) + - P.infF ((P.markovOp^[N])^[k] (P.markovOp^[N] h)) + ≤ (1 - p₀ ^ N) ^ k + * (P.supF (P.markovOp^[N] h) - P.infF (P.markovOp^[N] h)) := ih _ + _ ≤ (1 - p₀ ^ N) ^ k * ((1 - p₀ ^ N) * (P.supF h - P.infF h)) := + mul_le_mul_of_nonneg_left (P.osc_markovOp_le hp0 hp hbad h) (by positivity) + _ = (1 - p₀ ^ N) ^ (k + 1) * (P.supF h - P.infF h) := by ring + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-! ### Stationarity is preserved along the iteration -/ + +theorem integral_iterate_eq {μ : Measure P.State} [IsProbabilityMeasure μ] + (hμ : P.push μ = μ) (n : ℕ) (h : P.State →ᵇ ℝ) : + ∫ s, (P.markovOp^[n] h) s ∂μ = ∫ s, h s ∂μ := by + induction n generalizing h with + | zero => simp + | succ n ih => + rw [Function.iterate_succ_apply, ih (P.markovOp h)] + have hint := P.integral_push μ h + rw [hμ] at hint + exact hint.symm + +theorem integral_cycle_eq {μ : Measure P.State} [IsProbabilityMeasure μ] + (hμ : P.push μ = μ) (N k : ℕ) (h : P.State →ᵇ ℝ) : + ∫ s, ((P.markovOp^[N])^[k] h) s ∂μ = ∫ s, h s ∂μ := by + induction k generalizing h with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply, ih, P.integral_iterate_eq hμ] + +theorem integral_le_supF (μ : Measure P.State) [IsProbabilityMeasure μ] (h : P.State →ᵇ ℝ) : + ∫ s, h s ∂μ ≤ P.supF h := by + calc ∫ s, h s ∂μ ≤ ∫ _s, P.supF h ∂μ := + integral_mono (h.integrable μ) (integrable_const _) fun s => P.le_supF h s + _ = P.supF h := by simp + +theorem infF_le_integral (μ : Measure P.State) [IsProbabilityMeasure μ] (h : P.State →ᵇ ℝ) : + P.infF h ≤ ∫ s, h s ∂μ := by + calc P.infF h = ∫ _s, P.infF h ∂μ := by simp + _ ≤ ∫ s, h s ∂μ := + integral_mono (integrable_const _) (h.integrable μ) fun s => P.infF_le h s + +/-! ### Uniqueness -/ + +/-- **The stationary agent distribution is unique**, given a Doeblin condition: some income +state `z₀` is always reachable with probability at least `p₀ > 0`, and `N` consecutive `z₀` +shocks drive *every* state to the single state `s₀`. + +In the income fluctuation problem `s₀` is the borrowing constraint. The atom is what makes a +Doeblin argument possible at all: the asset transition is deterministic, so laws started from +different assets have disjoint supports and no minorization against a spread-out measure can +hold — but they all charge the constraint. -/ +theorem stationary_unique {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 < p₀) + (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) + {μ ν : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) (hν : P.IsStationary ν) : + μ = ν := by + have hp1 : p₀ ≤ 1 := le_trans (hp Classical.ofNonempty) (P.prob_le_one _ _) + have hεpos : 0 < p₀ ^ N := pow_pos hp0 N + have hεle : p₀ ^ N ≤ 1 := pow_le_one₀ hp0.le hp1 + have hμ' : P.push (μ : Measure P.State) = (μ : Measure P.State) := + congrArg (fun x : ProbabilityMeasure P.State => (x : Measure P.State)) hμ + have hν' : P.push (ν : Measure P.State) = (ν : Measure P.State) := + congrArg (fun x : ProbabilityMeasure P.State => (x : Measure P.State)) hν + have key : ∀ h : P.State →ᵇ ℝ, + ∫ s, h s ∂(μ : Measure P.State) = ∫ s, h s ∂(ν : Measure P.State) := by + intro h + set D := ∫ s, h s ∂(μ : Measure P.State) - ∫ s, h s ∂(ν : Measure P.State) with hD + have hbound : ∀ k : ℕ, |D| ≤ (1 - p₀ ^ N) ^ k * (P.supF h - P.infF h) := by + intro k + set g := (P.markovOp^[N])^[k] h with hg + have hμg : ∫ s, g s ∂(μ : Measure P.State) = ∫ s, h s ∂(μ : Measure P.State) := + P.integral_cycle_eq hμ' N k h + have hνg : ∫ s, g s ∂(ν : Measure P.State) = ∫ s, h s ∂(ν : Measure P.State) := + P.integral_cycle_eq hν' N k h + have hgap := P.osc_cycle_le hp0.le hp hbad k h + have h1 := P.integral_le_supF (μ : Measure P.State) g + have h2 := P.infF_le_integral (ν : Measure P.State) g + have h3 := P.integral_le_supF (ν : Measure P.State) g + have h4 := P.infF_le_integral (μ : Measure P.State) g + rw [abs_le] + constructor <;> [linarith; linarith] + have hlim : Filter.Tendsto + (fun k : ℕ => (1 - p₀ ^ N) ^ k * (P.supF h - P.infF h)) atTop (𝓝 0) := by + have : Filter.Tendsto (fun k : ℕ => (1 - p₀ ^ N) ^ k) atTop (𝓝 0) := + tendsto_pow_atTop_nhds_zero_of_lt_one (by linarith) (by linarith) + simpa using this.mul_const (P.supF h - P.infF h) + have : |D| ≤ 0 := + le_of_tendsto_of_tendsto' tendsto_const_nhds hlim hbound + have : D = 0 := by + have := abs_nonneg D + have hz : |D| = 0 := le_antisymm ‹|D| ≤ 0› (abs_nonneg D) + exact abs_eq_zero.mp hz + linarith [hD ▸ this] + have hfin : (μ : ProbabilityMeasure P.State).toFiniteMeasure + = (ν : ProbabilityMeasure P.State).toFiniteMeasure := + FiniteMeasure.ext_of_forall_integral_eq key + exact ProbabilityMeasure.toMeasure_injective + (congrArg (fun x : FiniteMeasure P.State => (x : Measure P.State)) hfin) + +/-! ### Discharging the Doeblin condition in the model + +The abstract condition asks that `N` bad shocks drive *every* state to the borrowing +constraint. Monotonicity of the policy reduces that to the single worst case: if the richest +household is exhausted in `N` periods, everyone is, because the asset map is increasing and +bounded below by zero. -/ + +/-- The asset map under the worst income shock. -/ +noncomputable def gBad (z₀ : Z) (x : ↥(Icc assetFloor assetCap)) : ↥(Icc assetFloor assetCap) := + P.nextAssets (x, z₀) + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem gBad_mono (z₀ : Z) : Monotone (P.gBad z₀) := fun x y hxy => + P.policy_mono x.2 y.2 hxy + +/-- The richest state. -/ +def topState : ↥(Icc assetFloor assetCap) := ⟨assetCap, ⟨P.assetFloor_le_assetCap, le_rfl⟩⟩ + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem le_topState (x : ↥(Icc assetFloor assetCap)) : x ≤ P.topState := x.2.2 + +/-- The bottom state: the borrowing constraint. -/ +def botState : ↥(Icc assetFloor assetCap) := ⟨assetFloor, ⟨le_rfl, P.assetFloor_le_assetCap⟩⟩ + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem botState_le (x : ↥(Icc assetFloor assetCap)) : P.botState ≤ x := x.2.1 + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **Monotonicity reduces exhaustion to the worst case.** -/ +theorem gBad_iterate_eq_bot {z₀ : Z} {N : ℕ} (hexh : (P.gBad z₀)^[N] P.topState = P.botState) + (x : ↥(Icc assetFloor assetCap)) : (P.gBad z₀)^[N] x = P.botState := + le_antisymm (hexh ▸ (P.gBad_mono z₀).iterate N (P.le_topState x)) (P.botState_le _) + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem badStep_iterate_succ (z₀ : Z) (n : ℕ) (s : P.State) : + (P.badStep z₀)^[n + 1] s = ((P.gBad z₀)^[n] (P.nextAssets s), z₀) := by + induction n generalizing s with + | zero => rfl + | succ n ih => + rw [Function.iterate_succ_apply, ih (P.badStep z₀ s)] + rfl + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The Doeblin condition holds** once the richest household is exhausted by `N` bad +shocks: after one more period every state has collapsed to the borrowing constraint. -/ +theorem badStep_iterate_eq {z₀ : Z} {N : ℕ} (hexh : (P.gBad z₀)^[N] P.topState = P.botState) + (s : P.State) : (P.badStep z₀)^[N + 1] s = (P.botState, z₀) := by + rw [P.badStep_iterate_succ z₀ N s, P.gBad_iterate_eq_bot hexh] + +/-- **Uniqueness of the stationary agent distribution for the income fluctuation problem.** + +The two economic assumptions are that the worst income state `z₀` is reachable from every +income state (`hreach`), and that a household starting with the maximum assets is driven to +the borrowing constraint by `N` consecutive draws of `z₀` (`hexh`). Monotonicity of the +policy then carries the second from the richest household to every household. -/ +theorem stationary_unique_of_exhausts {z₀ : Z} {N : ℕ} + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + (hexh : (P.gBad z₀)^[N] P.topState = P.botState) + {μ ν : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) (hν : P.IsStationary ν) : + μ = ν := by + obtain ⟨z₁, -, hmin⟩ := Finset.exists_min_image Finset.univ + (fun z => P.transitionMatrix z z₀) ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + exact P.stationary_unique (p₀ := P.transitionMatrix z₁ z₀) (hreach z₁) + (fun s => hmin _ (Finset.mem_univ _)) (P.badStep_iterate_eq hexh) hμ hν + +/-- **Existence and uniqueness together.** -/ +theorem existsUnique_isStationary {z₀ : Z} {N : ℕ} + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + (hexh : (P.gBad z₀)^[N] P.topState = P.botState) : + ∃! μ : ProbabilityMeasure P.State, P.IsStationary μ := by + obtain ⟨μ, hμ⟩ := P.exists_isStationary + ⟨Measure.dirac (Classical.ofNonempty : P.State), inferInstance⟩ + exact ⟨μ, hμ, fun ν hν => P.stationary_unique_of_exhausts hreach hexh hν hμ⟩ + +/-! ### Convergence + +Uniqueness says there is one stationary distribution; convergence says the economy finds it. +The same oscillation contraction gives both. Iterating the operator flattens any test +function geometrically, and a flat test function cannot tell two distributions apart, so the +iterates of ANY initial distribution become indistinguishable from the stationary one. + +This is what makes the stationary distribution computable rather than merely unique: it is +the limit of iterating from wherever you start. -/ + +theorem integral_pushProb_iterate (μ : ProbabilityMeasure P.State) (m : ℕ) (h : P.State →ᵇ ℝ) : + ∫ s, h s ∂((P.pushProb^[m] μ : ProbabilityMeasure P.State) : Measure P.State) + = ∫ s, (P.markovOp^[m] h) s ∂(μ : Measure P.State) := by + induction m generalizing h with + | zero => simp + | succ m ih => + rw [Function.iterate_succ_apply', P.coe_pushProb, P.integral_push, ih, + Function.iterate_succ_apply] + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- Oscillation cannot grow under the operator. -/ +theorem osc_iterate_le (h : P.State →ᵇ ℝ) (j : ℕ) : + P.supF (P.markovOp^[j] h) - P.infF (P.markovOp^[j] h) ≤ P.supF h - P.infF h := by + have h1 : P.supF (P.markovOp^[j] h) ≤ P.supF h := + ciSup_le fun s => P.iterate_le_supF h j s + have h2 : P.infF h ≤ P.infF (P.markovOp^[j] h) := + le_ciInf fun s => P.infF_le_iterate h j s + linarith + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The oscillation of an iterated test function vanishes.** -/ +theorem tendsto_osc_iterate {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 < p₀) + (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) + (h : P.State →ᵇ ℝ) : + Tendsto (fun m => P.supF (P.markovOp^[m] h) - P.infF (P.markovOp^[m] h)) atTop (𝓝 0) := by + have hp1 : p₀ ≤ 1 := le_trans (hp Classical.ofNonempty) (P.prob_le_one _ _) + have hεpos : 0 < p₀ ^ N := pow_pos hp0 N + have hεle : p₀ ^ N ≤ 1 := pow_le_one₀ hp0.le hp1 + rw [Metric.tendsto_atTop] + intro γ hγ + have hlim : Tendsto (fun k : ℕ => (1 - p₀ ^ N) ^ k * (P.supF h - P.infF h)) atTop (𝓝 0) := by + have := tendsto_pow_atTop_nhds_zero_of_lt_one (by linarith) (by linarith : 1 - p₀ ^ N < 1) + simpa using this.mul_const (P.supF h - P.infF h) + obtain ⟨k, hk⟩ := (hlim.eventually (gt_mem_nhds hγ)).exists + refine ⟨N * k, fun m hm => ?_⟩ + obtain ⟨j, rfl⟩ := Nat.exists_eq_add_of_le hm + have hsplit : P.markovOp^[N * k + j] h = P.markovOp^[j] ((P.markovOp^[N])^[k] h) := by + rw [add_comm, Function.iterate_add_apply, Function.iterate_mul] + rw [Real.dist_eq, hsplit, sub_zero, + abs_of_nonneg (by linarith [P.infF_le_supF (P.markovOp^[j] ((P.markovOp^[N])^[k] h))])] + exact lt_of_le_of_lt (le_trans (P.osc_iterate_le _ j) + (P.osc_cycle_le hp0.le hp hbad k h)) hk + +/-- **Convergence to the stationary distribution.** From any starting point, the iterates of +the distribution operator converge weakly to the stationary distribution. -/ +theorem tendsto_pushProb_iterate {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 < p₀) + (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) + (μ₀ : ProbabilityMeasure P.State) {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) : + Tendsto (fun m => P.pushProb^[m] μ₀) atTop (𝓝 μ) := by + have hμ' : P.push (μ : Measure P.State) = (μ : Measure P.State) := + congrArg (fun x : ProbabilityMeasure P.State => (x : Measure P.State)) hμ + rw [ProbabilityMeasure.tendsto_iff_forall_integral_tendsto] + intro h + rw [tendsto_iff_dist_tendsto_zero] + refine squeeze_zero (fun _ => dist_nonneg) (fun m => ?_) + (P.tendsto_osc_iterate hp0 hp hbad h) + -- the gap is at most the oscillation of the iterated test function + have hleft : ∫ s, h s ∂((P.pushProb^[m] μ₀ : ProbabilityMeasure P.State) : Measure P.State) + = ∫ s, (P.markovOp^[m] h) s ∂(μ₀ : Measure P.State) := P.integral_pushProb_iterate μ₀ m h + have hright : ∫ s, h s ∂(μ : Measure P.State) + = ∫ s, (P.markovOp^[m] h) s ∂(μ : Measure P.State) := + (P.integral_iterate_eq hμ' m h).symm + rw [Real.dist_eq, hleft, hright] + have h1 := P.integral_le_supF (μ₀ : Measure P.State) (P.markovOp^[m] h) + have h2 := P.infF_le_integral (μ₀ : Measure P.State) (P.markovOp^[m] h) + have h3 := P.integral_le_supF (μ : Measure P.State) (P.markovOp^[m] h) + have h4 := P.infF_le_integral (μ : Measure P.State) (P.markovOp^[m] h) + rw [abs_le] + constructor <;> linarith + +/-! ### Exhaustion from strict dissaving + +`badStep_iterate_eq` takes exhaustion as a hypothesis in the awkward form `gBad^[N] top = bot` +-- an exact hit on the borrowing constraint after a specified number of periods. That is hard +to check directly, and in the earlier calibrations it was obtained only by making the policy +identically zero. + +Açıkgöz (2018) obtains it instead from two natural facts, in the proof of his Proposition 5: +the constraint binds near zero, and assets strictly decline above that at the worst income +state. A continuous strictly declining map on a compact interval has a UNIFORM minimum +decline, so finitely many periods carry the richest household below the threshold, and one +more step pins it to the constraint. + +His Proposition 4, which produces the asset bound itself, is a different matter: its proof +runs the Euler equation at an interior optimum and takes limits of `u'(x+ε)/u'(x)`, so it +needs differentiability of the utility and the first-order condition. This development +deliberately has neither. The lemma below extracts only the part that does not. -/ + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem exists_exhaust_of_decline {z₀ : Z} {a₀ : ℝ} (ha₀ : assetFloor < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc assetFloor a₀, P.policy (a, z₀) = assetFloor) + (hdecl : ∀ a ∈ Icc a₀ assetCap, P.policy (a, z₀) < a) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + classical + -- the minimal decline over the compact interval above the threshold + have hne : (Icc a₀ assetCap).Nonempty := ⟨a₀, ⟨le_rfl, hle⟩⟩ + have hcont : ContinuousOn (fun a : ℝ => a - P.policy (a, z₀)) (Icc a₀ assetCap) := by + refine continuousOn_id.sub (P.continuousOn_policy.comp + (continuous_id.prodMk continuous_const).continuousOn fun a ha => ?_) + exact ⟨le_trans ha₀.le ha.1, ha.2⟩ + obtain ⟨am, hamem, hamin⟩ := isCompact_Icc.exists_isMinOn hne hcont + set Δ : ℝ := am - P.policy (am, z₀) with hΔdef + have hΔpos : 0 < Δ := by rw [hΔdef]; linarith [hdecl am hamem] + have hstep : ∀ a ∈ Icc a₀ assetCap, P.policy (a, z₀) ≤ a - Δ := fun a ha => by + have h := hamin ha + rw [Set.mem_ofPred_eq] at h + rw [hΔdef] + linarith + -- after `k` steps either the threshold is reached or assets have fallen by `k·Δ` + have key : ∀ k : ℕ, (((P.gBad z₀)^[k] P.topState : ↥(Icc assetFloor assetCap)) : ℝ) ≤ a₀ ∨ + (((P.gBad z₀)^[k] P.topState : ↥(Icc assetFloor assetCap)) : ℝ) ≤ assetCap - k * Δ := by + intro k + induction k with + | zero => right; simp [topState] + | succ k ih => + rw [Function.iterate_succ_apply'] + have hval : (((P.gBad z₀) ((P.gBad z₀)^[k] P.topState) : + ↥(Icc assetFloor assetCap)) : ℝ) + = P.policy ((((P.gBad z₀)^[k] P.topState : ↥(Icc assetFloor assetCap)) : ℝ), z₀) := rfl + rcases ih with h | h + · -- already at or below the threshold: the next step is exactly zero + left + rw [hval, hzero _ ⟨((P.gBad z₀)^[k] P.topState).2.1, h⟩] + exact ha₀.le + · rcases le_total (((P.gBad z₀)^[k] P.topState : ↥(Icc assetFloor assetCap)) : ℝ) a₀ + with hlow | hhigh + · left + rw [hval, hzero _ ⟨((P.gBad z₀)^[k] P.topState).2.1, hlow⟩] + exact ha₀.le + · right + rw [hval] + have hmem : (((P.gBad z₀)^[k] P.topState : ↥(Icc assetFloor assetCap)) : ℝ) + ∈ Icc a₀ assetCap := ⟨hhigh, ((P.gBad z₀)^[k] P.topState).2.2⟩ + have hdec := hstep _ hmem + push_cast + rw [show assetCap - ((k : ℝ) + 1) * Δ = (assetCap - (k : ℝ) * Δ) - Δ by ring] + linarith + -- finitely many steps carry assets below the threshold + obtain ⟨N, hN⟩ := exists_nat_gt ((assetCap - a₀) / Δ) + refine ⟨N + 1, ?_⟩ + have hNle : (((P.gBad z₀)^[N] P.topState : ↥(Icc assetFloor assetCap)) : ℝ) ≤ a₀ := by + rcases key N with h | h + · exact h + · rw [div_lt_iff₀ hΔpos] at hN + linarith + refine Subtype.ext ?_ + rw [Function.iterate_succ_apply'] + change P.policy ((((P.gBad z₀)^[N] P.topState : ↥(Icc assetFloor assetCap)) : ℝ), z₀) + = assetFloor + exact hzero _ ⟨((P.gBad z₀)^[N] P.topState).2.1, hNle⟩ + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean new file mode 100644 index 000000000..04849c946 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean @@ -0,0 +1,336 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Blackwell +public import LeanPool.AiyagariEquilibrium.Topology.Berge + + +/-! +# The Bellman operator of a deterministic dynamic program + +The operator + + `T v s = sup { r (s, a) + β * v (g (s, a)) | a ∈ Γ s }` + +is shown to map bounded continuous functions to bounded continuous functions, and to +satisfy Blackwell's sufficient conditions. Combined with `LeanEconomics.Blackwell` this +gives the value function of the program, its uniqueness, and convergence of value function +iteration. + +## The formulation + +A program is given by a feasible correspondence `Γ : S → Set A` with nonempty compact +values that is continuous -- upper and lower hemicontinuous -- together with a bounded +continuous reward `r : (S × A) →ᵇ ℝ`, a continuous law of motion `g : C(S × A, S)`, and a +discount factor `β < 1`. + +The feasible set moves with the state, which is what a budget constraint does. The price of +that generality is the continuity of `T v`, and it is paid by Berge's maximum theorem in +`LeanEconomics.Topology.Berge`: `continuous_maxValue` is exactly the statement that the +value of a parametrised maximisation problem is continuous in the parameter. + +Verifying that a particular `Γ` is hemicontinuous is real work, done per model. The two +programs in `LeanEconomics.DynamicProgramming.Examples` discharge it in the two cheapest +ways: a constraint set that does not move, and one pinned to a single continuously varying +point. + +## Main definitions + +* `LeanEconomics.DynamicProgram` : the data of a deterministic dynamic program. +* `LeanEconomics.DynamicProgram.bellman` : its Bellman operator on `S →ᵇ ℝ`. +* `LeanEconomics.DynamicProgram.valueFunction` : the value function. + +## Main results + +* `LeanEconomics.DynamicProgram.blackwell` : the Bellman operator satisfies Blackwell's + conditions, hence is a contraction. +* `LeanEconomics.DynamicProgram.bellman_valueFunction`, + `LeanEconomics.DynamicProgram.eq_valueFunction` : the value function is the unique + solution of the Bellman equation. +* `LeanEconomics.DynamicProgram.exists_optimal_action` : the supremum is attained, so a + greedy policy exists. + +## References + +* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*, chapter 4. +-/ + +@[expose] public section + +open scoped NNReal +open Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +variable {S A : Type*} [TopologicalSpace S] [TopologicalSpace A] + +/-- A **deterministic dynamic program**: the agent observes a state, picks an action from +the feasible set at that state, collects a reward, and moves to the state the law of motion +dictates. -/ +structure DynamicProgram (S A : Type*) [TopologicalSpace S] [TopologicalSpace A] where + /-- The actions available to the agent, as a function of the state. -/ + feasible : S → Set A + /-- Each feasible set is compact, so that the supremum over it is attained. -/ + isCompact_feasible : ∀ s, IsCompact (feasible s) + /-- The agent always has something to do. -/ + feasible_nonempty : ∀ s, (feasible s).Nonempty + /-- The feasible set does not explode as the state moves. -/ + upperHemicontinuous_feasible : UpperHemicontinuous feasible + /-- The feasible set does not collapse as the state moves. -/ + lowerHemicontinuous_feasible : LowerHemicontinuous feasible + /-- The one-period reward, bounded and continuous in state and action jointly. -/ + reward : (S × A) →ᵇ ℝ + /-- The law of motion. -/ + transition : C(S × A, S) + /-- The discount factor. -/ + discount : ℝ≥0 + /-- Future rewards are discounted strictly, which is what makes the operator a + contraction. -/ + discount_lt_one : discount < 1 + +namespace DynamicProgram + +variable (D : DynamicProgram S A) + +/-- What the agent gets from action `a` in state `s`: this period's reward plus the +discounted continuation value at the state the action leads to. -/ +noncomputable def objective (v : S →ᵇ ℝ) (s : S) (a : A) : ℝ := + D.reward (s, a) + D.discount * v (D.transition (s, a)) + +theorem continuous_uncurry_objective (v : S →ᵇ ℝ) : Continuous ↿(D.objective v) := + D.reward.continuous.add (continuous_const.mul (v.continuous.comp D.transition.continuous)) + +/-- The objective is bounded uniformly in the state and the action, by the size of the +reward plus the discounted size of the continuation value. -/ +theorem abs_objective_le (v : S →ᵇ ℝ) (s : S) (a : A) : + |D.objective v s a| ≤ ‖D.reward‖ + D.discount * ‖v‖ := by + have h₁ : |D.reward (s, a)| ≤ ‖D.reward‖ := by + simpa [Real.norm_eq_abs] using D.reward.norm_coe_le_norm (s, a) + have h₂ : |v (D.transition (s, a))| ≤ ‖v‖ := by + simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (D.transition (s, a)) + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + rw [abs_le] at h₁ h₂ + simp only [objective, abs_le] + constructor <;> + nlinarith [h₁.1, h₁.2, h₂.1, h₂.2, mul_le_mul_of_nonneg_left h₂.1 hβ, + mul_le_mul_of_nonneg_left h₂.2 hβ] + +/-- The Bellman operator as a bare function: the value of the one-period problem, in the +sense of `LeanEconomics.maxValue`. -/ +noncomputable def bellmanFn (v : S →ᵇ ℝ) (s : S) : ℝ := + maxValue (D.objective v) D.feasible s + +theorem le_bellmanFn (v : S →ᵇ ℝ) {s : S} {a : A} (ha : a ∈ D.feasible s) : + D.objective v s a ≤ D.bellmanFn v s := + le_maxValue (D.continuous_uncurry_objective v) (D.isCompact_feasible s) ha + +theorem bellmanFn_le (v : S →ᵇ ℝ) {s : S} {c : ℝ} + (h : ∀ a ∈ D.feasible s, D.objective v s a ≤ c) : D.bellmanFn v s ≤ c := + maxValue_le (D.feasible_nonempty s) h + +theorem abs_bellmanFn_le (v : S →ᵇ ℝ) (s : S) : + |D.bellmanFn v s| ≤ ‖D.reward‖ + D.discount * ‖v‖ := by + obtain ⟨a, ha⟩ := D.feasible_nonempty s + rw [abs_le] + refine ⟨?_, D.bellmanFn_le v fun b _ => (le_abs_self _).trans (D.abs_objective_le v s b)⟩ + exact le_trans (neg_le_of_abs_le (D.abs_objective_le v s a)) (D.le_bellmanFn v ha) + +/-- The **Bellman operator** on bounded continuous functions. Continuity of the image is +Berge's maximum theorem; boundedness is `abs_bellmanFn_le`. -/ +noncomputable def bellman (v : S →ᵇ ℝ) : S →ᵇ ℝ := + BoundedContinuousFunction.ofNormedAddCommGroup (D.bellmanFn v) + (continuous_maxValue (D.continuous_uncurry_objective v) D.feasible_nonempty + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible) + (‖D.reward‖ + D.discount * ‖v‖) + fun s => by simpa [Real.norm_eq_abs] using D.abs_bellmanFn_le v s + +@[simp] +theorem bellman_apply (v : S →ᵇ ℝ) (s : S) : + D.bellman v s = maxValue (D.objective v) D.feasible s := rfl + +/-- **The Bellman operator satisfies Blackwell's sufficient conditions.** Monotonicity +holds because a larger continuation value raises the objective at every action; +discounting holds because the continuation value enters multiplied by `β`. -/ +theorem blackwell : Blackwell D.discount D.bellman where + monotone := by + intro v w hvw s + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + have key : ∀ a ∈ D.feasible s, D.objective v s a ≤ D.bellmanFn w s := by + intro a ha + refine le_trans ?_ (D.le_bellmanFn w ha) + have hx : v (D.transition (s, a)) ≤ w (D.transition (s, a)) := by + simpa using hvw (D.transition (s, a)) + simp only [objective] + nlinarith + exact D.bellmanFn_le v key + discounting := by + intro v c _ s + have key : ∀ a ∈ D.feasible s, D.objective (v + const S c) s a + ≤ D.bellmanFn v s + D.discount * c := by + intro a ha + have h : D.objective (v + const S c) s a = D.objective v s a + D.discount * c := by + simp only [objective, coe_add, const_apply, Pi.add_apply] + ring + rw [h] + have := D.le_bellmanFn v (s := s) ha + linarith + exact D.bellmanFn_le (v + const S c) key + +/-- The supremum defining the Bellman operator is **attained**: in every state there is an +optimal action. This is what makes a greedy policy well defined. -/ +theorem exists_optimal_action (v : S →ᵇ ℝ) (s : S) : + ∃ a ∈ D.feasible s, D.bellman v s = D.objective v s a ∧ + ∀ b ∈ D.feasible s, D.objective v s b ≤ D.objective v s a := by + obtain ⟨a, ha, hmax⟩ := + argmax_nonempty (D.continuous_uncurry_objective v) (D.feasible_nonempty s) + (D.isCompact_feasible s) + exact ⟨a, ha, + maxValue_eq (D.continuous_uncurry_objective v) (D.isCompact_feasible s) ⟨ha, hmax⟩, + fun b hb => isMaxOn_iff.mp hmax b hb⟩ + +/-- The maximiser correspondence of the one-period problem is upper hemicontinuous, by the +second half of Berge's theorem. -/ +theorem upperHemicontinuous_argmax (v : S →ᵇ ℝ) : + UpperHemicontinuous (argmax (D.objective v) D.feasible) := fun s => + upperHemicontinuousAt_argmax (D.continuous_uncurry_objective v) D.feasible_nonempty + D.isCompact_feasible (D.upperHemicontinuous_feasible s) (D.lowerHemicontinuous_feasible s) + +section Monotone + +variable [Preorder S] + +/-- The Bellman operator preserves monotonicity when the feasible set grows with the state, +the reward rises with it, and the law of motion is monotone in it. -/ +theorem monotone_bellman + (hfeas : ∀ s s', s ≤ s' → D.feasible s ⊆ D.feasible s') + (hreward : ∀ a, Monotone fun s => D.reward (s, a)) + (htrans : ∀ a, Monotone fun s => D.transition (s, a)) + (v : S →ᵇ ℝ) (hv : Monotone ⇑v) : Monotone ⇑(D.bellman v) := by + intro s s' hss + refine D.bellmanFn_le v fun a ha => ?_ + refine le_trans ?_ (D.le_bellmanFn v (hfeas s s' hss ha)) + have h₁ : D.reward (s, a) ≤ D.reward (s', a) := hreward a hss + have h₂ : v (D.transition (s, a)) ≤ v (D.transition (s', a)) := hv (htrans a hss) + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + simp only [objective] + nlinarith + +end Monotone + +section Concave + +variable [AddCommMonoid S] [Module ℝ S] [AddCommMonoid A] [Module ℝ A] + +/-- The Bellman operator preserves concavity on a convex set `s`, given that the feasible +graph is convex over `s`, the reward is concave along it, the law of motion is affine, and +`s` is forward invariant. + +The mixed action is feasible at the mixed state, so it is *available*; the value at the +mixed state is at least what it delivers, which by concavity of the reward and of the +continuation value is at least the mixture of the two optimal values. -/ +theorem concaveOn_bellman {s : Set S} (hs : Convex ℝ s) + (hgraph : ∀ x ∈ s, ∀ y ∈ s, ∀ ax ∈ D.feasible x, ∀ ay ∈ D.feasible y, + ∀ θ φ : ℝ, 0 ≤ θ → 0 ≤ φ → θ + φ = 1 → + θ • ax + φ • ay ∈ D.feasible (θ • x + φ • y)) + (hreward : ∀ x ∈ s, ∀ y ∈ s, ∀ ax ∈ D.feasible x, ∀ ay ∈ D.feasible y, + ∀ θ φ : ℝ, 0 ≤ θ → 0 ≤ φ → θ + φ = 1 → + θ • D.reward (x, ax) + φ • D.reward (y, ay) + ≤ D.reward (θ • x + φ • y, θ • ax + φ • ay)) + (htrans : ∀ (x y : S) (ax ay : A) (θ φ : ℝ), + D.transition (θ • x + φ • y, θ • ax + φ • ay) + = θ • D.transition (x, ax) + φ • D.transition (y, ay)) + (hinv : ∀ x ∈ s, ∀ a ∈ D.feasible x, D.transition (x, a) ∈ s) + (v : S →ᵇ ℝ) (hv : ConcaveOn ℝ s ⇑v) : ConcaveOn ℝ s ⇑(D.bellman v) := by + refine ⟨hs, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + obtain ⟨ax, hax, heqx, -⟩ := D.exists_optimal_action v x + obtain ⟨ay, hay, heqy, -⟩ := D.exists_optimal_action v y + have hle := D.le_bellmanFn v (hgraph x hx y hy ax hax ay hay θ φ hθ hφ hθφ) + have hr := hreward x hx y hy ax hax ay hay θ φ hθ hφ hθφ + have hvg := hv.2 (hinv x hx ax hax) (hinv y hy ay hay) hθ hφ hθφ + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + have hkey : θ • D.objective v x ax + φ • D.objective v y ay + ≤ D.objective v (θ • x + φ • y) (θ • ax + φ • ay) := by + simp only [objective] + rw [htrans] + have hscaled := mul_le_mul_of_nonneg_left hvg hβ + simp only [smul_eq_mul] at hr hscaled ⊢ + linarith + rw [heqx, heqy] + exact le_trans hkey hle + +end Concave + +section ValueFunction + +/-- The **value function** of the program: the unique fixed point of its Bellman +operator. -/ +noncomputable def valueFunction : S →ᵇ ℝ := + D.blackwell.valueFunction D.discount_lt_one + +/-- The value function satisfies the **Bellman equation**. -/ +theorem bellman_valueFunction : D.bellman D.valueFunction = D.valueFunction := + D.blackwell.isFixedPt_valueFunction D.discount_lt_one + +/-- The Bellman equation has **no other** bounded continuous solution. -/ +theorem eq_valueFunction {v : S →ᵇ ℝ} (hv : D.bellman v = v) : v = D.valueFunction := + D.blackwell.eq_valueFunction D.discount_lt_one hv + +/-- **Value function iteration converges** to the value function from any starting +guess. -/ +theorem tendsto_iterate_valueFunction (v : S →ᵇ ℝ) : + Tendsto (fun n => D.bellman^[n] v) atTop (𝓝 D.valueFunction) := + D.blackwell.tendsto_iterate_valueFunction D.discount_lt_one v + +/-- The geometric error bound for value function iteration on a dynamic program. -/ +theorem norm_iterate_sub_valueFunction_le (v : S →ᵇ ℝ) (n : ℕ) : + ‖D.bellman^[n] v - D.valueFunction‖ + ≤ (D.discount : ℝ) ^ n / (1 - D.discount) * ‖D.bellman v - v‖ := + D.blackwell.norm_iterate_sub_valueFunction_le D.discount_lt_one v n + +/-- In every state, the value function is attained by some feasible action: the **optimal +policy** exists, and the value function is the reward it collects plus the discounted value +of the state it leads to. -/ +theorem exists_optimal_policy (s : S) : + ∃ a ∈ D.feasible s, D.valueFunction s + = D.reward (s, a) + D.discount * D.valueFunction (D.transition (s, a)) := by + obtain ⟨a, ha, heq, -⟩ := D.exists_optimal_action D.valueFunction s + rw [D.bellman_valueFunction] at heq + exact ⟨a, ha, heq⟩ + +/-- **The value function is increasing in the state** when the feasible set grows with the +state, the reward rises with it, and the law of motion is monotone in it. -/ +theorem monotone_valueFunction [Preorder S] + (hfeas : ∀ s s', s ≤ s' → D.feasible s ⊆ D.feasible s') + (hreward : ∀ a, Monotone fun s => D.reward (s, a)) + (htrans : ∀ a, Monotone fun s => D.transition (s, a)) : + Monotone ⇑D.valueFunction := + D.blackwell.monotone_valueFunction D.discount_lt_one + fun v hv => D.monotone_bellman hfeas hreward htrans v hv + +/-- **The value function is concave on `s`** under those conditions. -/ +theorem concaveOn_valueFunction [AddCommMonoid S] [Module ℝ S] [AddCommMonoid A] [Module ℝ A] + {s : Set S} (hs : Convex ℝ s) + (hgraph : ∀ x ∈ s, ∀ y ∈ s, ∀ ax ∈ D.feasible x, ∀ ay ∈ D.feasible y, + ∀ θ φ : ℝ, 0 ≤ θ → 0 ≤ φ → θ + φ = 1 → + θ • ax + φ • ay ∈ D.feasible (θ • x + φ • y)) + (hreward : ∀ x ∈ s, ∀ y ∈ s, ∀ ax ∈ D.feasible x, ∀ ay ∈ D.feasible y, + ∀ θ φ : ℝ, 0 ≤ θ → 0 ≤ φ → θ + φ = 1 → + θ • D.reward (x, ax) + φ • D.reward (y, ay) + ≤ D.reward (θ • x + φ • y, θ • ax + φ • ay)) + (htrans : ∀ (x y : S) (ax ay : A) (θ φ : ℝ), + D.transition (θ • x + φ • y, θ • ax + φ • ay) + = θ • D.transition (x, ax) + φ • D.transition (y, ay)) + (hinv : ∀ x ∈ s, ∀ a ∈ D.feasible x, D.transition (x, a) ∈ s) : + ConcaveOn ℝ s ⇑D.valueFunction := + Blackwell.concaveOn_valueFunction hs D.blackwell D.discount_lt_one + fun v hv => D.concaveOn_bellman hs hgraph hreward htrans hinv v hv + +end ValueFunction + +end DynamicProgram + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean new file mode 100644 index 000000000..13195bac5 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean @@ -0,0 +1,478 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Topology.ContinuousMap.Bounded.Normed +public import Mathlib.Topology.MetricSpace.Contracting + + +/-! +# Blackwell's sufficient conditions + +An operator on bounded continuous functions that is *monotone* and *discounts* is a +contraction. This is the standard route to showing that a Bellman operator has a unique +fixed point: verifying monotonicity and discounting is elementary, whereas verifying the +contraction property directly is not. + +The setting is the space `S →ᵇ ℝ` of bounded continuous real-valued functions on a state +space `S`, with the supremum norm. It is already a complete metric space, which is what +the Banach fixed point theorem needs. + +## Main definitions + +* `LeanEconomics.Blackwell β T` : `T` is monotone and discounts at rate `β`. +* `LeanEconomics.Blackwell.valueFunction` : the unique fixed point of `T`. + +## Main results + +* `LeanEconomics.Blackwell.contractingWith` : Blackwell's conditions imply `T` is a + `β`-contraction. +* `LeanEconomics.Blackwell.isFixedPt_valueFunction`, + `LeanEconomics.Blackwell.eq_valueFunction` : the value function is the unique solution + of the fixed point equation `T v = v`. +* `LeanEconomics.Blackwell.tendsto_iterate_valueFunction` : value function iteration + converges to it, from any starting guess. +* `LeanEconomics.Blackwell.norm_iterate_sub_valueFunction_le` : the geometric error bound + `‖Tⁿ v - v*‖ ≤ βⁿ / (1 - β) * ‖T v - v‖`, which is the stopping rule used in practice. + +## Examples + +* `LeanEconomics.Blackwell.affine` : the affine operator `v ↦ f + β • v` satisfies the + conditions. This is the degenerate case of a Bellman operator in which the agent has no + choice to make, and it witnesses that the hypotheses are consistent. + +## References + +* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*, theorem 3.3. +* Blackwell, *Discounted dynamic programming*, Ann. Math. Statist. 36 (1965). +-/ + +@[expose] public section + +open scoped NNReal +open Filter Topology + +namespace BoundedContinuousFunction + +variable {S : Type*} [TopologicalSpace S] + +/-- Any bounded continuous function is dominated by any other one shifted up by the +distance between them. -/ +theorem le_add_const_norm_sub (v w : S →ᵇ ℝ) : v ≤ w + const S ‖v - w‖ := by + rw [← sub_le_iff_le_add'] + exact sub_nonneg.mp (norm_sub_nonneg (v - w)) + +end BoundedContinuousFunction + +namespace LeanEconomics + +open BoundedContinuousFunction + +variable {S : Type*} [TopologicalSpace S] + +/-- **Blackwell's sufficient conditions** for an operator on bounded continuous functions +to be a contraction of modulus `β`. + +`monotone` says a pointwise larger continuation value cannot lower the operator's value; +`discounting` says that raising the continuation value everywhere by a constant `c` raises +the operator's value by at most `β * c`. For a Bellman operator both are immediate: the +first because the objective is increasing in the continuation value, the second because +the continuation value enters multiplied by the discount factor. -/ +structure Blackwell (β : ℝ≥0) (T : (S →ᵇ ℝ) → (S →ᵇ ℝ)) : Prop where + /-- A pointwise larger continuation value gives a pointwise larger image. -/ + monotone : Monotone T + /-- Adding a constant `c ≥ 0` to the continuation value raises the image by at most + `β * c`. -/ + discounting : ∀ (v : S →ᵇ ℝ) (c : ℝ), 0 ≤ c → T (v + const S c) ≤ T v + const S (β * c) + +namespace Blackwell + +variable {β : ℝ≥0} {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} + +/-- The one-sided bound at the heart of Blackwell's argument: `T v` cannot exceed `T w` +by more than `β` times the distance between `v` and `w`. -/ +theorem apply_le_add_const (h : Blackwell β T) (v w : S →ᵇ ℝ) : + T v ≤ T w + const S (β * ‖v - w‖) := + (h.monotone (le_add_const_norm_sub v w)).trans (h.discounting w _ (norm_nonneg _)) + +/-- Blackwell's conditions make `T` Lipschitz with constant `β` in the supremum norm. -/ +theorem norm_sub_le (h : Blackwell β T) (v w : S →ᵇ ℝ) : ‖T v - T w‖ ≤ β * ‖v - w‖ := by + rw [norm_le (by positivity)] + intro x + have h₁ : T v x ≤ T w x + β * ‖v - w‖ := by simpa using h.apply_le_add_const v w x + have h₂ : T w x ≤ T v x + β * ‖w - v‖ := by simpa using h.apply_le_add_const w v x + rw [norm_sub_rev w v] at h₂ + rw [BoundedContinuousFunction.sub_apply, Real.norm_eq_abs, abs_le] + constructor <;> linarith + +theorem lipschitzWith (h : Blackwell β T) : LipschitzWith β T := + LipschitzWith.of_dist_le_mul fun v w => by + simpa only [dist_eq_norm] using h.norm_sub_le v w + +/-- **Blackwell's theorem**: a monotone operator that discounts at rate `β < 1` is a +contraction of modulus `β`. -/ +theorem contractingWith (h : Blackwell β T) (hβ : β < 1) : ContractingWith β T := + ⟨hβ, h.lipschitzWith⟩ + +section ValueFunction + +variable (h : Blackwell β T) (hβ : β < 1) + +/-- The **value function**: the unique fixed point of an operator satisfying Blackwell's +conditions, obtained from the Banach fixed point theorem. -/ +noncomputable def valueFunction : S →ᵇ ℝ := + ContractingWith.fixedPoint T (h.contractingWith hβ) + +/-- The value function solves the fixed point equation. -/ +theorem isFixedPt_valueFunction : T (h.valueFunction hβ) = h.valueFunction hβ := + (h.contractingWith hβ).fixedPoint_isFixedPt + +/-- The value function is the *only* solution of the fixed point equation. -/ +theorem eq_valueFunction {v : S →ᵇ ℝ} (hv : T v = v) : v = h.valueFunction hβ := + (h.contractingWith hβ).fixedPoint_unique hv + +/-- **Value function iteration converges**, from any starting guess. -/ +theorem tendsto_iterate_valueFunction (v : S →ᵇ ℝ) : + Tendsto (fun n => T^[n] v) atTop (𝓝 (h.valueFunction hβ)) := + (h.contractingWith hβ).tendsto_iterate_fixedPoint v + +/-- The geometric error bound for value function iteration: after `n` iterations from a +guess `v`, the distance to the value function is at most `βⁿ / (1 - β)` times the size of +the first update. This is the a priori bound behind the usual stopping rule. -/ +theorem norm_iterate_sub_valueFunction_le (v : S →ᵇ ℝ) (n : ℕ) : + ‖T^[n] v - h.valueFunction hβ‖ ≤ (β : ℝ) ^ n / (1 - β) * ‖T v - v‖ := by + have key := (h.contractingWith hβ).apriori_dist_iterate_fixedPoint_le v n + rw [dist_eq_norm, dist_eq_norm, norm_sub_rev v (T v)] at key + calc ‖T^[n] v - h.valueFunction hβ‖ ≤ ‖T v - v‖ * (β : ℝ) ^ n / (1 - β) := key + _ = (β : ℝ) ^ n / (1 - β) * ‖T v - v‖ := by ring + +end ValueFunction + +/-- The affine operator `v ↦ f + β • v`, a dynamic program with a single feasible action, +satisfies Blackwell's conditions. -/ +theorem affine (f : S →ᵇ ℝ) (β : ℝ≥0) : + Blackwell β (fun v => f + (β : ℝ) • v) where + monotone := by + intro v w hvw x + have hx : v x ≤ w x := by simpa using hvw x + have hβ : (0 : ℝ) ≤ β := β.coe_nonneg + have : f x + (β : ℝ) * v x ≤ f x + (β : ℝ) * w x := by nlinarith + simpa using this + discounting := by + intro v c _ + refine le_of_eq ?_ + ext x + simp only [coe_add, coe_smul, const_apply, Pi.add_apply, smul_eq_mul] + ring + +end Blackwell + +/-- **A dominated operator has a dominated fixed point.** If one Bellman operator never returns +more than another, its value function never does either. + +This is the workhorse of comparative statics: to compare two economies one only has to compare +their operators at a COMMON continuation value, which is a one-period calculation, and the +comparison of the infinite-horizon values follows. The two need not share a discount factor. +No policy comparison, no differentiability, +nothing about argmaxes -- the whole content is that value function iteration started from the +same place stays ordered. -/ +theorem Blackwell.valueFunction_le {β₁ β₂ : ℝ≥0} {T₁ T₂ : (S →ᵇ ℝ) → (S →ᵇ ℝ)} + (h₁ : Blackwell β₁ T₁) (h₂ : Blackwell β₂ T₂) (hβ₁ : β₁ < 1) (hβ₂ : β₂ < 1) + (hle : ∀ v : S →ᵇ ℝ, ⇑(T₁ v) ≤ ⇑(T₂ v)) (s : S) : + h₁.valueFunction hβ₁ s ≤ h₂.valueFunction hβ₂ s := by + have hiter : ∀ n : ℕ, ⇑(T₁^[n] (0 : S →ᵇ ℝ)) ≤ ⇑(T₂^[n] (0 : S →ᵇ ℝ)) := by + intro n + induction n with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact le_trans (hle _) (h₂.monotone ih) + have hl₁ := h₁.tendsto_iterate_valueFunction hβ₁ (0 : S →ᵇ ℝ) + have hl₂ := h₂.tendsto_iterate_valueFunction hβ₂ (0 : S →ᵇ ℝ) + have hp₁ : Filter.Tendsto (fun n => T₁^[n] (0 : S →ᵇ ℝ) s) Filter.atTop + (𝓝 (h₁.valueFunction hβ₁ s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp hl₁ + have hp₂ : Filter.Tendsto (fun n => T₂^[n] (0 : S →ᵇ ℝ) s) Filter.atTop + (𝓝 (h₂.valueFunction hβ₂ s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp hl₂ + exact le_of_tendsto_of_tendsto hp₁ hp₂ (Filter.Eventually.of_forall fun n => hiter n s) + +/-! ### Monotonicity of the value function + +If the operator preserves monotone functions then so does its fixed point, because the +monotone functions are a *closed* subset of `S →ᵇ ℝ` and value function iteration converges +in norm. Nothing about the fixed point itself is needed, only that the property is closed +and preserved. This is the standard route to comparative statics. -/ + +section Monotone + +variable [Preorder S] + +/-- The monotone bounded continuous functions form a closed set: a supremum-norm limit of +monotone functions is monotone, since evaluation at a point is continuous. -/ +theorem isClosed_monotone : IsClosed {v : S →ᵇ ℝ | Monotone ⇑v} := by + have heq : {v : S →ᵇ ℝ | Monotone ⇑v} + = ⋂ (x : S) (y : S) (_ : x ≤ y), {v : S →ᵇ ℝ | v x ≤ v y} := by + ext v; simp [Monotone] + rw [heq] + refine isClosed_iInter fun x => isClosed_iInter fun y => isClosed_iInter fun _ => ?_ + exact isClosed_le (BoundedContinuousFunction.lipschitz_eval_const x).continuous + (BoundedContinuousFunction.lipschitz_eval_const y).continuous + +/-- **The value function is monotone** whenever the operator preserves monotonicity. -/ +theorem Blackwell.monotone_valueFunction {β : ℝ≥0} {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} + (h : Blackwell β T) (hβ : β < 1) + (hT : ∀ v : S →ᵇ ℝ, Monotone ⇑v → Monotone ⇑(T v)) : + Monotone ⇑(h.valueFunction hβ) := by + have hiter : ∀ n : ℕ, Monotone ⇑(T^[n] (0 : S →ᵇ ℝ)) := by + intro n + induction n with + | zero => intro x y _; simp + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ ih + exact isClosed_monotone.mem_of_tendsto (h.tendsto_iterate_valueFunction hβ 0) + (Filter.Eventually.of_forall hiter) + +end Monotone + +/-! ### Concavity of the value function + +The same closed-set argument as for monotonicity, and the reason it is wanted is the agent +distribution. Defining a distribution over states needs the optimal policy to be a +*function*, and `exists_optimal_action` only gives existence of a maximiser. Uniqueness +comes from strict concavity of the objective in the action, which needs the continuation +value to be concave -- that is, the value function. So this is the first link in the chain +from the value function to the stationary distribution. -/ + +section Concave + +variable {S : Type*} [TopologicalSpace S] [AddCommMonoid S] [Module ℝ S] + +/-- The functions concave on a fixed convex set form a closed set. + +Stated on a set rather than the whole space deliberately. The household models clamp assets +with `max 0 a`, which is convex and so breaks concavity globally -- but `max 0 a = a` on +`[0, ā]`, so concavity does hold on the compact convex region the economics lives in, and +that is the region the agent distribution will be supported on. -/ +theorem isClosed_concaveOn {s : Set S} (hs : Convex ℝ s) : + IsClosed {v : S →ᵇ ℝ | ConcaveOn ℝ s ⇑v} := by + have heq : {v : S →ᵇ ℝ | ConcaveOn ℝ s ⇑v} + = ⋂ (p : S × S × ℝ × ℝ) (_ : p.1 ∈ s ∧ p.2.1 ∈ s ∧ 0 ≤ p.2.2.1 ∧ 0 ≤ p.2.2.2 ∧ + p.2.2.1 + p.2.2.2 = 1), + {v : S →ᵇ ℝ | p.2.2.1 • v p.1 + p.2.2.2 • v p.2.1 + ≤ v (p.2.2.1 • p.1 + p.2.2.2 • p.2.1)} := by + ext v + simp only [Set.mem_ofPred_eq, Set.mem_iInter] + constructor + · rintro ⟨-, hv⟩ p ⟨hx, hy, ha, hb, hab⟩ + exact hv hx hy ha hb hab + · intro hv + exact ⟨hs, fun x hx y hy a b ha hb hab => hv (x, y, a, b) ⟨hx, hy, ha, hb, hab⟩⟩ + rw [heq] + refine isClosed_iInter fun p => isClosed_iInter fun _ => ?_ + exact isClosed_le + ((((BoundedContinuousFunction.lipschitz_eval_const p.1).continuous).const_smul p.2.2.1).add + (((BoundedContinuousFunction.lipschitz_eval_const p.2.1).continuous).const_smul p.2.2.2)) + (BoundedContinuousFunction.lipschitz_eval_const + (p.2.2.1 • p.1 + p.2.2.2 • p.2.1)).continuous + +/-- **The value function is concave on `s`** whenever the operator preserves concavity +on `s`. -/ +theorem Blackwell.concaveOn_valueFunction {β : ℝ≥0} {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} {s : Set S} + (hs : Convex ℝ s) (h : Blackwell β T) (hβ : β < 1) + (hT : ∀ v : S →ᵇ ℝ, ConcaveOn ℝ s ⇑v → ConcaveOn ℝ s ⇑(T v)) : + ConcaveOn ℝ s ⇑(h.valueFunction hβ) := by + have hiter : ∀ n : ℕ, ConcaveOn ℝ s ⇑(T^[n] (0 : S →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact ⟨hs, fun x _ y _ a b _ _ _ => by simp⟩ + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ ih + exact (isClosed_concaveOn hs).mem_of_tendsto (h.tendsto_iterate_valueFunction hβ 0) + (Filter.Eventually.of_forall hiter) + +end Concave + +/-! ### Concavity along slices + +A stochastic household's state is a pair `(assets, income state)`, and that is not a module +over `ℝ` -- there is no sensible convex combination of income states. So concavity cannot be +stated on the state space itself. What is wanted, and what is true, is concavity in assets +for each fixed income state, which is concavity along a family of slices. It is still a +closed condition, so the same argument applies. -/ + +section ConcaveSlice + +variable {ι : Type*} {E : Type*} [AddCommMonoid E] [Module ℝ E] + +/-- Concavity along every slice is a closed condition. -/ +theorem isClosed_forall_concaveOn {s : Set E} (hs : Convex ℝ s) (g : ι → E → S) : + IsClosed {v : S →ᵇ ℝ | ∀ i, ConcaveOn ℝ s fun e => v (g i e)} := by + have heq : {v : S →ᵇ ℝ | ∀ i, ConcaveOn ℝ s fun e => v (g i e)} + = ⋂ (p : ι × E × E × ℝ × ℝ) + (_ : p.2.1 ∈ s ∧ p.2.2.1 ∈ s ∧ 0 ≤ p.2.2.2.1 ∧ 0 ≤ p.2.2.2.2 ∧ + p.2.2.2.1 + p.2.2.2.2 = 1), + {v : S →ᵇ ℝ | p.2.2.2.1 • v (g p.1 p.2.1) + p.2.2.2.2 • v (g p.1 p.2.2.1) + ≤ v (g p.1 (p.2.2.2.1 • p.2.1 + p.2.2.2.2 • p.2.2.1))} := by + ext v + simp only [Set.mem_ofPred_eq, Set.mem_iInter] + constructor + · intro hv p ⟨hx, hy, ha, hb, hab⟩ + exact (hv p.1).2 hx hy ha hb hab + · intro hv i + exact ⟨hs, fun x hx y hy a b ha hb hab => hv (i, x, y, a, b) ⟨hx, hy, ha, hb, hab⟩⟩ + rw [heq] + refine isClosed_iInter fun p => isClosed_iInter fun _ => ?_ + exact isClosed_le + (((BoundedContinuousFunction.lipschitz_eval_const + (g p.1 p.2.1)).continuous.const_smul p.2.2.2.1).add + ((BoundedContinuousFunction.lipschitz_eval_const + (g p.1 p.2.2.1)).continuous.const_smul p.2.2.2.2)) + (BoundedContinuousFunction.lipschitz_eval_const + (g p.1 (p.2.2.2.1 • p.2.1 + p.2.2.2.2 • p.2.2.1))).continuous + +/-- **The value function is concave along every slice** whenever the operator preserves +that. -/ +theorem Blackwell.forall_concaveOn_valueFunction {β : ℝ≥0} {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} + {s : Set E} (hs : Convex ℝ s) (g : ι → E → S) (h : Blackwell β T) (hβ : β < 1) + (hT : ∀ v : S →ᵇ ℝ, (∀ i, ConcaveOn ℝ s fun e => v (g i e)) → + ∀ i, ConcaveOn ℝ s fun e => (T v) (g i e)) : + ∀ i, ConcaveOn ℝ s fun e => (h.valueFunction hβ) (g i e) := by + have hiter : ∀ n : ℕ, ∀ i, ConcaveOn ℝ s fun e => (T^[n] (0 : S →ᵇ ℝ)) (g i e) := by + intro n + induction n with + | zero => exact fun _ => ⟨hs, fun x _ y _ a b _ _ _ => by simp⟩ + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ ih + exact (isClosed_forall_concaveOn hs g).mem_of_tendsto (h.tendsto_iterate_valueFunction hβ 0) + (Filter.Eventually.of_forall hiter) + +end ConcaveSlice + +section Lipschitz + +/-! +### Lipschitz value functions + +The third closed class, after monotone and concave. A value function that is Lipschitz in one +coordinate is what lets a marginal argument run without derivatives: it bounds the marginal +value of that coordinate uniformly, which is what a corner condition needs. + +As with concavity, the property is stated along a family of slices `g i`, since the state +space need not be a metric space in the coordinate of interest. +-/ + +variable {S : Type*} [TopologicalSpace S] {ι : Type*} + +theorem isClosed_forall_lipschitzOn (t : Set ℝ) (L : ℝ) (g : ι → ℝ → S) : + IsClosed {v : S →ᵇ ℝ | ∀ i, ∀ x ∈ t, ∀ y ∈ t, |v (g i x) - v (g i y)| ≤ L * |x - y|} := by + have heq : {v : S →ᵇ ℝ | ∀ i, ∀ x ∈ t, ∀ y ∈ t, |v (g i x) - v (g i y)| ≤ L * |x - y|} + = ⋂ (p : ι × ℝ × ℝ) (_ : p.2.1 ∈ t ∧ p.2.2 ∈ t), + {v : S →ᵇ ℝ | |v (g p.1 p.2.1) - v (g p.1 p.2.2)| ≤ L * |p.2.1 - p.2.2|} := by + ext v + simp only [Set.mem_ofPred_eq, Set.mem_iInter] + exact ⟨fun hv p hp => hv p.1 p.2.1 hp.1 p.2.2 hp.2, fun hv i x hx y hy => hv (i, x, y) ⟨hx, hy⟩⟩ + rw [heq] + refine isClosed_iInter fun p => isClosed_iInter fun _ => ?_ + exact isClosed_le + (((BoundedContinuousFunction.lipschitz_eval_const (g p.1 p.2.1)).continuous.sub + (BoundedContinuousFunction.lipschitz_eval_const (g p.1 p.2.2)).continuous).abs) + continuous_const + +/-- **The value function is Lipschitz along the slices** whenever the operator preserves that +property. The nonnegativity of `L` is what makes the zero function a valid starting point for +the iteration. -/ +theorem Blackwell.forall_lipschitzOn_valueFunction {β : ℝ≥0} {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} + (h : Blackwell β T) (hβ : β < 1) {t : Set ℝ} {L : ℝ} (hL : 0 ≤ L) {g : ι → ℝ → S} + (hT : ∀ v : S →ᵇ ℝ, + (∀ i, ∀ x ∈ t, ∀ y ∈ t, |v (g i x) - v (g i y)| ≤ L * |x - y|) → + ∀ i, ∀ x ∈ t, ∀ y ∈ t, |T v (g i x) - T v (g i y)| ≤ L * |x - y|) : + ∀ i, ∀ x ∈ t, ∀ y ∈ t, + |h.valueFunction hβ (g i x) - h.valueFunction hβ (g i y)| ≤ L * |x - y| := by + have hiter : ∀ n : ℕ, ∀ i, ∀ x ∈ t, ∀ y ∈ t, + |(T^[n] (0 : S →ᵇ ℝ)) (g i x) - (T^[n] (0 : S →ᵇ ℝ)) (g i y)| ≤ L * |x - y| := by + intro n + induction n with + | zero => + intro i x _ y _ + simpa using mul_nonneg hL (abs_nonneg _) + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ ih + exact isClosed_forall_lipschitzOn t L g |>.mem_of_tendsto + (h.tendsto_iterate_valueFunction hβ 0) (Filter.Eventually.of_forall hiter) + +end Lipschitz + +section IncreasingDifferences + +/-! +### Increasing differences + +The fourth closed class, after monotone, concave and Lipschitz. + +A function of two real coordinates has increasing differences when raising the second +coordinate raises the increment in the first. For a value function whose coordinates are +assets and the interest rate, that says the marginal value of wealth rises with the rate -- +and that is the derivative-free form of the condition driving Light (2018) Theorem 1, where +it appears as `f'(·, R₂) ≥ f'(·, R₁)` and is obtained from the envelope theorem. + +Stating it as increments rather than derivatives is what makes it expressible here at all, +since nothing in this development differentiates anything. Its natural home is the augmented +programme of `Models/IncomeFluctuationAugmented.lean`, where the rate is a state coordinate, +so the property is about a single function rather than a family. + +Whether the Bellman operator preserves it is a separate question and is NOT settled here; +Light's argument for the derivative version compares different asset levels across rates via +a rescaling, so it does not transcribe. +-/ + +variable {S : Type*} [TopologicalSpace S] {ι : Type*} + +theorem isClosed_forall_increasingDifferences (s t : Set ℝ) (g : ι → ℝ → ℝ → S) : + IsClosed {v : S →ᵇ ℝ | ∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + v (g i a₂ r₁) - v (g i a₁ r₁) ≤ v (g i a₂ r₂) - v (g i a₁ r₂)} := by + have heq : {v : S →ᵇ ℝ | ∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + v (g i a₂ r₁) - v (g i a₁ r₁) ≤ v (g i a₂ r₂) - v (g i a₁ r₂)} + = ⋂ (p : ι × ℝ × ℝ × ℝ × ℝ) + (_ : p.2.1 ∈ s ∧ p.2.2.1 ∈ s ∧ p.2.2.2.1 ∈ t ∧ p.2.2.2.2 ∈ t ∧ + p.2.1 ≤ p.2.2.1 ∧ p.2.2.2.1 ≤ p.2.2.2.2), + {v : S →ᵇ ℝ | + v (g p.1 p.2.2.1 p.2.2.2.1) - v (g p.1 p.2.1 p.2.2.2.1) + ≤ v (g p.1 p.2.2.1 p.2.2.2.2) - v (g p.1 p.2.1 p.2.2.2.2)} := by + ext v + simp only [Set.mem_ofPred_eq, Set.mem_iInter] + exact ⟨fun hv p hp => hv p.1 p.2.1 hp.1 p.2.2.1 hp.2.1 p.2.2.2.1 hp.2.2.1 p.2.2.2.2 hp.2.2.2.1 + hp.2.2.2.2.1 hp.2.2.2.2.2, + fun hv i a₁ h1 a₂ h2 r₁ h3 r₂ h4 h5 h6 => hv (i, a₁, a₂, r₁, r₂) ⟨h1, h2, h3, h4, h5, h6⟩⟩ + rw [heq] + refine isClosed_iInter fun p => isClosed_iInter fun _ => ?_ + exact isClosed_le + ((BoundedContinuousFunction.lipschitz_eval_const _).continuous.sub + (BoundedContinuousFunction.lipschitz_eval_const _).continuous) + ((BoundedContinuousFunction.lipschitz_eval_const _).continuous.sub + (BoundedContinuousFunction.lipschitz_eval_const _).continuous) + +/-- **The value function has increasing differences** whenever the operator preserves the +property. The zero function has it trivially, which is what lets the iteration start. -/ +theorem Blackwell.forall_increasingDifferences_valueFunction {β : ℝ≥0} + {T : (S →ᵇ ℝ) → (S →ᵇ ℝ)} (h : Blackwell β T) (hβ : β < 1) {s t : Set ℝ} {g : ι → ℝ → ℝ → S} + (hT : ∀ v : S →ᵇ ℝ, + (∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + v (g i a₂ r₁) - v (g i a₁ r₁) ≤ v (g i a₂ r₂) - v (g i a₁ r₂)) → + ∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + T v (g i a₂ r₁) - T v (g i a₁ r₁) ≤ T v (g i a₂ r₂) - T v (g i a₁ r₂)) : + ∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + h.valueFunction hβ (g i a₂ r₁) - h.valueFunction hβ (g i a₁ r₁) + ≤ h.valueFunction hβ (g i a₂ r₂) - h.valueFunction hβ (g i a₁ r₂) := by + have hiter : ∀ n : ℕ, ∀ i, ∀ a₁ ∈ s, ∀ a₂ ∈ s, ∀ r₁ ∈ t, ∀ r₂ ∈ t, a₁ ≤ a₂ → r₁ ≤ r₂ → + (T^[n] (0 : S →ᵇ ℝ)) (g i a₂ r₁) - (T^[n] (0 : S →ᵇ ℝ)) (g i a₁ r₁) + ≤ (T^[n] (0 : S →ᵇ ℝ)) (g i a₂ r₂) - (T^[n] (0 : S →ᵇ ℝ)) (g i a₁ r₂) := by + intro n + induction n with + | zero => intro i a₁ _ a₂ _ r₁ _ r₂ _ _ _; simp + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ ih + exact isClosed_forall_increasingDifferences s t g |>.mem_of_tendsto + (h.tendsto_iterate_valueFunction hβ 0) (Filter.Eventually.of_forall hiter) + +end IncreasingDifferences + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean new file mode 100644 index 000000000..18177c57f --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean @@ -0,0 +1,370 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Bellman +public import LeanPool.AiyagariEquilibrium.Topology.BergeEReal + + +/-! +# Dynamic programs with extended-real rewards + +The reward may take the value `-∞`, as `log c` does at zero consumption. The value function +stays real-valued and bounded, so the contraction theory applies unchanged. + +## What this is for + +Every model in this library that handles utility unbounded below does it with a *floor*: +replace `u` by `max floor u`, which is bounded, and then prove separately that the floor +never binds at an optimum. That argument has to be redone per model, and it fails outright +once the state space is unbounded, because the bound on the value function that it needs is +itself inflated by the floor. Admitting `-∞` removes the device rather than working around +it. + +## Why the value function is still bounded + +Two hypotheses do it, and both are economically ordinary: + +* `reward_le` bounds the reward above on the feasible graph. Only there: off it the reward + may do anything, which matters because an action outside the budget set typically has no + sensible reward at all. +* `select` is a continuous feasible policy whose reward is bounded below by `rewardMin`. + This says the agent always has *some* decent option -- it never faces a situation where + every available action is catastrophic. Without it the value would be `-∞` and there + would be nothing to compute. + +Between them the supremum sits in `[rewardMin - β‖v‖, rewardMax + β‖v‖]`, so it is finite, +and `EReal.toReal` turns it back into a bounded continuous real function. + +## Main results + +* `LeanEconomics.ExtendedProgram.blackwell` : the operator is a contraction. +* `LeanEconomics.ExtendedProgram.exists_optimal_policy` : the value function satisfies the + Bellman equation, with the reward appearing as an `EReal` and no floor anywhere. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +variable {S A : Type*} [TopologicalSpace S] [TopologicalSpace A] + +/-- A dynamic program whose reward may be `-∞`. -/ +structure ExtendedProgram (S A : Type*) [TopologicalSpace S] [TopologicalSpace A] where + /-- The actions available at each state. -/ + feasible : S → Set A + isCompact_feasible : ∀ s, IsCompact (feasible s) + feasible_nonempty : ∀ s, (feasible s).Nonempty + upperHemicontinuous_feasible : UpperHemicontinuous feasible + lowerHemicontinuous_feasible : LowerHemicontinuous feasible + /-- The reward, continuous into the extended reals and possibly `-∞`. -/ + reward : C(S × A, EReal) + /-- An upper bound on the reward, needed only on the feasible graph. -/ + rewardMax : ℝ + reward_le : ∀ s, ∀ a ∈ feasible s, reward (s, a) ≤ (rewardMax : EReal) + /-- A continuous feasible policy. -/ + select : C(S, A) + select_mem : ∀ s, select s ∈ feasible s + /-- Its reward is bounded below: the agent always has a decent option. -/ + rewardMin : ℝ + le_reward_select : ∀ s, (rewardMin : EReal) ≤ reward (s, select s) + /-- The law of motion. -/ + transition : C(S × A, S) + /-- The factor applied to next-period value. -/ + discount : ℝ≥0 + discount_lt_one : discount < 1 + +/-! ### Extending a utility by `-∞` + +Retrofitting a model that uses a floor means turning its real-valued `u`, which is only +sensible on positive consumption, into a continuous `EReal`-valued reward. That is this +lemma, and it is the only shared cost of the retrofit: supply `u` falling to `-∞` at zero +and the extension is continuous. -/ + +/-- A function on the positives, extended by `-∞` to the whole line. -/ +noncomputable def extendBot (u : ℝ → ℝ) : ℝ → EReal := fun c => if 0 < c then (u c : EReal) else ⊥ + +@[simp] theorem extendBot_of_pos {u : ℝ → ℝ} {c : ℝ} (hc : 0 < c) : + extendBot u c = (u c : EReal) := by simp [extendBot, hc] + +@[simp] theorem extendBot_of_nonpos {u : ℝ → ℝ} {c : ℝ} (hc : c ≤ 0) : extendBot u c = ⊥ := by + simp [extendBot, not_lt.mpr hc] + +/-- The extension is monotone when `u` is monotone on the positives. -/ +theorem extendBot_mono {u : ℝ → ℝ} (hu : MonotoneOn u (Ioi 0)) : Monotone (extendBot u) := by + intro x y hxy + rcases le_or_gt x 0 with h | h + · rw [extendBot_of_nonpos h]; exact bot_le + · rw [extendBot_of_pos h, extendBot_of_pos (lt_of_lt_of_le h hxy), EReal.coe_le_coe_iff] + exact hu h (lt_of_lt_of_le h hxy) hxy + +/-- **The extension is continuous** exactly when `u` falls to `-∞` at zero consumption -- +which is what makes a floor necessary in the first place, so nothing extra is being +assumed. -/ +theorem continuous_extendBot {u : ℝ → ℝ} (hcont : ContinuousOn u (Ioi 0)) + (hbot : Tendsto u (𝓝[>] 0) atBot) : Continuous (extendBot u) := by + rw [continuous_iff_continuousAt] + intro c + rcases lt_trichotomy 0 c with h | h | h + · -- positive consumption: the extension agrees with `u` nearby + have hev : extendBot u =ᶠ[𝓝 c] fun x => (u x : EReal) := by + filter_upwards [lt_mem_nhds h] with x hx + exact extendBot_of_pos hx + have hcu : ContinuousAt (fun x => (u x : EReal)) c := + continuous_coe_real_ereal.continuousAt.comp (hcont.continuousAt (Ioi_mem_nhds h)) + exact hcu.congr hev.symm + · -- zero consumption: both sides fall to `⊥` + subst h + rw [ContinuousAt, extendBot_of_nonpos le_rfl, EReal.tendsto_nhds_bot_iff_real] + intro x + rw [← nhdsLE_sup_nhdsGT (0 : ℝ), Filter.eventually_sup] + constructor + · filter_upwards [self_mem_nhdsWithin] with a ha + rw [extendBot_of_nonpos ha] + exact EReal.bot_lt_coe x + · filter_upwards [hbot.eventually (Filter.eventually_lt_atBot x), self_mem_nhdsWithin] + with a ha ha' + rw [extendBot_of_pos ha'] + exact_mod_cast ha + · -- negative consumption: constantly `⊥` nearby + have hev : extendBot u =ᶠ[𝓝 c] fun _ => (⊥ : EReal) := by + filter_upwards [gt_mem_nhds h] with x hx + exact extendBot_of_nonpos hx.le + exact continuousAt_const.congr hev.symm + +namespace ExtendedProgram + +variable (D : ExtendedProgram S A) + +/-- The objective, in the extended reals. -/ +noncomputable def objectiveE (v : S →ᵇ ℝ) (s : S) (a : A) : EReal := + D.reward (s, a) + ((D.discount * v (D.transition (s, a)) : ℝ) : EReal) + +/-- The value of the one-period problem, in the extended reals. -/ +noncomputable def maxE (v : S →ᵇ ℝ) (s : S) : EReal := maxValueE (D.objectiveE v) D.feasible s + +/-- The upper bound on the one-period value. -/ +noncomputable def hiBound (v : S →ᵇ ℝ) : ℝ := D.rewardMax + D.discount * ‖v‖ + +/-- The lower bound on the one-period value, coming from the feasible selection. -/ +noncomputable def loBound (v : S →ᵇ ℝ) : ℝ := D.rewardMin - D.discount * ‖v‖ + +theorem continuous_uncurry_objectiveE (v : S →ᵇ ℝ) : Continuous ↿(D.objectiveE v) := by + have h1 : Continuous fun p : S × A => D.reward p := D.reward.continuous + have h2 : Continuous fun p : S × A => ((D.discount * v (D.transition p) : ℝ) : EReal) := + continuous_coe_real_ereal.comp + (continuous_const.mul (v.continuous.comp D.transition.continuous)) + rw [continuous_iff_continuousAt] + intro p + exact (EReal.continuousAt_add (Or.inr (EReal.coe_ne_bot _)) (Or.inr (EReal.coe_ne_top _))).comp + (h1.continuousAt.prodMk h2.continuousAt) + +theorem abs_discount_smul_le (v : S →ᵇ ℝ) (t : S) : + |(D.discount : ℝ) * v t| ≤ D.discount * ‖v‖ := by + rw [abs_mul, abs_of_nonneg D.discount.coe_nonneg] + exact mul_le_mul_of_nonneg_left + (by simpa [Real.norm_eq_abs] using v.norm_coe_le_norm t) D.discount.coe_nonneg + +theorem maxE_le (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≤ ((D.hiBound v : ℝ) : EReal) := by + refine maxValueE_le fun a ha => ?_ + have h1 : D.reward (s, a) ≤ ((D.rewardMax : ℝ) : EReal) := D.reward_le s a ha + have h2 : ((D.discount * v (D.transition (s, a)) : ℝ) : EReal) + ≤ ((D.discount * ‖v‖ : ℝ) : EReal) := by + rw [EReal.coe_le_coe_iff] + exact (abs_le.mp (D.abs_discount_smul_le v (D.transition (s, a)))).2 + calc D.objectiveE v s a ≤ ((D.rewardMax : ℝ) : EReal) + ((D.discount * ‖v‖ : ℝ) : EReal) := + add_le_add h1 h2 + _ = ((D.hiBound v : ℝ) : EReal) := by rw [hiBound, EReal.coe_add] + +theorem le_maxE (v : S →ᵇ ℝ) (s : S) : ((D.loBound v : ℝ) : EReal) ≤ D.maxE v s := by + refine le_trans ?_ (le_maxValueE (D.select_mem s)) + have h1 : ((D.rewardMin : ℝ) : EReal) ≤ D.reward (s, D.select s) := D.le_reward_select s + have h2 : ((-(D.discount * ‖v‖) : ℝ) : EReal) + ≤ ((D.discount * v (D.transition (s, D.select s)) : ℝ) : EReal) := by + rw [EReal.coe_le_coe_iff] + exact (abs_le.mp (D.abs_discount_smul_le v (D.transition (s, D.select s)))).1 + calc ((D.loBound v : ℝ) : EReal) + = ((D.rewardMin : ℝ) : EReal) + ((-(D.discount * ‖v‖) : ℝ) : EReal) := by + rw [← EReal.coe_add] + congr 1 + _ ≤ _ := add_le_add h1 h2 + +theorem maxE_ne_top (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≠ ⊤ := by + intro h + have hle := D.maxE_le v s + rw [h, top_le_iff] at hle + exact EReal.coe_ne_top _ hle + +theorem maxE_ne_bot (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≠ ⊥ := by + intro h + have hle := D.le_maxE v s + rw [h, le_bot_iff] at hle + exact EReal.coe_ne_bot _ hle + +/-- The Bellman operator as a bare real-valued function. -/ +noncomputable def bellmanFn (v : S →ᵇ ℝ) (s : S) : ℝ := (D.maxE v s).toReal + +@[simp] +theorem coe_bellmanFn (v : S →ᵇ ℝ) (s : S) : ((D.bellmanFn v s : ℝ) : EReal) = D.maxE v s := + EReal.coe_toReal (D.maxE_ne_top v s) (D.maxE_ne_bot v s) + +theorem le_bellmanFn (v : S →ᵇ ℝ) {s : S} {a : A} (ha : a ∈ D.feasible s) : + D.objectiveE v s a ≤ ((D.bellmanFn v s : ℝ) : EReal) := by + rw [D.coe_bellmanFn] + exact le_maxValueE ha + +theorem bellmanFn_le (v : S →ᵇ ℝ) {s : S} {c : ℝ} + (h : ∀ a ∈ D.feasible s, D.objectiveE v s a ≤ (c : EReal)) : D.bellmanFn v s ≤ c := by + have hle : D.maxE v s ≤ (c : EReal) := maxValueE_le h + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at hle + exact hle + +theorem bellmanFn_bounds (v : S →ᵇ ℝ) (s : S) : + D.loBound v ≤ D.bellmanFn v s ∧ D.bellmanFn v s ≤ D.hiBound v := by + constructor + · have := D.le_maxE v s + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at this + exact this + · have := D.maxE_le v s + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at this + exact this + +theorem abs_bellmanFn_le (v : S →ᵇ ℝ) (s : S) : + |D.bellmanFn v s| ≤ max |D.loBound v| |D.hiBound v| := by + obtain ⟨h1, h2⟩ := D.bellmanFn_bounds v s + rw [abs_le] + constructor + · have : -|D.loBound v| ≤ D.loBound v := neg_abs_le _ + have h3 : -(max |D.loBound v| |D.hiBound v|) ≤ -|D.loBound v| := neg_le_neg (le_max_left _ _) + linarith + · exact h2.trans ((le_abs_self _).trans (le_max_right _ _)) + +theorem continuous_bellmanFn (v : S →ᵇ ℝ) : Continuous (D.bellmanFn v) := by + have hcont : Continuous (D.maxE v) := + continuous_maxValueE (D.continuous_uncurry_objectiveE v) D.feasible_nonempty + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible + refine EReal.continuousOn_toReal.comp_continuous hcont fun s => ?_ + simp only [mem_compl_iff, mem_insert_iff, mem_singleton_iff, not_or] + exact ⟨D.maxE_ne_bot v s, D.maxE_ne_top v s⟩ + +/-- The **Bellman operator** on bounded continuous functions, with an extended-real +reward. -/ +noncomputable def bellman (v : S →ᵇ ℝ) : S →ᵇ ℝ := + BoundedContinuousFunction.ofNormedAddCommGroup (D.bellmanFn v) (D.continuous_bellmanFn v) + (max |D.loBound v| |D.hiBound v|) + fun s => by simpa [Real.norm_eq_abs] using D.abs_bellmanFn_le v s + +@[simp] +theorem bellman_apply (v : S →ᵇ ℝ) (s : S) : D.bellman v s = D.bellmanFn v s := rfl + +/-- **Blackwell's conditions hold.** The extended-real reward is a common summand on both +sides of each inequality, so it plays no part: monotonicity and discounting are facts about +the continuation term alone. -/ +theorem blackwell : Blackwell D.discount D.bellman where + monotone := by + intro v w hvw s + refine D.bellmanFn_le v fun a ha => ?_ + refine le_trans ?_ (D.le_bellmanFn w ha) + simp only [objectiveE] + refine add_le_add (le_refl _) ?_ + rw [EReal.coe_le_coe_iff] + exact mul_le_mul_of_nonneg_left (by simpa using hvw (D.transition (s, a))) + D.discount.coe_nonneg + discounting := by + intro v c _ s + have hgoal : ((D.bellman v + const S ((D.discount : ℝ) * c)) s : ℝ) + = D.bellmanFn v s + (D.discount : ℝ) * c := by simp + refine le_trans (D.bellmanFn_le (v + const S c) + (c := D.bellmanFn v s + (D.discount : ℝ) * c) fun a ha => ?_) (le_of_eq hgoal.symm) + have hsplit : D.objectiveE (v + const S c) s a + = D.objectiveE v s a + ((D.discount * c : ℝ) : EReal) := by + simp only [objectiveE, coe_add, const_apply, Pi.add_apply] + rw [show (D.discount : ℝ) * (v (D.transition (s, a)) + c) + = D.discount * v (D.transition (s, a)) + D.discount * c by ring, + EReal.coe_add, add_assoc] + rw [hsplit] + calc D.objectiveE v s a + ((D.discount * c : ℝ) : EReal) + ≤ ((D.bellmanFn v s : ℝ) : EReal) + ((D.discount * c : ℝ) : EReal) := + add_le_add (D.le_bellmanFn v ha) (le_refl _) + _ = ((D.bellmanFn v s + D.discount * c : ℝ) : EReal) := (EReal.coe_add _ _).symm + +/-- The supremum is attained: an optimal action exists at every state. -/ +theorem exists_optimal_action (v : S →ᵇ ℝ) (s : S) : + ∃ a ∈ D.feasible s, D.objectiveE v s a = ((D.bellmanFn v s : ℝ) : EReal) := by + obtain ⟨a, ha, heq⟩ := maxValueE_eq (D.continuous_uncurry_objectiveE v) + (D.feasible_nonempty s) (D.isCompact_feasible s) + exact ⟨a, ha, by rw [D.coe_bellmanFn]; exact heq.symm⟩ + +/-- The maximiser correspondence is upper hemicontinuous, by the argmax half of Berge for +extended-real objectives. -/ +theorem upperHemicontinuous_argmax (v : S →ᵇ ℝ) : + UpperHemicontinuous (argmax (D.objectiveE v) D.feasible) := + upperHemicontinuous_argmaxE (D.continuous_uncurry_objectiveE v) + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible + +section Monotone + +variable [Preorder S] + +/-- The operator preserves monotonicity under the same conditions as in the real case. -/ +theorem monotone_bellman + (hfeas : ∀ s s', s ≤ s' → D.feasible s ⊆ D.feasible s') + (hreward : ∀ a, Monotone fun s => D.reward (s, a)) + (htrans : ∀ a, Monotone fun s => D.transition (s, a)) + (v : S →ᵇ ℝ) (hv : Monotone ⇑v) : Monotone ⇑(D.bellman v) := by + intro s s' hss + refine D.bellmanFn_le v fun a ha => ?_ + refine le_trans ?_ (D.le_bellmanFn v (hfeas s s' hss ha)) + refine add_le_add (hreward a hss) ?_ + rw [EReal.coe_le_coe_iff] + exact mul_le_mul_of_nonneg_left (hv (htrans a hss)) D.discount.coe_nonneg + +end Monotone + +section ValueFunction + +/-- The **value function** of the program. -/ +noncomputable def valueFunction : S →ᵇ ℝ := D.blackwell.valueFunction D.discount_lt_one + +theorem bellman_valueFunction : D.bellman D.valueFunction = D.valueFunction := + D.blackwell.isFixedPt_valueFunction D.discount_lt_one + +theorem eq_valueFunction {v : S →ᵇ ℝ} (hv : D.bellman v = v) : v = D.valueFunction := + D.blackwell.eq_valueFunction D.discount_lt_one hv + +theorem tendsto_iterate_valueFunction (v : S →ᵇ ℝ) : + Tendsto (fun n => D.bellman^[n] v) atTop (𝓝 D.valueFunction) := + D.blackwell.tendsto_iterate_valueFunction D.discount_lt_one v + +/-- **The Bellman equation**, with the reward an extended real and no floor anywhere. -/ +theorem exists_optimal_policy (s : S) : + ∃ a ∈ D.feasible s, ((D.valueFunction s : ℝ) : EReal) + = D.reward (s, a) + ((D.discount * D.valueFunction (D.transition (s, a)) : ℝ) : EReal) := by + obtain ⟨a, ha, heq⟩ := D.exists_optimal_action D.valueFunction s + refine ⟨a, ha, ?_⟩ + have hfix : D.bellmanFn D.valueFunction s = D.valueFunction s := by + conv_rhs => rw [← D.bellman_valueFunction] + rfl + rw [← hfix, ← heq] + rfl + +/-- **The value function is monotone** under the same conditions as in the real case. -/ +theorem monotone_valueFunction [Preorder S] + (hfeas : ∀ s s', s ≤ s' → D.feasible s ⊆ D.feasible s') + (hreward : ∀ a, Monotone fun s => D.reward (s, a)) + (htrans : ∀ a, Monotone fun s => D.transition (s, a)) : + Monotone ⇑D.valueFunction := + D.blackwell.monotone_valueFunction D.discount_lt_one + fun v hv => D.monotone_bellman hfeas hreward htrans v hv + +end ValueFunction + +end ExtendedProgram + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean new file mode 100644 index 000000000..b45efe5ee --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean @@ -0,0 +1,455 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Extended +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Stochastic + + +/-! +# Stochastic dynamic programs with extended-real rewards + +`LeanEconomics.DynamicProgramming.Extended` allows a reward of `-∞` but has a deterministic +law of motion; `LeanEconomics.DynamicProgramming.Stochastic` has finitely many shocks but a +real-valued reward. This file is the combination, and it is what a stochastic model with +log or CRRA utility needs if it is not to carry a floor. + +## Why the combination is routine + +The expectation `∑ z, p z * v (g z)` is a sum of *real* numbers, since value functions stay +real-valued. So the objective is again `reward + ↑(real)`: an extended real plus a finite +coercion, exactly the shape `Extended` already handles. Neither half has to be redone -- +the reward's `-∞` never meets the expectation's arithmetic, and `⊥ + ⊤` never arises. + +Accordingly the two facts about the expectation are the same two as in the real stochastic +file: `prob_nonneg` gives monotonicity, `prob_sum` gives discounting. + +## Main results + +* `LeanEconomics.ExtendedStochasticProgram.blackwell` : the operator is a contraction. +* `LeanEconomics.ExtendedStochasticProgram.exists_optimal_policy` : the stochastic Bellman + equation, with the reward an extended real and no floor. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +variable {S A Z : Type*} [TopologicalSpace S] [TopologicalSpace A] [Fintype Z] + +/-- A stochastic dynamic program whose reward may be `-∞`. -/ +structure ExtendedStochasticProgram (S A Z : Type*) [TopologicalSpace S] [TopologicalSpace A] + [Fintype Z] where + /-- The actions available at each state. -/ + feasible : S → Set A + isCompact_feasible : ∀ s, IsCompact (feasible s) + feasible_nonempty : ∀ s, (feasible s).Nonempty + upperHemicontinuous_feasible : UpperHemicontinuous feasible + lowerHemicontinuous_feasible : LowerHemicontinuous feasible + /-- The reward, possibly `-∞`. -/ + reward : C(S × A, EReal) + /-- An upper bound on the reward, needed only on the feasible graph. -/ + rewardMax : ℝ + reward_le : ∀ s, ∀ a ∈ feasible s, reward (s, a) ≤ (rewardMax : EReal) + /-- A continuous feasible policy whose reward is bounded below. -/ + select : C(S, A) + select_mem : ∀ s, select s ∈ feasible s + /-- A uniform one-period payoff available through the feasible policy `select`. -/ + rewardMin : ℝ + le_reward_select : ∀ s, (rewardMin : EReal) ≤ reward (s, select s) + /-- The state reached when shock `z` occurs. -/ + transition : Z → C(S × A, S) + /-- The probability that shock `z` occurs. -/ + prob : Z → C(S × A, ℝ) + prob_nonneg : ∀ z p, 0 ≤ prob z p + prob_sum : ∀ p, ∑ z, prob z p = 1 + /-- The factor applied to next-period value. -/ + discount : ℝ≥0 + discount_lt_one : discount < 1 + +namespace ExtendedStochasticProgram + +variable (D : ExtendedStochasticProgram S A Z) + +/-- The expected continuation value: a sum of reals, so it never meets the reward's `-∞`. -/ +noncomputable def expect (v : S →ᵇ ℝ) (p : S × A) : ℝ := + ∑ z, D.prob z p * v (D.transition z p) + +/-- The objective, in the extended reals. -/ +noncomputable def objectiveE (v : S →ᵇ ℝ) (s : S) (a : A) : EReal := + D.reward (s, a) + ((D.discount * D.expect v (s, a) : ℝ) : EReal) + +/-- The value of the one-period problem, in the extended reals. -/ +noncomputable def maxE (v : S →ᵇ ℝ) (s : S) : EReal := maxValueE (D.objectiveE v) D.feasible s + +/-- The upper bound on the one-period value. -/ +noncomputable def hiBound (v : S →ᵇ ℝ) : ℝ := D.rewardMax + D.discount * ‖v‖ + +/-- The lower bound on the one-period value. -/ +noncomputable def loBound (v : S →ᵇ ℝ) : ℝ := D.rewardMin - D.discount * ‖v‖ + +theorem continuous_expect (v : S →ᵇ ℝ) : Continuous (D.expect v) := + continuous_finsetSum _ fun z _ => + (D.prob z).continuous.mul (v.continuous.comp (D.transition z).continuous) + +theorem continuous_uncurry_objectiveE (v : S →ᵇ ℝ) : Continuous ↿(D.objectiveE v) := by + have h1 : Continuous fun p : S × A => D.reward p := D.reward.continuous + have h2 : Continuous fun p : S × A => ((D.discount * D.expect v p : ℝ) : EReal) := + continuous_coe_real_ereal.comp (continuous_const.mul (D.continuous_expect v)) + rw [continuous_iff_continuousAt] + intro p + exact (EReal.continuousAt_add (Or.inr (EReal.coe_ne_bot _)) (Or.inr (EReal.coe_ne_top _))).comp + (h1.continuousAt.prodMk h2.continuousAt) + +/-- An average of values of `v` is no larger than the size of `v`. -/ +theorem abs_expect_le (v : S →ᵇ ℝ) (p : S × A) : |D.expect v p| ≤ ‖v‖ := by + calc |D.expect v p| ≤ ∑ z, |D.prob z p * v (D.transition z p)| := + Finset.abs_sum_le_sum_abs _ _ + _ = ∑ z, D.prob z p * |v (D.transition z p)| := by + refine Finset.sum_congr rfl fun z _ => ?_ + rw [abs_mul, abs_of_nonneg (D.prob_nonneg z p)] + _ ≤ ∑ z, D.prob z p * ‖v‖ := by + refine Finset.sum_le_sum fun z _ => mul_le_mul_of_nonneg_left ?_ (D.prob_nonneg z p) + simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (D.transition z p) + _ = ‖v‖ := by rw [← Finset.sum_mul, D.prob_sum, one_mul] + +theorem maxE_le (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≤ ((D.hiBound v : ℝ) : EReal) := by + refine maxValueE_le fun a ha => ?_ + have h1 : D.reward (s, a) ≤ ((D.rewardMax : ℝ) : EReal) := D.reward_le s a ha + have h2 : ((D.discount * D.expect v (s, a) : ℝ) : EReal) ≤ ((D.discount * ‖v‖ : ℝ) : EReal) := by + rw [EReal.coe_le_coe_iff] + exact mul_le_mul_of_nonneg_left (abs_le.mp (D.abs_expect_le v (s, a))).2 D.discount.coe_nonneg + calc D.objectiveE v s a ≤ ((D.rewardMax : ℝ) : EReal) + ((D.discount * ‖v‖ : ℝ) : EReal) := + add_le_add h1 h2 + _ = ((D.hiBound v : ℝ) : EReal) := by rw [hiBound, EReal.coe_add] + +theorem le_maxE (v : S →ᵇ ℝ) (s : S) : ((D.loBound v : ℝ) : EReal) ≤ D.maxE v s := by + refine le_trans ?_ (le_maxValueE (D.select_mem s)) + have h1 : ((D.rewardMin : ℝ) : EReal) ≤ D.reward (s, D.select s) := D.le_reward_select s + have h2 : ((-(D.discount * ‖v‖) : ℝ) : EReal) + ≤ ((D.discount * D.expect v (s, D.select s) : ℝ) : EReal) := by + rw [EReal.coe_le_coe_iff] + have := (abs_le.mp (D.abs_expect_le v (s, D.select s))).1 + have h := mul_le_mul_of_nonneg_left this D.discount.coe_nonneg + linarith + calc ((D.loBound v : ℝ) : EReal) + = ((D.rewardMin : ℝ) : EReal) + ((-(D.discount * ‖v‖) : ℝ) : EReal) := by + rw [← EReal.coe_add] + congr 1 + _ ≤ _ := add_le_add h1 h2 + +theorem maxE_ne_top (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≠ ⊤ := by + intro h + have hle := D.maxE_le v s + rw [h, top_le_iff] at hle + exact EReal.coe_ne_top _ hle + +theorem maxE_ne_bot (v : S →ᵇ ℝ) (s : S) : D.maxE v s ≠ ⊥ := by + intro h + have hle := D.le_maxE v s + rw [h, le_bot_iff] at hle + exact EReal.coe_ne_bot _ hle + +/-- The Bellman operator as a bare real-valued function. -/ +noncomputable def bellmanFn (v : S →ᵇ ℝ) (s : S) : ℝ := (D.maxE v s).toReal + +@[simp] +theorem coe_bellmanFn (v : S →ᵇ ℝ) (s : S) : ((D.bellmanFn v s : ℝ) : EReal) = D.maxE v s := + EReal.coe_toReal (D.maxE_ne_top v s) (D.maxE_ne_bot v s) + +theorem le_bellmanFn (v : S →ᵇ ℝ) {s : S} {a : A} (ha : a ∈ D.feasible s) : + D.objectiveE v s a ≤ ((D.bellmanFn v s : ℝ) : EReal) := by + rw [D.coe_bellmanFn] + exact le_maxValueE ha + +theorem bellmanFn_le (v : S →ᵇ ℝ) {s : S} {c : ℝ} + (h : ∀ a ∈ D.feasible s, D.objectiveE v s a ≤ (c : EReal)) : D.bellmanFn v s ≤ c := by + have hle : D.maxE v s ≤ (c : EReal) := maxValueE_le h + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at hle + exact hle + +theorem bellmanFn_bounds (v : S →ᵇ ℝ) (s : S) : + D.loBound v ≤ D.bellmanFn v s ∧ D.bellmanFn v s ≤ D.hiBound v := by + constructor + · have := D.le_maxE v s + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at this + exact this + · have := D.maxE_le v s + rw [← D.coe_bellmanFn, EReal.coe_le_coe_iff] at this + exact this + +theorem abs_bellmanFn_le (v : S →ᵇ ℝ) (s : S) : + |D.bellmanFn v s| ≤ max |D.loBound v| |D.hiBound v| := by + obtain ⟨h1, h2⟩ := D.bellmanFn_bounds v s + rw [abs_le] + constructor + · have h3 : -|D.loBound v| ≤ D.loBound v := neg_abs_le _ + have h4 : -(max |D.loBound v| |D.hiBound v|) ≤ -|D.loBound v| := neg_le_neg (le_max_left _ _) + linarith + · exact h2.trans ((le_abs_self _).trans (le_max_right _ _)) + +theorem continuous_bellmanFn (v : S →ᵇ ℝ) : Continuous (D.bellmanFn v) := by + have hcont : Continuous (D.maxE v) := + continuous_maxValueE (D.continuous_uncurry_objectiveE v) D.feasible_nonempty + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible + refine EReal.continuousOn_toReal.comp_continuous hcont fun s => ?_ + simp only [mem_compl_iff, mem_insert_iff, mem_singleton_iff, not_or] + exact ⟨D.maxE_ne_bot v s, D.maxE_ne_top v s⟩ + +/-- The **stochastic Bellman operator** with an extended-real reward. -/ +noncomputable def bellman (v : S →ᵇ ℝ) : S →ᵇ ℝ := + BoundedContinuousFunction.ofNormedAddCommGroup (D.bellmanFn v) (D.continuous_bellmanFn v) + (max |D.loBound v| |D.hiBound v|) + fun s => by simpa [Real.norm_eq_abs] using D.abs_bellmanFn_le v s + +@[simp] +theorem bellman_apply (v : S →ᵇ ℝ) (s : S) : D.bellman v s = D.bellmanFn v s := rfl + +/-- Adding a constant to the continuation value adds exactly that constant to the +expectation: this is `prob_sum`. -/ +theorem expect_add_const (v : S →ᵇ ℝ) (c : ℝ) (p : S × A) : + D.expect (v + const S c) p = D.expect v p + c := by + have hterm : ∀ z : Z, D.prob z p * (v + const S c) (D.transition z p) + = D.prob z p * v (D.transition z p) + D.prob z p * c := fun z => by + simp only [coe_add, const_apply, Pi.add_apply]; ring + simp only [expect] + rw [Finset.sum_congr rfl fun z _ => hterm z, Finset.sum_add_distrib, ← Finset.sum_mul, + D.prob_sum, one_mul] + +/-- **Blackwell's conditions hold.** -/ +theorem blackwell : Blackwell D.discount D.bellman where + monotone := by + intro v w hvw s + refine D.bellmanFn_le v fun a ha => ?_ + refine le_trans ?_ (D.le_bellmanFn w ha) + simp only [objectiveE] + refine add_le_add (le_refl _) ?_ + rw [EReal.coe_le_coe_iff] + refine mul_le_mul_of_nonneg_left ?_ D.discount.coe_nonneg + refine Finset.sum_le_sum fun z _ => mul_le_mul_of_nonneg_left ?_ (D.prob_nonneg z _) + simpa using hvw (D.transition z (s, a)) + discounting := by + intro v c _ s + have hgoal : ((D.bellman v + const S ((D.discount : ℝ) * c)) s : ℝ) + = D.bellmanFn v s + (D.discount : ℝ) * c := by simp + refine le_trans (D.bellmanFn_le (v + const S c) + (c := D.bellmanFn v s + (D.discount : ℝ) * c) fun a ha => ?_) (le_of_eq hgoal.symm) + have hsplit : D.objectiveE (v + const S c) s a + = D.objectiveE v s a + ((D.discount * c : ℝ) : EReal) := by + simp only [objectiveE, D.expect_add_const] + rw [show (D.discount : ℝ) * (D.expect v (s, a) + c) + = D.discount * D.expect v (s, a) + D.discount * c by ring, + EReal.coe_add, add_assoc] + rw [hsplit] + calc D.objectiveE v s a + ((D.discount * c : ℝ) : EReal) + ≤ ((D.bellmanFn v s : ℝ) : EReal) + ((D.discount * c : ℝ) : EReal) := + add_le_add (D.le_bellmanFn v ha) (le_refl _) + _ = ((D.bellmanFn v s + D.discount * c : ℝ) : EReal) := (EReal.coe_add _ _).symm + +/-- The supremum is attained: an optimal action exists at every state. -/ +theorem exists_optimal_action (v : S →ᵇ ℝ) (s : S) : + ∃ a ∈ D.feasible s, D.objectiveE v s a = ((D.bellmanFn v s : ℝ) : EReal) := by + obtain ⟨a, ha, heq⟩ := maxValueE_eq (D.continuous_uncurry_objectiveE v) + (D.feasible_nonempty s) (D.isCompact_feasible s) + exact ⟨a, ha, by rw [D.coe_bellmanFn]; exact heq.symm⟩ + +/-- The maximiser correspondence is upper hemicontinuous. -/ +theorem upperHemicontinuous_argmax (v : S →ᵇ ℝ) : + UpperHemicontinuous (argmax (D.objectiveE v) D.feasible) := + upperHemicontinuous_argmaxE (D.continuous_uncurry_objectiveE v) + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible + +section ValueFunction + +/-- The **value function** of the program. -/ +noncomputable def valueFunction : S →ᵇ ℝ := D.blackwell.valueFunction D.discount_lt_one + +theorem bellman_valueFunction : D.bellman D.valueFunction = D.valueFunction := + D.blackwell.isFixedPt_valueFunction D.discount_lt_one + +/-- **An explicit bound on the value function**, in terms of the reward bounds alone. + +The right-hand side mentions no state and no fixed point, which is exactly what is needed to +make bounds derived from `‖valueFunction‖` uniform across a FAMILY of programs: the reward +bounds of a family are typically easy to control, whereas its value functions are not. -/ +theorem norm_valueFunction_le : + ‖D.valueFunction‖ ≤ max |D.rewardMin| |D.rewardMax| / (1 - D.discount) := by + have hβ1 : (D.discount : ℝ) < 1 := by exact_mod_cast D.discount_lt_one + have hβ0 : (0 : ℝ) ≤ (D.discount : ℝ) := D.discount.coe_nonneg + set M : ℝ := max |D.rewardMin| |D.rewardMax| with hM + have hM0 : 0 ≤ M := le_trans (abs_nonneg _) (le_max_left _ _) + have hV0 : 0 ≤ ‖D.valueFunction‖ := norm_nonneg _ + have hkey : ‖D.valueFunction‖ ≤ M + D.discount * ‖D.valueFunction‖ := by + refine (BoundedContinuousFunction.norm_le (by positivity)).mpr fun s => ?_ + have hfix : D.valueFunction s = D.bellmanFn D.valueFunction s := by + rw [← D.bellman_apply, D.bellman_valueFunction] + rw [Real.norm_eq_abs, hfix] + refine le_trans (D.abs_bellmanFn_le _ s) (max_le ?_ ?_) + · have h1 : |D.rewardMin| ≤ M := le_max_left _ _ + rw [abs_le] at h1 ⊢ + constructor <;> · simp only [loBound]; nlinarith [h1.1, h1.2] + · have h1 : |D.rewardMax| ≤ M := le_max_right _ _ + rw [abs_le] at h1 ⊢ + constructor <;> · simp only [hiBound]; nlinarith [h1.1, h1.2] + rw [le_div_iff₀ (by linarith)] + nlinarith [hkey] + +theorem eq_valueFunction {v : S →ᵇ ℝ} (hv : D.bellman v = v) : v = D.valueFunction := + D.blackwell.eq_valueFunction D.discount_lt_one hv + +theorem tendsto_iterate_valueFunction (v : S →ᵇ ℝ) : + Tendsto (fun n => D.bellman^[n] v) atTop (𝓝 D.valueFunction) := + D.blackwell.tendsto_iterate_valueFunction D.discount_lt_one v + +/-- The constant plan is not improved on by the operator, so value function iteration +started there stays below it. -/ +theorem bellman_const_le {c : ℝ} (hc : D.rewardMax + D.discount * c ≤ c) : + D.bellman (const S c) ≤ const S c := by + intro s + refine D.bellmanFn_le (const S c) fun a ha => ?_ + have hexp : D.expect (const S c) (s, a) = c := by + have := D.expect_add_const (0 : S →ᵇ ℝ) c (s, a) + simpa [expect] using this + simp only [objectiveE, hexp] + calc D.reward (s, a) + ((D.discount * c : ℝ) : EReal) + ≤ ((D.rewardMax : ℝ) : EReal) + ((D.discount * c : ℝ) : EReal) := + add_le_add (D.reward_le s a ha) (le_refl _) + _ = ((D.rewardMax + D.discount * c : ℝ) : EReal) := (EReal.coe_add _ _).symm + _ ≤ ((c : ℝ) : EReal) := EReal.coe_le_coe_iff.mpr hc + +theorem le_bellman_const {c : ℝ} (hc : c ≤ D.rewardMin + D.discount * c) : + const S c ≤ D.bellman (const S c) := by + intro s + have hexp : D.expect (const S c) (s, D.select s) = c := by + have := D.expect_add_const (0 : S →ᵇ ℝ) c (s, D.select s) + simpa [expect] using this + have hle : ((c : ℝ) : EReal) ≤ D.objectiveE (const S c) s (D.select s) := by + simp only [objectiveE, hexp] + calc ((c : ℝ) : EReal) ≤ ((D.rewardMin + D.discount * c : ℝ) : EReal) := + EReal.coe_le_coe_iff.mpr hc + _ = ((D.rewardMin : ℝ) : EReal) + ((D.discount * c : ℝ) : EReal) := EReal.coe_add _ _ + _ ≤ D.reward (s, D.select s) + ((D.discount * c : ℝ) : EReal) := + add_le_add (D.le_reward_select s) (le_refl _) + have := le_trans hle (D.le_bellmanFn (const S c) (D.select_mem s)) + simpa using EReal.coe_le_coe_iff.mp this + +/-- **The value function never exceeds the best constant plan.** Unlike `norm_valueFunction_le` +this is one-sided, and the pair of bounds caps the OSCILLATION of the value function by +`(rewardMax - rewardMin) / (1 - β)` -- a quantity that does not move when a constant is added to +the reward, which the sup norm does. That matters for CES: `c ^ (1-γ) / (1-γ)` differs from `log` +by the constant `1 / (1-γ)`, so any bound built from `‖V‖` blows up as `γ → 1` while the model it +describes does not. -/ +theorem valueFunction_le_const {c : ℝ} (hc : D.rewardMax + D.discount * c ≤ c) (s : S) : + D.valueFunction s ≤ c := by + have hiter : ∀ n : ℕ, D.bellman^[n] (const S c) ≤ const S c := by + intro n + induction n with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply'] + exact le_trans (D.blackwell.monotone ih) (D.bellman_const_le hc) + have hlim := D.tendsto_iterate_valueFunction (const S c) + have hpt : Filter.Tendsto (fun n => D.bellman^[n] (const S c) s) Filter.atTop + (𝓝 (D.valueFunction s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp hlim + exact le_of_tendsto' hpt fun n => hiter n s + +theorem const_le_valueFunction {c : ℝ} (hc : c ≤ D.rewardMin + D.discount * c) (s : S) : + c ≤ D.valueFunction s := by + have hiter : ∀ n : ℕ, (const S c : S →ᵇ ℝ) ≤ D.bellman^[n] (const S c) := by + intro n + induction n with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply'] + exact le_trans (D.le_bellman_const hc) (D.blackwell.monotone ih) + have hlim := D.tendsto_iterate_valueFunction (const S c) + have hpt : Filter.Tendsto (fun n => D.bellman^[n] (const S c) s) Filter.atTop + (𝓝 (D.valueFunction s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp hlim + exact ge_of_tendsto' hpt fun n => hiter n s + +/-- **A dominated operator has a dominated value function, on an invariant class.** The +domination need only hold on a class of continuation values that contains `0` and that the +dominated operator preserves, since value function iteration from `0` never leaves it. That is +what lets the discount factor be compared: a more patient agent's operator dominates only on +NONNEGATIVE continuation values, which is exactly the class the iteration stays in when the +reward is nonnegative. -/ +theorem valueFunction_le_of_bellman_le_on (D₁ D₂ : ExtendedStochasticProgram S A Z) + (C : (S →ᵇ ℝ) → Prop) (hC0 : C 0) (hC : ∀ v, C v → C (D₁.bellman v)) + (hle : ∀ v : S →ᵇ ℝ, C v → ⇑(D₁.bellman v) ≤ ⇑(D₂.bellman v)) (s : S) : + D₁.valueFunction s ≤ D₂.valueFunction s := by + have hmemC : ∀ n : ℕ, C (D₁.bellman^[n] (0 : S →ᵇ ℝ)) := by + intro n + induction n with + | zero => simpa using hC0 + | succ k ih => rw [Function.iterate_succ_apply']; exact hC _ ih + have hiter : ∀ n : ℕ, + ⇑(D₁.bellman^[n] (0 : S →ᵇ ℝ)) ≤ ⇑(D₂.bellman^[n] (0 : S →ᵇ ℝ)) := by + intro n + induction n with + | zero => simp + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact le_trans (hle _ (hmemC k)) (D₂.blackwell.monotone ih) + have hp₁ : Filter.Tendsto (fun n => D₁.bellman^[n] (0 : S →ᵇ ℝ) s) Filter.atTop + (𝓝 (D₁.valueFunction s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp + (D₁.tendsto_iterate_valueFunction 0) + have hp₂ : Filter.Tendsto (fun n => D₂.bellman^[n] (0 : S →ᵇ ℝ) s) Filter.atTop + (𝓝 (D₂.valueFunction s)) := + ((BoundedContinuousFunction.evalCLM ℝ s).continuous.tendsto _).comp + (D₂.tendsto_iterate_valueFunction 0) + exact le_of_tendsto_of_tendsto hp₁ hp₂ (Filter.Eventually.of_forall fun n => hiter n s) + +/-- **Nonnegative rewards give a nonnegative value.** -/ +theorem zero_le_bellman (D : ExtendedStochasticProgram S A Z) (hmin : 0 ≤ D.rewardMin) + {v : S →ᵇ ℝ} (hv : 0 ≤ ⇑v) : 0 ≤ ⇑(D.bellman v) := by + intro s + have hexp : 0 ≤ D.expect v (s, D.select s) := by + refine Finset.sum_nonneg fun z _ => ?_ + exact mul_nonneg (D.prob_nonneg z _) (hv _) + have hobj : ((D.rewardMin + D.discount * D.expect v (s, D.select s) : ℝ) : EReal) + ≤ D.objectiveE v s (D.select s) := by + rw [EReal.coe_add] + exact add_le_add (D.le_reward_select s) (le_refl _) + have := le_trans hobj (D.le_bellmanFn v (D.select_mem s)) + rw [EReal.coe_le_coe_iff] at this + have hβ : (0 : ℝ) ≤ (D.discount : ℝ) := D.discount.coe_nonneg + have : 0 ≤ D.bellmanFn v s := by nlinarith [hmin, hexp, hβ] + exact this + +/-- **A dominated operator has a dominated value function.** The comparative-statics workhorse, +at the level the models use: comparing two economies reduces to comparing their Bellman operators +at a common continuation value. -/ +theorem valueFunction_le_of_bellman_le (D₁ D₂ : ExtendedStochasticProgram S A Z) + (hle : ∀ v : S →ᵇ ℝ, ⇑(D₁.bellman v) ≤ ⇑(D₂.bellman v)) (s : S) : + D₁.valueFunction s ≤ D₂.valueFunction s := + Blackwell.valueFunction_le D₁.blackwell D₂.blackwell D₁.discount_lt_one D₂.discount_lt_one + hle s + +/-- **The stochastic Bellman equation**, with the reward an extended real and no floor. -/ +theorem exists_optimal_policy (s : S) : + ∃ a ∈ D.feasible s, ((D.valueFunction s : ℝ) : EReal) + = D.reward (s, a) + + ((D.discount * ∑ z, D.prob z (s, a) * D.valueFunction (D.transition z (s, a)) : + ℝ) : EReal) := by + obtain ⟨a, ha, heq⟩ := D.exists_optimal_action D.valueFunction s + refine ⟨a, ha, ?_⟩ + have hfix : D.bellmanFn D.valueFunction s = D.valueFunction s := by + conv_rhs => rw [← D.bellman_valueFunction] + rfl + rw [← hfix, ← heq] + rfl + +end ValueFunction + +end ExtendedStochasticProgram + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean new file mode 100644 index 000000000..09e2d2fe3 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean @@ -0,0 +1,232 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Bellman + + +/-! +# Stochastic dynamic programming with finitely many shocks + +The Bellman operator + + `T v s = sup { r (s, a) + β * ∑ z, p z (s, a) * v (g z (s, a)) | a ∈ Γ s }` + +where a shock `z` is drawn from a finite set with probabilities `p z (s, a)` and leads to +the state `g z (s, a)`. This is the deterministic operator of +`LeanEconomics.DynamicProgramming.Bellman` with the successor state replaced by an +expectation over successors. + +## Why this is a new structure rather than a reuse + +The deterministic program's continuation is `v` evaluated at one point. Here it is a convex +combination of `v` at several points, which is not of that form, so the operator has to be +rebuilt. Everything above the operator is reused unchanged: the same Blackwell conditions +give the contraction, and the same Berge theorem gives continuity. Only the two facts about +the expectation are new, and they are exactly the two properties of a probability +distribution: + +* `p ≥ 0` gives **monotonicity** -- a larger continuation value cannot lower the average; +* `∑ p = 1` gives **discounting** -- adding a constant to the continuation value adds + exactly that constant to the average, not more. + +Shock probabilities may depend on the state and the action, which covers a Markov chain on +a component of the state as the special case where they depend on that component alone. + +## Main results + +* `LeanEconomics.StochasticDynamicProgram.blackwell` : the operator is a contraction. +* `LeanEconomics.StochasticDynamicProgram.exists_optimal_policy` : the value function + satisfies the stochastic Bellman equation, and the maximum is attained. +-/ + +@[expose] public section + +open scoped NNReal +open Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +variable {S A Z : Type*} [TopologicalSpace S] [TopologicalSpace A] [Fintype Z] + +/-- A **stochastic dynamic program** with finitely many shocks. -/ +structure StochasticDynamicProgram (S A Z : Type*) [TopologicalSpace S] [TopologicalSpace A] + [Fintype Z] where + /-- The actions available at each state. -/ + feasible : S → Set A + isCompact_feasible : ∀ s, IsCompact (feasible s) + feasible_nonempty : ∀ s, (feasible s).Nonempty + upperHemicontinuous_feasible : UpperHemicontinuous feasible + lowerHemicontinuous_feasible : LowerHemicontinuous feasible + /-- The one-period reward. -/ + reward : (S × A) →ᵇ ℝ + /-- The state reached when shock `z` occurs. -/ + transition : Z → C(S × A, S) + /-- The probability that shock `z` occurs. -/ + prob : Z → C(S × A, ℝ) + prob_nonneg : ∀ z p, 0 ≤ prob z p + prob_sum : ∀ p, ∑ z, prob z p = 1 + /-- The factor applied to next-period value. -/ + discount : ℝ≥0 + discount_lt_one : discount < 1 + +namespace StochasticDynamicProgram + +variable (D : StochasticDynamicProgram S A Z) + +/-- The expected continuation value of `v` after choosing `a` at `s`. -/ +noncomputable def expectation (v : S →ᵇ ℝ) (p : S × A) : ℝ := + ∑ z, D.prob z p * v (D.transition z p) + +/-- Current reward plus the discounted expected continuation value. -/ +noncomputable def objective (v : S →ᵇ ℝ) (s : S) (a : A) : ℝ := + D.reward (s, a) + D.discount * D.expectation v (s, a) + +theorem continuous_expectation (v : S →ᵇ ℝ) : Continuous (D.expectation v) := + continuous_finsetSum _ fun z _ => + (D.prob z).continuous.mul (v.continuous.comp (D.transition z).continuous) + +theorem continuous_uncurry_objective (v : S →ᵇ ℝ) : Continuous ↿(D.objective v) := + D.reward.continuous.add (continuous_const.mul (D.continuous_expectation v)) + +/-- An average of values of `v` is no larger than the size of `v`. This is where `p ≥ 0` +and `∑ p = 1` first do their work. -/ +theorem abs_expectation_le (v : S →ᵇ ℝ) (p : S × A) : |D.expectation v p| ≤ ‖v‖ := by + calc |D.expectation v p| ≤ ∑ z, |D.prob z p * v (D.transition z p)| := + Finset.abs_sum_le_sum_abs _ _ + _ = ∑ z, D.prob z p * |v (D.transition z p)| := by + refine Finset.sum_congr rfl fun z _ => ?_ + rw [abs_mul, abs_of_nonneg (D.prob_nonneg z p)] + _ ≤ ∑ z, D.prob z p * ‖v‖ := by + refine Finset.sum_le_sum fun z _ => mul_le_mul_of_nonneg_left ?_ (D.prob_nonneg z p) + simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (D.transition z p) + _ = ‖v‖ := by rw [← Finset.sum_mul, D.prob_sum, one_mul] + +theorem abs_objective_le (v : S →ᵇ ℝ) (s : S) (a : A) : + |D.objective v s a| ≤ ‖D.reward‖ + D.discount * ‖v‖ := by + have h₁ : |D.reward (s, a)| ≤ ‖D.reward‖ := by + simpa [Real.norm_eq_abs] using D.reward.norm_coe_le_norm (s, a) + have h₂ := D.abs_expectation_le v (s, a) + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + rw [abs_le] at h₁ h₂ + simp only [objective, abs_le] + constructor <;> + nlinarith [h₁.1, h₁.2, h₂.1, h₂.2, mul_le_mul_of_nonneg_left h₂.1 hβ, + mul_le_mul_of_nonneg_left h₂.2 hβ] + +/-- The Bellman operator as a bare function. -/ +noncomputable def bellmanFn (v : S →ᵇ ℝ) (s : S) : ℝ := + maxValue (D.objective v) D.feasible s + +theorem le_bellmanFn (v : S →ᵇ ℝ) {s : S} {a : A} (ha : a ∈ D.feasible s) : + D.objective v s a ≤ D.bellmanFn v s := + le_maxValue (D.continuous_uncurry_objective v) (D.isCompact_feasible s) ha + +theorem bellmanFn_le (v : S →ᵇ ℝ) {s : S} {c : ℝ} + (h : ∀ a ∈ D.feasible s, D.objective v s a ≤ c) : D.bellmanFn v s ≤ c := + maxValue_le (D.feasible_nonempty s) h + +theorem abs_bellmanFn_le (v : S →ᵇ ℝ) (s : S) : + |D.bellmanFn v s| ≤ ‖D.reward‖ + D.discount * ‖v‖ := by + obtain ⟨a, ha⟩ := D.feasible_nonempty s + rw [abs_le] + refine ⟨?_, D.bellmanFn_le v fun b _ => (le_abs_self _).trans (D.abs_objective_le v s b)⟩ + exact le_trans (neg_le_of_abs_le (D.abs_objective_le v s a)) (D.le_bellmanFn v ha) + +/-- The **stochastic Bellman operator** on bounded continuous functions. -/ +noncomputable def bellman (v : S →ᵇ ℝ) : S →ᵇ ℝ := + BoundedContinuousFunction.ofNormedAddCommGroup (D.bellmanFn v) + (continuous_maxValue (D.continuous_uncurry_objective v) D.feasible_nonempty + D.isCompact_feasible D.upperHemicontinuous_feasible D.lowerHemicontinuous_feasible) + (‖D.reward‖ + D.discount * ‖v‖) + fun s => by simpa [Real.norm_eq_abs] using D.abs_bellmanFn_le v s + +@[simp] +theorem bellman_apply (v : S →ᵇ ℝ) (s : S) : + D.bellman v s = maxValue (D.objective v) D.feasible s := rfl + +/-- **Blackwell's conditions hold.** Monotonicity is `prob_nonneg`: averaging with +nonnegative weights preserves order. Discounting is `prob_sum`: the weights sum to one, so +raising the continuation value by `c` raises the average by exactly `c`, and the objective +by `β * c`. -/ +theorem blackwell : Blackwell D.discount D.bellman where + monotone := by + intro v w hvw s + have hβ : (0 : ℝ) ≤ D.discount := D.discount.coe_nonneg + have key : ∀ a ∈ D.feasible s, D.objective v s a ≤ D.bellmanFn w s := by + intro a ha + refine le_trans ?_ (D.le_bellmanFn w ha) + have hsum : D.expectation v (s, a) ≤ D.expectation w (s, a) := by + refine Finset.sum_le_sum fun z _ => mul_le_mul_of_nonneg_left ?_ (D.prob_nonneg z _) + simpa using hvw (D.transition z (s, a)) + simp only [objective] + nlinarith + exact D.bellmanFn_le v key + discounting := by + intro v c _ s + have key : ∀ a ∈ D.feasible s, D.objective (v + const S c) s a + ≤ D.bellmanFn v s + D.discount * c := by + intro a ha + have hterm : ∀ z : Z, D.prob z (s, a) * (v + const S c) (D.transition z (s, a)) + = D.prob z (s, a) * v (D.transition z (s, a)) + D.prob z (s, a) * c := fun z => by + simp only [coe_add, const_apply, Pi.add_apply]; ring + have hsum : D.expectation (v + const S c) (s, a) = D.expectation v (s, a) + c := by + simp only [expectation] + rw [Finset.sum_congr rfl fun z _ => hterm z, Finset.sum_add_distrib, + ← Finset.sum_mul, D.prob_sum, one_mul] + have h : D.objective (v + const S c) s a = D.objective v s a + D.discount * c := by + simp only [objective, hsum]; ring + rw [h] + have := D.le_bellmanFn v (s := s) ha + linarith + exact D.bellmanFn_le (v + const S c) key + +/-- The supremum is attained: an optimal action exists at every state. -/ +theorem exists_optimal_action (v : S →ᵇ ℝ) (s : S) : + ∃ a ∈ D.feasible s, D.bellman v s = D.objective v s a ∧ + ∀ b ∈ D.feasible s, D.objective v s b ≤ D.objective v s a := by + obtain ⟨a, ha, hmax⟩ := + argmax_nonempty (D.continuous_uncurry_objective v) (D.feasible_nonempty s) + (D.isCompact_feasible s) + exact ⟨a, ha, + maxValue_eq (D.continuous_uncurry_objective v) (D.isCompact_feasible s) ⟨ha, hmax⟩, + fun b hb => isMaxOn_iff.mp hmax b hb⟩ + +section ValueFunction + +/-- The **value function** of the stochastic program. -/ +noncomputable def valueFunction : S →ᵇ ℝ := D.blackwell.valueFunction D.discount_lt_one + +theorem bellman_valueFunction : D.bellman D.valueFunction = D.valueFunction := + D.blackwell.isFixedPt_valueFunction D.discount_lt_one + +theorem eq_valueFunction {v : S →ᵇ ℝ} (hv : D.bellman v = v) : v = D.valueFunction := + D.blackwell.eq_valueFunction D.discount_lt_one hv + +theorem tendsto_iterate_valueFunction (v : S →ᵇ ℝ) : + Tendsto (fun n => D.bellman^[n] v) atTop (𝓝 D.valueFunction) := + D.blackwell.tendsto_iterate_valueFunction D.discount_lt_one v + +theorem norm_iterate_sub_valueFunction_le (v : S →ᵇ ℝ) (n : ℕ) : + ‖D.bellman^[n] v - D.valueFunction‖ + ≤ (D.discount : ℝ) ^ n / (1 - D.discount) * ‖D.bellman v - v‖ := + D.blackwell.norm_iterate_sub_valueFunction_le D.discount_lt_one v n + +/-- **The stochastic Bellman equation.** At every state there is an optimal action, and the +value is the reward it earns plus the discounted expected value of the successor state. -/ +theorem exists_optimal_policy (s : S) : + ∃ a ∈ D.feasible s, D.valueFunction s + = D.reward (s, a) + + D.discount * ∑ z, D.prob z (s, a) * D.valueFunction (D.transition z (s, a)) := by + obtain ⟨a, ha, heq, -⟩ := D.exists_optimal_action D.valueFunction s + rw [D.bellman_valueFunction] at heq + exact ⟨a, ha, heq⟩ + +end ValueFunction + +end StochasticDynamicProgram + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean new file mode 100644 index 000000000..c862c1f62 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean @@ -0,0 +1,151 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness + + +/-! +# Aiyagari stationary equilibrium + +An interest rate at which the capital households choose to hold equals the capital firms +want to employ. + +Aggregation is the easy half and is done here in full: mean assets under the agent +distribution is the integral of the asset coordinate, which is a BOUNDED CONTINUOUS function +on the compact state space, so aggregate capital is weakly continuous in the distribution. +That is the one place the compactness of the state space pays off directly, and it means no +separate uniform-integrability argument is needed. + +The firm side is left abstract: any continuous capital demand schedule `D` will do, so a +Cobb-Douglas firm with depreciation is one instance among many, and nothing here depends on +which. + +What is assumed rather than proved is that capital supply moves continuously with the +interest rate. That is the remaining gap, and it is a real one -- see the note at the end of +this file. +-/ + +@[expose] public section + +open scoped NNReal ENNReal +open Set Filter Topology BoundedContinuousFunction MeasureTheory + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### Aggregation -/ + +theorem continuous_assetCoord : Continuous fun s : P.State => (s.1 : ℝ) := + continuous_subtype_val.comp continuous_fst + +theorem norm_assetCoord_le (s : P.State) : ‖(s.1 : ℝ)‖ ≤ |assetFloor| + |assetCap| := + abs_le.mpr ⟨by linarith [s.1.2.1, neg_abs_le assetFloor, abs_nonneg assetCap], + by linarith [s.1.2.2, le_abs_self assetCap, abs_nonneg assetFloor]⟩ + +/-- The asset coordinate, as a bounded continuous function. Boundedness is exactly the asset +cap, and continuity is immediate; together they are what make aggregate capital behave well +under weak convergence. -/ +noncomputable def assetCoord : P.State →ᵇ ℝ := + ofNormedAddCommGroup (fun s => (s.1 : ℝ)) P.continuous_assetCoord (|assetFloor| + |assetCap|) + P.norm_assetCoord_le + +@[simp] +theorem assetCoord_apply (s : P.State) : P.assetCoord s = (s.1 : ℝ) := rfl + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- **Aggregate capital**: mean assets under an agent distribution. -/ +noncomputable def aggregateCapital (μ : ProbabilityMeasure P.State) : ℝ := + ∫ s, P.assetCoord s ∂(μ : Measure P.State) + +/-- **Aggregation is weakly continuous.** The asset coordinate is bounded and continuous, so +this is exactly what the weak topology is designed to give. -/ +theorem continuous_aggregateCapital : Continuous P.aggregateCapital := + P.continuous_integral_bcf P.assetCoord + +end IncomeFluctuation + +/-! ### Equilibrium -/ + +section Equilibrium + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] {assetFloor assetCap : ℝ} + +/-- **An Aiyagari stationary equilibrium** at interest rate `r`: the household problem +`Pf r` has a stationary agent distribution whose aggregate capital equals the capital +demanded at that rate. -/ +def IsAiyagariEquilibrium (Pf : ℝ → IncomeFluctuation Z assetFloor assetCap) (D : ℝ → ℝ) + (r : ℝ) : Prop := + ∃ μ : ProbabilityMeasure (Pf r).State, + (Pf r).IsStationary μ ∧ (Pf r).aggregateCapital μ = D r + +/-- **Existence of an Aiyagari stationary equilibrium.** + +Given a capital supply schedule `S` that is realised by a stationary distribution at every +interest rate and is continuous, and any continuous capital demand schedule `D`, a rate at +which the two agree exists as soon as excess demand changes sign across the interval. + +Uniqueness of the stationary distribution is what makes `S` a FUNCTION of `r` rather than a +correspondence, and that is what allows the intermediate value theorem to close the argument +in place of a fixed point theorem for correspondences -- Mathlib has no Kakutani. -/ +theorem exists_aiyagari_equilibrium + {Pf : ℝ → IncomeFluctuation Z assetFloor assetCap} {D S : ℝ → ℝ} {rlo rhi : ℝ} (hle : rlo ≤ rhi) + (hS : ∀ r, ∃ μ : ProbabilityMeasure (Pf r).State, + (Pf r).IsStationary μ ∧ (Pf r).aggregateCapital μ = S r) + (hScont : ContinuousOn S (Icc rlo rhi)) (hDcont : ContinuousOn D (Icc rlo rhi)) + (hlo : S rlo ≤ D rlo) (hhi : D rhi ≤ S rhi) : + ∃ r ∈ Icc rlo rhi, IsAiyagariEquilibrium Pf D r := by + have hcont : ContinuousOn (fun r => S r - D r) (Icc rlo rhi) := hScont.sub hDcont + have hmem : (0 : ℝ) ∈ Icc (S rlo - D rlo) (S rhi - D rhi) := + ⟨by linarith, by linarith⟩ + obtain ⟨r, hr, hr0⟩ := intermediate_value_Icc hle hcont hmem + refine ⟨r, hr, ?_⟩ + obtain ⟨μ, hstat, hagg⟩ := hS r + exact ⟨μ, hstat, by rw [hagg]; linarith [sub_eq_zero.mp hr0]⟩ + +end Equilibrium + +end LeanEconomics + +/-! +## What remains + +`exists_aiyagari_equilibrium` takes continuity of capital supply `S` as a hypothesis. The +pieces already proved discharge part of it and identify exactly what is missing. + +Given a family of household problems indexed by the interest rate: + +1. `continuousAt_fixedPoint` (in `DynamicProgramming/Parametric.lean`) reduces continuity of + `r ↦ valueFunction r` to continuity of `r ↦ bellman r v₀` at the SINGLE function + `v₀ = valueFunction r₀`, in sup norm. The Blackwell contraction modulus is the discount + factor, which does not vary with `r`, so the common-modulus hypothesis is free. +2. From a continuous value function, continuity of the policy in `r` would follow by the same + Berge argument that already gives continuity in the state, since the maximiser is unique. +3. From a continuous policy, `r ↦ push r` is weakly continuous, so the set of stationary + distributions has closed graph; with UNIQUENESS (`stationary_unique_of_exhausts`) a + convergent subnet argument on the compact space of probability measures gives continuity + of `r ↦ μ r`. +4. `continuous_aggregateCapital` then gives continuity of `S`, since the asset coordinate is + bounded and continuous. This step is proved. + +Step 1 is the substantial one: it needs a sup-norm (not pointwise) estimate, uniform over the +state space, and the extended-real reward makes it delicate near zero consumption, where +utility falls to `-∞`. The estimate is true -- the optimum keeps consumption bounded away +from zero because the value is finite -- but proving it uniformly in `r` is real work. + +A second obstacle is structural rather than mathematical: `assetCap` is a FIELD of +`IncomeFluctuation`, so `(Pf r).State` is a different type for each `r`. Any statement +quantifying over the family across rates has to either fix the cap across the family or carry +transports. That is why the theorem above quantifies the distribution inside an existential, +where the dependence causes no trouble. +-/ diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean new file mode 100644 index 000000000..388388de7 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean @@ -0,0 +1,106 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationAugmented +public import LeanPool.AiyagariEquilibrium.Equilibrium.Aiyagari + + +/-! +# Capital supply is continuous, and the equilibrium theorem applies + +`exists_aiyagari_equilibrium` has carried one hypothesis since it was proved: that capital +supply moves continuously with the interest rate. This file discharges it. + +Aggregation turns out not to depend on the rate at all -- the asset coordinate is the asset +coordinate whatever the rate, and `aggregateCapital` is equal across rates by `rfl` -- so all +the rate dependence sits in the stationary distribution, and `tendsto_stationary` supplies it. +Capital supply is then a continuous function composed with a convergent selection. + +The family is indexed by the CLAMPED rate, so that a household problem is defined at every +real number while agreeing with the intended one on `[rlo, rhi]`. Without that the family +would need a proof of `0 < 1 + r` at every real `r`, which is false. +-/ + +@[expose] public section + +open Set Filter Topology MeasureTheory + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +-- Continuity in the rate is read off the augmented state space, which is built at a zero +-- borrowing limit; see `IncomeFluctuationAugmented`. +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) {rlo rhi : ℝ} + +/-- The household problem at the clamped rate, defined at every real number. -/ +noncomputable def rateFamily (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (r : ℝ) : + IncomeFluctuation Z (0 : ℝ) assetCap := + P.withRate (clampRate rlo rhi r) (rateOK_of_floor_zero (one_add_clampRate_pos hrlo hle r)) + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem rateFamily_eq (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) {r : ℝ} (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) : P.rateFamily hrlo hle r = P.withRate r hrr := by + unfold rateFamily + congr 1 + exact clampRate_eq hr + +/-- **Capital supply is continuous in the interest rate.** -/ +theorem continuousOn_capitalSupply (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, + (P.withRate r hrr).IsStationary μ → μ = ν r) : + ContinuousOn (fun r => P.aggregateCapital (ν r)) (Icc rlo rhi) := by + intro r₀ hr₀ + have hrr₀ : P.RateOK r₀ := rateOK_of_floor_zero (by have := hr₀.1; linarith) + exact (P.continuous_aggregateCapital.tendsto (ν r₀)).comp + (P.tendsto_stationary hrlo hle hr₀ hrr₀ ν hν (huniq r₀ hr₀ hrr₀)) + +/-- **An Aiyagari stationary equilibrium exists**, with nothing assumed about the household +side beyond a selection of stationary distributions that is unique at each rate. + +Compared with `exists_aiyagari_equilibrium`, the continuity of capital supply is no longer a +hypothesis: it is proved, from continuity of the policy in the rate and weak continuity of +aggregation. What remains assumed is the firm side -- any continuous demand schedule -- and +that excess demand changes sign, which is where the economics of the calibration enters. -/ +theorem exists_equilibrium_of_selection (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, + (P.withRate r hrr).IsStationary μ → μ = ν r) + (D : ℝ → ℝ) (hD : ContinuousOn D (Icc rlo rhi)) + (hlo : P.aggregateCapital (ν rlo) ≤ D rlo) + (hhi : D rhi ≤ P.aggregateCapital (ν rhi)) : + ∃ r ∈ Icc rlo rhi, + IsAiyagariEquilibrium (P.rateFamily hrlo hle) D r := by + classical + set S : ℝ → ℝ := fun r => P.aggregateCapital (ν (clampRate rlo rhi r)) with hS + have hlo' : rlo ∈ Icc rlo rhi := ⟨le_rfl, hle⟩ + have hhi' : rhi ∈ Icc rlo rhi := ⟨hle, le_rfl⟩ + -- on the interval the clamp is inactive, so `S` is the supply schedule + have hSeq : ∀ r ∈ Icc rlo rhi, S r = P.aggregateCapital (ν r) := fun r hr => by + rw [hS]; simp only [clampRate_eq hr] + refine exists_aiyagari_equilibrium (Pf := P.rateFamily hrlo hle) (S := S) hle ?_ ?_ hD + (by rw [hSeq rlo hlo']; exact hlo) (by rw [hSeq rhi hhi']; exact hhi) + · -- a stationary distribution realising `S` at every rate + intro r + refine ⟨ν (clampRate rlo rhi r), ?_, rfl⟩ + have hmem := clampRate_mem hle r + have hrr : P.RateOK (clampRate rlo rhi r) := + rateOK_of_floor_zero (one_add_clampRate_pos hrlo hle r) + have := hν _ hmem hrr + rwa [show P.rateFamily hrlo hle r = P.withRate (clampRate rlo rhi r) hrr from rfl] + · -- and `S` is continuous there + refine (P.continuousOn_capitalSupply hrlo hle ν hν huniq).congr ?_ + exact fun r hr => hSeq r hr + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean new file mode 100644 index 000000000..a1852de14 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -0,0 +1,511 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.LogAiyagari +public import LeanPool.AiyagariEquilibrium.Equilibrium.CapitalSupplyMonotone +public import LeanPool.AiyagariEquilibrium.Equilibrium.Uniqueness +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity +public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange + + +/-! +# Uniqueness of the equilibrium rate for a log economy at large `β` + +The chain of Light (2018) — individual saving rises with the rate (Theorem 1), so aggregate +saving does (Theorem 2), so it meets a strictly decreasing demand once (Theorem 3) — assembled +for LOG utility with the constants that survive `β = 0.96`. + +## The normalised firm + +Aiyagari's firm is Cobb--Douglas with capital share `α`: `k(r) = (α/(r+δ))^(1/(1-α))` and +`w(r) = (1-α) k(r)^α`. Light's Theorem 3 works with the ratio, and the ratio needs no root: + + `k(r) / w(r) = α / ((1-α)(r+δ))`, + +strictly decreasing in `r`. Capital supply is homogeneous of degree one in the wage (Açıkgöz +Proposition 7, `Equilibrium/Homothetic`), so Aiyagari's steady-state condition `K(r) = Ea(r)` at +wage `w(r)` is the condition `k(r)/w(r) = Ea₁(r)` for the unit-wage economy. This file proves +that the latter has at most one solution. + +## What is assumed of the income process + +That its transitions are MONOTONE — a higher-income state's row first-order stochastically +dominates a lower one's (`MonotoneTransitions`; iid is the trivial case) — and that the worst +state has income `minIncome` and is reachable from everywhere. Nothing else: no transition +probabilities are named. That is why the theorem can be stated for Aiyagari's calibration +without reproducing his seven-state Tauchen chains: a Tauchen discretisation of an AR(1) with +positive persistence has monotone transitions by construction, so at `μ = 1` every column of his +Table II is covered, `ρ = 0` and `ρ > 0` alike. + +## What is assumed of the cap + +One inequality, `2β · maxIncome < (1 - β(1+rhi)) · cap`. The asset cap is bookkeeping imposed for +compactness; this says it was set large enough not to bind, and it is the only place the cap +enters. +-/ + +@[expose] public section + +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +/-! ### The normalised Cobb--Douglas demand -/ + +/-- `k(r)/w(r)` for a Cobb--Douglas firm with capital share `α` and depreciation `δ`. -/ +noncomputable def normalisedDemand (α δ r : ℝ) : ℝ := α / ((1 - α) * (r + δ)) + +theorem normalisedDemand_strictAntiOn {α δ rlo rhi : ℝ} (hα0 : 0 < α) (hα1 : α < 1) + (hδ : 0 < rlo + δ) : StrictAntiOn (normalisedDemand α δ) (Icc rlo rhi) := by + intro x hx y hy hxy + simp only [normalisedDemand] + have hx0 : 0 < (1 - α) * (x + δ) := mul_pos (by linarith) (by linarith [hx.1]) + have hlt : (1 - α) * (x + δ) < (1 - α) * (y + δ) := + mul_lt_mul_of_pos_left (by linarith) (by linarith) + exact div_lt_div_of_pos_left hα0 hx0 hlt + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-! ### Theorem 1 on the interval, by chaining -/ + +/-- **Saving rises with the rate on `[0, rhi]`**, for log utility. Local comparisons from +`crra_policy_mono_withRate_of_minMPC` chained by `monotoneOn_of_local`. For log the minimal MPC is +`1 - β` at every rate, so every cap condition collapses to the one on `hcap`. -/ +theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hε : 0 < ε) + (hcap : (P.discount : ℝ) * P.maxIncome + + ((P.discount : ℝ) * (1 + rhi) + ε) * assetCap < assetCap) : + ∀ r ∈ Icc rlo rhi, ∀ r' ∈ Icc rlo rhi, r ≤ r' → + ∀ s : ℝ × Z, s.1 ∈ Icc (0 : ℝ) assetCap → + (P.rateFamily hrlo hlohi r).policy s + ≤ (P.rateFamily hrlo hlohi r').policy s := by + intro r hr r' hr' hle s hs + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hmono : MonotoneOn (fun x => (P.rateFamily hrlo hlohi x).policy s) + (Icc rlo rhi) := by + refine monotoneOn_of_local hε ?_ + intro x hx y hy hxy hgapxy + have hxok : P.RateOK x := rateOK_of_floor_zero (by linarith [hx.1]) + have hyok : P.RateOK y := rateOK_of_floor_zero (by linarith [hy.1]) + change (P.rateFamily hrlo hlohi x).policy s + ≤ (P.rateFamily hrlo hlohi y).policy s + rw [P.rateFamily_eq _ _ hx hxok, P.rateFamily_eq _ _ hy hyok] + obtain ⟨a, z⟩ := s + have hκx : 1 - (P.withRate x hxok).minMPC 1 = (P.discount : ℝ) := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring + have hκy : 1 - (P.withRate y hyok).minMPC 1 = (P.discount : ℝ) := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring + have hκxpos : 0 < (P.withRate x hxok).minMPC 1 := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; linarith + have hκypos : 0 < (P.withRate y hyok).minMPC 1 := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; linarith + have hx_le : (P.discount : ℝ) * (1 + x) ≤ (P.discount : ℝ) * (1 + rhi) := + mul_le_mul_of_nonneg_left (by linarith [hx.2]) hβ.le + have hy_le : (P.discount : ℝ) * (1 + y) ≤ (P.discount : ℝ) * (1 + rhi) := + mul_le_mul_of_nonneg_left (by linarith [hy.2]) hβ.le + have hεcap : (0 : ℝ) ≤ ε * assetCap := mul_nonneg hε.le hcap0 + refine P.crra_policy_mono_withRate_of_minMPC (γ := 1) one_pos le_rfl hu hxok hyok hxy hκxpos + hκypos hβ + (by linarith) ((P.withRate x hxok).positiveConsumptionAll_of_unbounded hunb) + ((P.withRate y hyok).positiveConsumptionAll_of_unbounded hunb) ?_ ?_ ?_ hs z + · rw [hκx] + have := mul_le_mul_of_nonneg_left hx_le hcap0 + nlinarith + · rw [hκy] + have := mul_le_mul_of_nonneg_left hy_le hcap0 + nlinarith + · rw [hκx] + have h1 : ((P.discount : ℝ) * (1 + x) + (y - x)) * assetCap + ≤ ((P.discount : ℝ) * (1 + rhi) + ε) * assetCap := + mul_le_mul_of_nonneg_right (by linarith) hcap0 + linarith + exact hmono hr hr' hle + +/-! ### The Doeblin chain on the interval -/ + +section Measure + +variable [MeasurableSpace Z] [BorelSpace Z] + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- The per-rate corner test, from its worst case at the top of the interval. For log the test +at rate `r` is `β(1+r)(income z₀ + (1+r) a₀) < minIncome`, increasing in `r`. -/ +theorem log_corner_of_top {rlo rhi a₀ : ℝ} {z₀ : Z} (hβ : 0 < (P.discount : ℝ)) (ha₀ : 0 < a₀) + (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) + {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) : + ((P.withRate r hrr).discount : ℝ) * (1 + (P.withRate r hrr).interest) + * (P.withRate r hrr).minIncome ^ (-(1 : ℝ)) + < ((P.withRate r hrr).income z₀ + (1 + (P.withRate r hrr).interest) * a₀) ^ (-(1 : ℝ)) := by + have hy : 0 < P.income z₀ := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z₀) + have hmin : 0 < P.minIncome := P.minIncome_pos + have hq : 0 < P.income z₀ + (1 + r) * a₀ := by nlinarith [hrr.1] + have hstep : (P.discount : ℝ) * (1 + r) * (P.income z₀ + (1 + r) * a₀) + ≤ (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) := by + have h1 : (P.discount : ℝ) * (1 + r) ≤ (P.discount : ℝ) * (1 + rhi) := + mul_le_mul_of_nonneg_left (by linarith [hr.2]) hβ.le + have h2 : P.income z₀ + (1 + r) * a₀ ≤ P.income z₀ + (1 + rhi) * a₀ := by + nlinarith [hr.2] + exact mul_le_mul h1 h2 hq.le (mul_nonneg hβ.le (by linarith [hrr.1, hr.2])) + simp only [IncomeFluctuation.withRate_discount, IncomeFluctuation.withRate_interest, + IncomeFluctuation.withRate_minIncome, IncomeFluctuation.withRate_income, + Real.rpow_neg_one] + rw [mul_inv_lt_iff₀ hmin, inv_mul_eq_div, lt_div_iff₀ hq] + linarith + +/-- **A unique stationary distribution at every rate in `[0, rhi]`**, for log utility. -/ +theorem log_existsUnique_isStationary {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) + (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) + {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) : + ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ := by + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring + have hκpos : 0 < (P.withRate r hrr).minMPC 1 := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; linarith + refine (P.withRate r hrr).crra_existsUnique_isStationary_of_euler_corner_monotone (γ := 1) + one_pos hu rfl hκpos hβ (by simpa using + (show (P.discount : ℝ) * (1 + r) < 1 by nlinarith [hr.2, hβ])) + (P.monotoneTransitions_withRate hmono hrr) + ((P.withRate r hrr).positiveConsumptionAll_of_unbounded hunb) ?_ hz₀ hreach ha₀ hle + (P.log_corner_of_top hβ ha₀ hcorn hr hrr) + rw [hκ] + simp only [IncomeFluctuation.withRate_maxIncome, IncomeFluctuation.withRate_interest] + have := mul_le_mul_of_nonneg_left (show (1 + r) * assetCap ≤ (1 + rhi) * assetCap from + mul_le_mul_of_nonneg_right (by linarith [hr.2]) hcap0) hβ.le + nlinarith + +/-- **Convergence to it from any start**, at every rate in `[0, rhi]`. -/ +theorem log_tendsto_pushProb {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) + {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) + (μ₀ : ProbabilityMeasure P.State) {μ : ProbabilityMeasure P.State} + (hμ : (P.withRate r hrr).IsStationary μ) : + Tendsto (fun m => (P.withRate r hrr).pushProb^[m] μ₀) atTop (𝓝 μ) := by + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring + have hκpos : 0 < (P.withRate r hrr).minMPC 1 := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; linarith + refine (P.withRate r hrr).crra_tendsto_pushProb_of_euler_corner_monotone (γ := 1) + one_pos hu rfl hκpos hβ (by simpa using + (show (P.discount : ℝ) * (1 + r) < 1 by nlinarith [hr.2, hβ])) + (P.monotoneTransitions_withRate hmono hrr) + ((P.withRate r hrr).positiveConsumptionAll_of_unbounded hunb) ?_ hz₀ hreach ha₀ hle + (P.log_corner_of_top hβ ha₀ hcorn hr hrr) μ₀ hμ + rw [hκ] + simp only [IncomeFluctuation.withRate_maxIncome, IncomeFluctuation.withRate_interest] + have := mul_le_mul_of_nonneg_left (show (1 + r) * assetCap ≤ (1 + rhi) * assetCap from + mul_le_mul_of_nonneg_right (by linarith [hr.2]) hcap0) hβ.le + nlinarith + +/-! ### Theorem 2: capital supply is monotone -/ + +/-- **Capital supply rises with the rate on `[0, rhi]`**, for log utility, along any selection of +stationary distributions. Light's Theorems 1 and 2. -/ +theorem log_monotoneOn_capitalSupply {rlo rhi ε a₀ : ℝ} (hu : P.u = crraUtility 1) + (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hε : 0 < ε) + (hcap : (P.discount : ℝ) * P.maxIncome + + ((P.discount : ℝ) * (1 + rhi) + ε) * assetCap < assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) : + MonotoneOn (fun r => P.aggregateCapital (ν r)) (Icc rlo rhi) := by + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hcap' : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap := by + have := mul_nonneg hε.le hcap0 + nlinarith + refine P.monotoneOn_capitalSupply_of_policy_mono (P.rateFamily hrlo hlohi) + (fun r r' z z' => rfl) + (P.log_policy_mono_rateFamily hu hunb hβ hβ1 hrlo hlohi hβR hε hcap) + ⟨Measure.dirac (Classical.ofNonempty : P.State), inferInstance⟩ ν ?_ + intro r hr + have hrr : P.RateOK r := rateOK_of_floor_zero (by linarith [hr.1]) + rw [P.rateFamily_eq _ _ hr hrr] + exact P.log_tendsto_pushProb hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle hcorn hr hrr _ + (hν r hr hrr) + +/-! ### Theorem 3: single crossing -/ + +/-- **The equilibrium rate is unique on `[0, rhi]`**, for a log economy against the normalised +Cobb--Douglas firm. Two equilibria — a rate and a stationary distribution whose aggregate +capital is `k(r)/w(r)` — have the same rate. -/ +theorem log_equilibriumRate_unique {rlo rhi ε a₀ α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) + (hδ : 0 < rlo + δ) + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hε : 0 < ε) + (hcap : (P.discount : ℝ) * P.maxIncome + + ((P.discount : ℝ) * (1 + rhi) + ε) * assetCap < assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hreach : ∀ z, 0 < P.transitionMatrix z z₀) (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure P.State} + (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : P.aggregateCapital μ₁ = normalisedDemand α δ r₁) + (he₂ : P.aggregateCapital μ₂ = normalisedDemand α δ r₂) : r₁ = r₂ := by + classical + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hcap' : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap := by + have := mul_nonneg hε.le hcap0 + nlinarith + -- the selection of stationary distributions, defined through the clamped rate + have hE : ∀ r : ℝ, ∃! μ : ProbabilityMeasure P.State, + (P.withRate (clampRate rlo rhi r) + (rateOK_of_floor_zero (one_add_clampRate_pos hrlo hlohi r))).IsStationary μ := + fun r => P.log_existsUnique_isStationary hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle + hcorn (clampRate_mem hlohi r) _ + set ν : ℝ → ProbabilityMeasure P.State := fun r => (hE r).choose with hνdef + have hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r) := by + intro r hr hrr + have hc : clampRate rlo rhi r = r := clampRate_eq hr + rw [← P.withRate_congr hc _ hrr] + exact (hE r).choose_spec.1 + have hid : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, + (P.withRate r hrr).IsStationary μ → μ = ν r := by + intro r hr hrr μ hμ + have hc : clampRate rlo rhi r = r := clampRate_eq hr + refine (hE r).choose_spec.2 μ ?_ + rw [P.withRate_congr hc _ hrr] + exact hμ + rw [hid r₁ h₁ hrr₁ μ₁ hμ₁] at he₁ + rw [hid r₂ h₂ hrr₂ μ₂ hμ₂] at he₂ + exact eq_of_monotoneOn_of_strictAntiOn + (P.log_monotoneOn_capitalSupply hu hunb hβ hβ1 hrlo hlohi hβR hε hcap hmono hz₀ hreach ha₀ hle + hcorn ν hν) + (normalisedDemand_strictAntiOn hα0 hα1 hδ) h₁ h₂ he₁ he₂ + +/-! ### With the constants constructed + +The interval, the step and the corner level are not primitives of the economy. Given only that +the cap clears `2β · maxIncome / (1 - β(1+rhi))`, choose the step as half the room under `1` and +the corner level as small as the corner test needs. -/ + +/-- **Uniqueness from one inequality on the cap.** -/ +theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) + (hδ : 0 < rlo + δ) + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure P.State} + (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : P.aggregateCapital μ₁ = normalisedDemand α δ r₁) + (he₂ : P.aggregateCapital μ₂ = normalisedDemand α δ r₂) : r₁ = r₂ := by + set β : ℝ := (P.discount : ℝ) with hβdef + set R : ℝ := 1 + rhi with hRdef + have hR : 0 < R := by rw [hRdef]; linarith [hrlo, hlohi] + have hroom : 0 < 1 - β * R := by rw [hβdef, hRdef]; linarith + have hmaxpos : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome + have hcap0 : 0 < assetCap := by + by_contra h + have h' : assetCap ≤ 0 := not_lt.mp h + have : (1 - β * R) * assetCap ≤ 0 := mul_nonpos_of_nonneg_of_nonpos hroom.le h' + nlinarith [hcap, hβ, hmaxpos] + have hminpos : 0 < P.minIncome := P.minIncome_pos + -- the step: half the room under `1` + set ε : ℝ := (1 - β * R) / 2 with hεdef + have hε : 0 < ε := by rw [hεdef]; linarith + -- the corner level: small enough for the corner test, and below the cap + set A : ℝ := P.minIncome * (1 - β * R) / (2 * β * R ^ 2) with hAdef + have hA : 0 < A := by rw [hAdef]; positivity + set a₀ : ℝ := min assetCap A with ha₀def + have ha₀ : 0 < a₀ := lt_min hcap0 hA + have hle : a₀ ≤ assetCap := min_le_left _ _ + have ha₀A : a₀ ≤ A := min_le_right _ _ + -- the cap condition with the chosen step + have hcapε : β * P.maxIncome + (β * R + ε) * assetCap < assetCap := by + rw [hεdef] + nlinarith [hcap] + -- the corner test at the top of the interval + have hcorn : β * R * (P.income z₀ + R * a₀) < P.minIncome := by + rw [hmin] + have hkey : β * R * (R * A) = P.minIncome * (1 - β * R) / 2 := by + rw [hAdef]; field_simp; try ring + have hmono : β * R * (R * a₀) ≤ β * R * (R * A) := + mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left ha₀A hR.le) (by positivity) + have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring + rw [this] + nlinarith [hkey, hmono, hroom, hminpos] + exact P.log_equilibriumRate_unique hα0 hα1 hδ hu hunb hβ hβ1 hrlo hlohi hβR hε hcapε hmono hz₀ + hreach ha₀ hle hcorn h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ + +/-! ### Negative rates + +The uniqueness theorems live on `[0, rhi]`. Below zero the minimal-MPC ceiling on capital +supply, `β · maxIncome / (1 - β(1+r))`, still holds for log utility — `κ = 1 - β` at every +rate — while Cobb--Douglas demand blows up as `r ↓ -δ`. Where the ceiling is below demand there +is no equilibrium. -/ + +/-- **The log capital ceiling at any admissible rate** with `β(1+r) < 1`: no sign restriction on +`r`, because `κ = 1 - β` (`minMPC_one`). -/ +theorem log_aggregateCapital_le {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrr : P.RateOK r) + (hβR : (P.discount : ℝ) * (1 + r) < 1) + (hthr : (P.discount : ℝ) * (P.maxIncome + (1 + r) * assetCap) < assetCap) + {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : + P.aggregateCapital μ + ≤ (P.discount : ℝ) * P.maxIncome / (1 - (P.discount : ℝ) * (1 + r)) := by + have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring + have hκpos : 0 < (P.withRate r hrr).minMPC 1 := by + rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; linarith + have hβR' : ((P.withRate r hrr).discount : ℝ) * (1 + (P.withRate r hrr).interest) < 1 := hβR + have hthr' : (1 - (P.withRate r hrr).minMPC 1) + * ((P.withRate r hrr).maxIncome + (1 + (P.withRate r hrr).interest) * assetCap) + < assetCap := by + rw [hκ]; exact hthr + have hpc : (P.withRate r hrr).PositiveConsumptionAll := + (P.withRate r hrr).positiveConsumptionAll_of_unbounded hunb + have hposIt : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < (P.withRate r hrr).consumptionFnOf + (((P.withRate r hrr).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc _ ((P.withRate r hrr).concaveSlices_iterate_zero n) z a ha + have hslackIt : ∀ n : ℕ, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r hrr).policyOf + (((P.withRate r hrr).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap := + fun n x hx z => ((P.withRate r hrr).crra_minMPC_of_cap one_pos hu rfl hκpos hβ hβR' + hposIt hthr' n).2 x hx z + have hkey := (P.withRate r hrr).crra_aggregateCapital_le_of_minMPC one_pos hu hκpos hβ hβR' + hposIt hslackIt hμ + rw [hκ] at hkey + exact hkey + +/-- **No equilibrium where the ceiling is below demand.** -/ +theorem log_no_equilibrium_of_ceiling {α δ r : ℝ} (hu : P.u = crraUtility 1) + (hunb : P.Unbounded) (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hrr : P.RateOK r) (hβR : (P.discount : ℝ) * (1 + r) < 1) + (hthr : (P.discount : ℝ) * (P.maxIncome + (1 + r) * assetCap) < assetCap) + (hD : (P.discount : ℝ) * P.maxIncome / (1 - (P.discount : ℝ) * (1 + r)) + < normalisedDemand α δ r) + {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : + P.aggregateCapital μ ≠ normalisedDemand α δ r := fun h => by + have := P.log_aggregateCapital_le hu hunb hβ hβ1 hrr hβR hthr hμ + linarith + +/-! ### Aiyagari (1994), `μ = 1` + +`β = 0.96`, `α = 0.36`, `δ = 0.08`, log utility, no borrowing, and a labour endowment process +with monotone transitions about which nothing else is assumed. The time-preference rate is +`λ = 1/0.96 - 1 = 1/24`, and the theorem covers every interval `[rlo, rhi]` with +`-δ < rlo ≤ rhi < 1/24` — so the whole of `(-δ, λ)`, the range on which Cobb--Douglas demand is +finite and the household impatient; his reported equilibrium rates at `μ = 1` lie between `3.3%` +and `4.17%`. -/ + +/-- **Aiyagari (1994) at `μ = 1`: the equilibrium interest rate is unique** on `[rlo, rhi]` for +every `-δ < rlo ≤ rhi` below the time-preference rate `1/24`, for any economy with his +preferences and technology and any earnings process with monotone transitions, provided the +asset cap clears `(48/25) · maxIncome / (1 - (24/25)(1+rhi))`. -/ +theorem aiyagari1994_log_equilibriumRate_unique {rlo rhi : ℝ} + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) + (hrlo : -2 / 25 < rlo) (hlohi : rlo ≤ rhi) (hlam : rhi < 1 / 24) + (hcap : 2 * (24 / 25) * P.maxIncome < (1 - 24 / 25 * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure P.State} + (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : P.aggregateCapital μ₁ = normalisedDemand (9 / 25) (2 / 25) r₁) + (he₂ : P.aggregateCapital μ₂ = normalisedDemand (9 / 25) (2 / 25) r₂) : r₁ = r₂ := + P.log_equilibriumRate_unique_of_cap (by norm_num) (by norm_num) (by linarith) hu hunb + (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) + (by rw [hβ]; exact hcap) hmono hz₀ hmin hreach h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ + +/-- **Aiyagari (1994) at `μ = 1`: two equilibrium rates in `(-δ, λ)` coincide.** The interval +version applied to `[min r₁ r₂, max r₁ r₂]`; the cap condition is the one at the larger rate. +Together with `aiyagari1994_log_no_equilibrium_of_ceiling` below `-δ + ε` and +`log_no_equilibrium_of_patient_risk` above `λ + ε`, this is uniqueness of the equilibrium rate +for his log calibration, up to two thin bands the cap forces. -/ +theorem aiyagari1994_log_equilibriumRate_unique_Ioo + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) (h₂ : r₂ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) + (hcap : 2 * (24 / 25) * P.maxIncome < (1 - 24 / 25 * (1 + max r₁ r₂)) * assetCap) + (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure P.State} + (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : P.aggregateCapital μ₁ = normalisedDemand (9 / 25) (2 / 25) r₁) + (he₂ : P.aggregateCapital μ₂ = normalisedDemand (9 / 25) (2 / 25) r₂) : r₁ = r₂ := + P.aiyagari1994_log_equilibriumRate_unique hu hunb hβ (rlo := min r₁ r₂) (rhi := max r₁ r₂) + (lt_min h₁.1 h₂.1) min_le_max (max_lt h₁.2 h₂.2) hcap hmono hz₀ hmin hreach + ⟨min_le_left _ _, le_max_left _ _⟩ ⟨min_le_right _ _, le_max_right _ _⟩ + hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ + +/-- **Aiyagari (1994) at `μ = 1`: no equilibrium at a negative rate where the ceiling is below +demand.** The condition is the ceiling–demand inequality written out with his numbers: it is +polynomial in `r` and `maxIncome`, so it can be checked for any earnings grid. -/ +theorem aiyagari1994_log_no_equilibrium_of_ceiling {r : ℝ} + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) + (hcap : 24 * P.maxIncome < assetCap) (hlo : -2 / 25 < r) (hr0 : r ≤ 0) (hrr : P.RateOK r) + (hD : 24 / 25 * P.maxIncome * (r + 2 / 25) < 9 / 16 * (1 - 24 / 25 * (1 + r))) + {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : + P.aggregateCapital μ ≠ normalisedDemand (9 / 25) (2 / 25) r := by + have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hmax : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome + refine P.log_no_equilibrium_of_ceiling hu hunb (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) + hrr (by rw [hβ]; linarith) ?_ ?_ hμ + · rw [hβ] + have := mul_nonneg (neg_nonneg.mpr hr0) hcap0 + nlinarith + · rw [hβ] + simp only [normalisedDemand] + have h1 : (0 : ℝ) < 1 - 24 / 25 * (1 + r) := by linarith + have h2 : (0 : ℝ) < (1 - 9 / 25) * (r + 2 / 25) := by nlinarith + rw [div_lt_div_iff₀ h1 h2] + nlinarith [hD] + +/-- **Aiyagari (1994) at `μ = 1`: no equilibrium on `(-δ, -1/20]`** whenever the highest +endowment is at most `3/2` (in wage units, as his mean-one normalisation has it). The bound is +not sharp — the ceiling–demand crossing sits near `-4.7%` — and the band `(-4.7%, 0)` stays +open: there the minimal-MPC ceiling `β · maxIncome/(1 - β(1+r))` exceeds Cobb--Douglas demand, +and excluding it needs a bound on capital supply that the household side does not yet give. -/ +theorem aiyagari1994_log_no_equilibrium_below {r : ℝ} + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) + (hcap : 24 * P.maxIncome < assetCap) (hmax : P.maxIncome ≤ 3 / 2) + (hr : r ∈ Ioc (-2 / 25 : ℝ) (-1 / 20)) (hrr : P.RateOK r) + {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : + P.aggregateCapital μ ≠ normalisedDemand (9 / 25) (2 / 25) r := by + refine P.aiyagari1994_log_no_equilibrium_of_ceiling hu hunb hβ hcap hr.1 (by linarith [hr.2]) + hrr ?_ hμ + have hx : (0 : ℝ) ≤ r + 2 / 25 := by linarith [hr.1] + have := mul_le_mul_of_nonneg_right hmax hx + nlinarith [hr.2] + +end Measure + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean new file mode 100644 index 000000000..1f5f6b682 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -0,0 +1,277 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness + + +/-! +# Bisection finds the equilibrium rate, and the log Aiyagari instance + +Aiyagari (1994) computed his equilibrium by bisection on the interest rate: evaluate excess +supply at the midpoint of a bracket and keep the half on which it still changes sign. Hajek and +Livesay (2019) ask, for mean field games, which equilibria are STABLE fixed points of the +best-response map; for the Bewley model the raw best-response iteration `r ↦ r_d(K_s(r))` has +negative slope and may oscillate, so the algorithm that is certified here is the bisection. + +## The abstract statement + +`bisect f a b n` is the bracket after `n` steps. If `f` is STRICTLY MONOTONE on `[a, b]` and +vanishes at `r ∈ [a, b]`, then every bracket contains `r` (`bisect_mem_of_strictMonoOn`), the +width halves each step (`bisect_width`), and the endpoints converge to `r` +(`tendsto_bisect_fst`). No continuity and no sign condition at the ends are used: strict +monotonicity alone decides which half the zero is in. The midpoint after `n` steps is within +`(b - a)/2^(n+1)` of `r` (`abs_bisect_mid_sub_le`). + +## The instance + +For Aiyagari's log calibration the supply schedule along any selection of stationary +distributions is monotone (`log_monotoneOn_capitalSupply`) and Cobb--Douglas demand is strictly +decreasing, so excess supply is strictly increasing. `aiyagari1994_log_bisection` therefore says: +whatever equilibrium rate there is on `[rlo, rhi] ⊂ (-δ, λ)`, the bisection on excess supply +brackets it at every step and converges to it — the numerical procedure is certified to find the +rate that `aiyagari1994_log_equilibriumRate_unique` says is the only one. +-/ + +@[expose] public section + +open Set Filter Topology Function MeasureTheory + +namespace LeanEconomics + +/-! ### Bisection on the line -/ + +/-- One bisection step on a bracket `(a, b)`: if `f ≤ 0` at the midpoint the zero of an +increasing `f` is to the right, so keep `(mid, b)`; otherwise keep `(a, mid)`. -/ +noncomputable def bisectStep (f : ℝ → ℝ) (p : ℝ × ℝ) : ℝ × ℝ := + if f ((p.1 + p.2) / 2) ≤ 0 then ((p.1 + p.2) / 2, p.2) else (p.1, (p.1 + p.2) / 2) + +/-- The bracket after `n` bisection steps from `(a, b)`. -/ +noncomputable def bisect (f : ℝ → ℝ) (a b : ℝ) (n : ℕ) : ℝ × ℝ := (bisectStep f)^[n] (a, b) + +theorem bisect_succ (f : ℝ → ℝ) (a b : ℝ) (n : ℕ) : + bisect f a b (n + 1) = bisectStep f (bisect f a b n) := + iterate_succ_apply' _ _ _ + +theorem bisectStep_width (f : ℝ → ℝ) (p : ℝ × ℝ) : + (bisectStep f p).2 - (bisectStep f p).1 = (p.2 - p.1) / 2 := by + unfold bisectStep + split_ifs <;> simp only <;> ring + +/-- **The bracket halves at every step.** -/ +theorem bisect_width (f : ℝ → ℝ) (a b : ℝ) (n : ℕ) : + (bisect f a b n).2 - (bisect f a b n).1 = (b - a) / 2 ^ n := by + induction n with + | zero => simp [bisect] + | succ k ih => rw [bisect_succ, bisectStep_width, ih, pow_succ, div_div] + +/-- **Every bracket contains the zero of a strictly increasing function**, and stays inside the +initial bracket. -/ +theorem bisect_mem_of_strictMonoOn {f : ℝ → ℝ} {a b r : ℝ} (hf : StrictMonoOn f (Icc a b)) + (hr : r ∈ Icc a b) (hfr : f r = 0) (n : ℕ) : + a ≤ (bisect f a b n).1 ∧ (bisect f a b n).2 ≤ b ∧ + r ∈ Icc (bisect f a b n).1 (bisect f a b n).2 := by + induction n with + | zero => exact ⟨le_rfl, le_rfl, hr⟩ + | succ k ih => + obtain ⟨ha, hb, hr1, hr2⟩ := ih + rw [bisect_succ] + set p := bisect f a b k with hp + have hm1 : p.1 ≤ (p.1 + p.2) / 2 := by linarith + have hm2 : (p.1 + p.2) / 2 ≤ p.2 := by linarith + have hmem : (p.1 + p.2) / 2 ∈ Icc a b := ⟨by linarith, by linarith⟩ + unfold bisectStep + split_ifs with h + · -- `f ≤ 0` at the midpoint: the zero is at or to the right of it + refine ⟨by linarith, hb, ?_, hr2⟩ + by_contra hlt + push Not at hlt + have := hf hr hmem hlt + linarith + · -- `0 < f` at the midpoint: the zero is at or to the left of it + push Not at h + refine ⟨ha, by linarith, hr1, ?_⟩ + by_contra hlt + push Not at hlt + have := hf hmem hr hlt + linarith + +/-- **The midpoint after `n` steps is within `(b - a)/2^(n+1)` of the zero.** -/ +theorem abs_bisect_mid_sub_le {f : ℝ → ℝ} {a b r : ℝ} (hf : StrictMonoOn f (Icc a b)) + (hr : r ∈ Icc a b) (hfr : f r = 0) (n : ℕ) : + |((bisect f a b n).1 + (bisect f a b n).2) / 2 - r| ≤ (b - a) / 2 ^ (n + 1) := by + obtain ⟨-, -, h1, h2⟩ := bisect_mem_of_strictMonoOn hf hr hfr n + have hw := bisect_width f a b n + rw [abs_le, pow_succ, ← div_div] + constructor <;> linarith + +/-- The bracket width tends to zero. -/ +theorem tendsto_bisect_width (a b : ℝ) : + Tendsto (fun n : ℕ => (b - a) / 2 ^ n) atTop (𝓝 0) := by + have h := (tendsto_pow_atTop_nhds_zero_of_lt_one (by norm_num : (0 : ℝ) ≤ 1 / 2) + (by norm_num : (1 / 2 : ℝ) < 1)).const_mul (b - a) + rw [mul_zero] at h + refine h.congr fun n => ?_ + rw [one_div_pow, div_eq_mul_one_div (b - a)] + +/-- **The left endpoints converge to the zero.** -/ +theorem tendsto_bisect_fst {f : ℝ → ℝ} {a b r : ℝ} (hf : StrictMonoOn f (Icc a b)) + (hr : r ∈ Icc a b) (hfr : f r = 0) : + Tendsto (fun n => (bisect f a b n).1) atTop (𝓝 r) := by + rw [tendsto_iff_dist_tendsto_zero] + refine squeeze_zero (fun n => dist_nonneg) (fun n => ?_) (tendsto_bisect_width a b) + obtain ⟨-, -, h1, h2⟩ := bisect_mem_of_strictMonoOn hf hr hfr n + have hw := bisect_width f a b n + rw [Real.dist_eq, abs_le] + constructor <;> linarith + +/-- **The right endpoints converge to the zero.** -/ +theorem tendsto_bisect_snd {f : ℝ → ℝ} {a b r : ℝ} (hf : StrictMonoOn f (Icc a b)) + (hr : r ∈ Icc a b) (hfr : f r = 0) : + Tendsto (fun n => (bisect f a b n).2) atTop (𝓝 r) := by + rw [tendsto_iff_dist_tendsto_zero] + refine squeeze_zero (fun n => dist_nonneg) (fun n => ?_) (tendsto_bisect_width a b) + obtain ⟨-, -, h1, h2⟩ := bisect_mem_of_strictMonoOn hf hr hfr n + have hw := bisect_width f a b n + rw [Real.dist_eq, abs_le] + constructor <;> linarith + +/-- Monotone supply minus strictly decreasing demand is strictly increasing. -/ +theorem strictMonoOn_sub_of_monotoneOn_of_strictAntiOn {S D : ℝ → ℝ} {s : Set ℝ} + (hS : MonotoneOn S s) (hD : StrictAntiOn D s) : StrictMonoOn (fun r => S r - D r) s := + fun x hx y hy hxy => by + have := hS hx hy hxy.le + have := hD hx hy hxy + simp only + linarith + +/-! ### The log Aiyagari instance -/ + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-- **Excess supply along a selection of stationary distributions**: capital supplied under +`ν r` minus capital demanded at `r`. -/ +noncomputable def excessSupply (ν : ℝ → ProbabilityMeasure P.State) (D : ℝ → ℝ) (r : ℝ) : ℝ := + P.aggregateCapital (ν r) - D r + +/-- The two facts about a selection that the bisection needs, from one inequality on the cap: +supply along it is monotone, and it is the ONLY stationary distribution at each rate. The +constants are chosen as in `log_equilibriumRate_unique_of_cap`. -/ +theorem log_selection_of_cap {rlo rhi : ℝ} + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) : + MonotoneOn (fun r => P.aggregateCapital (ν r)) (Icc rlo rhi) ∧ + ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, + (P.withRate r hrr).IsStationary μ → μ = ν r := by + set β : ℝ := (P.discount : ℝ) with hβdef + set R : ℝ := 1 + rhi with hRdef + have hR : 0 < R := by rw [hRdef]; linarith [hrlo, hlohi] + have hroom : 0 < 1 - β * R := by rw [hβdef, hRdef]; linarith + have hmaxpos : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome + have hcap0 : 0 < assetCap := by + by_contra h + have h' : assetCap ≤ 0 := not_lt.mp h + have : (1 - β * R) * assetCap ≤ 0 := mul_nonpos_of_nonneg_of_nonpos hroom.le h' + nlinarith [hcap, hβ, hmaxpos] + have hminpos : 0 < P.minIncome := P.minIncome_pos + set ε : ℝ := (1 - β * R) / 2 with hεdef + have hε : 0 < ε := by rw [hεdef]; linarith + set A : ℝ := P.minIncome * (1 - β * R) / (2 * β * R ^ 2) with hAdef + have hA : 0 < A := by rw [hAdef]; positivity + set a₀ : ℝ := min assetCap A with ha₀def + have ha₀ : 0 < a₀ := lt_min hcap0 hA + have hle : a₀ ≤ assetCap := min_le_left _ _ + have ha₀A : a₀ ≤ A := min_le_right _ _ + have hcapε : β * P.maxIncome + (β * R + ε) * assetCap < assetCap := by + rw [hεdef] + nlinarith [hcap] + have hcap' : β * (P.maxIncome + R * assetCap) < assetCap := by + nlinarith [hcap, mul_pos hβ hmaxpos] + have hcorn : β * R * (P.income z₀ + R * a₀) < P.minIncome := by + rw [hmin] + have hkey : β * R * (R * A) = P.minIncome * (1 - β * R) / 2 := by + rw [hAdef]; field_simp; try ring + have hmono : β * R * (R * a₀) ≤ β * R * (R * A) := + mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left ha₀A hR.le) (by positivity) + have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring + rw [this] + nlinarith [hkey, hmono, hroom, hminpos] + refine ⟨P.log_monotoneOn_capitalSupply hu hunb hβ hβ1 hrlo hlohi hβR hε hcapε hmono hz₀ hreach + ha₀ hle hcorn ν hν, fun r hr hrr μ hμ => ?_⟩ + exact (P.log_existsUnique_isStationary hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle hcorn + hr hrr).unique hμ (hν r hr hrr) + +/-- **Bisection on excess supply brackets and converges to the equilibrium rate of the log +economy**, along any selection of stationary distributions. -/ +theorem log_bisection_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) (hδ : 0 < rlo + δ) + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) + {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) {μ : ProbabilityMeasure P.State} + (hμ : (P.withRate r hrr).IsStationary μ) + (he : P.aggregateCapital μ = normalisedDemand α δ r) : + (∀ n, r ∈ Icc (bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).1 + (bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).2) ∧ + (∀ n, |((bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).1 + + (bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).2) / 2 - r| + ≤ (rhi - rlo) / 2 ^ (n + 1)) ∧ + Tendsto (fun n => (bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).1) atTop + (𝓝 r) := by + obtain ⟨hS, hid⟩ := P.log_selection_of_cap hu hunb hβ hβ1 hrlo hlohi hβR hcap hmono hz₀ hmin + hreach ν hν + have hE : StrictMonoOn (P.excessSupply ν (normalisedDemand α δ)) (Icc rlo rhi) := + strictMonoOn_sub_of_monotoneOn_of_strictAntiOn hS (normalisedDemand_strictAntiOn hα0 hα1 hδ) + have hzero : P.excessSupply ν (normalisedDemand α δ) r = 0 := by + unfold excessSupply + rw [← hid r hr hrr μ hμ, he, sub_self] + exact ⟨fun n => (bisect_mem_of_strictMonoOn hE hr hzero n).2.2, + abs_bisect_mid_sub_le hE hr hzero, tendsto_bisect_fst hE hr hzero⟩ + +/-- **Aiyagari (1994) at `μ = 1`: his bisection finds his equilibrium rate.** On any +`[rlo, rhi] ⊂ (-δ, λ)` with the cap clearing `(48/25)·maxIncome/(1 - (24/25)(1+rhi))`, and along +any selection of stationary distributions, the bisection on excess supply brackets the +equilibrium rate at every step, its midpoint is within `(rhi - rlo)/2^(n+1)` after `n` steps, +and its endpoints converge to the rate. -/ +theorem aiyagari1994_log_bisection {rlo rhi : ℝ} + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) + (hrlo : -2 / 25 < rlo) (hlohi : rlo ≤ rhi) (hlam : rhi < 1 / 24) + (hcap : 2 * (24 / 25) * P.maxIncome < (1 - 24 / 25 * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) + {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) {μ : ProbabilityMeasure P.State} + (hμ : (P.withRate r hrr).IsStationary μ) + (he : P.aggregateCapital μ = normalisedDemand (9 / 25) (2 / 25) r) : + (∀ n, r ∈ Icc (bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).1 + (bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).2) ∧ + (∀ n, |((bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).1 + + (bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).2) / 2 - r| + ≤ (rhi - rlo) / 2 ^ (n + 1)) ∧ + Tendsto (fun n => (bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).1) + atTop (𝓝 r) := + P.log_bisection_of_cap (by norm_num) (by norm_num) (by linarith) hu hunb + (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) + (by rw [hβ]; exact hcap) hmono hz₀ hmin hreach ν hν hr hrr hμ he + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean new file mode 100644 index 000000000..d3a17a773 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean @@ -0,0 +1,223 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness +public import LeanPool.AiyagariEquilibrium.Equilibrium.Uniqueness + + +/-! +# Light (2018) Theorem 2: aggregate savings rise with the interest rate + +`Equilibrium.Uniqueness` proves single crossing — Light's Theorem 3 — but carries MONOTONE +CAPITAL SUPPLY as a hypothesis, with nothing in the development connecting that hypothesis to +the household side. This file supplies the connection. Given that every household saves more at +the higher rate, aggregate capital is larger there, so the hypothesis of `equilibriumRate_unique` +follows from the conclusion of Light's Theorem 1. + +That leaves the whole uniqueness argument resting on one thing: Theorem 1, itself reduced to the +Carroll and Kimball step in `IncomeFluctuationCarrollKimball`. + +## The argument + +Aggregate capital is mean assets under the stationary distribution, so what has to be compared +is two distributions, not two numbers. The comparison is first-order stochastic dominance in the +asset coordinate, `Dominates`, tested against bounded continuous functions that rise with assets +at each income state. + +Three facts do the work, and they are the three things a dominance argument always needs. + +* The Markov operator PRESERVES the test class: `markovOp` of a function rising in assets rises + in assets, because the policy does (`policy_mono`). Note where the income chain enters — it + does not. The probabilities depend on today's income state alone, so comparing two asset + levels at a FIXED income state leaves them untouched. +* A uniformly larger policy gives a larger operator, on the test class. +* Dominance survives weak limits, since the test functions are bounded and continuous. + +Then run both economies forward from the SAME initial distribution. They stay ordered at every +date by the first two facts, and each converges to its own stationary distribution by +`tendsto_pushProb_iterate` — the Doeblin convergence proved for step 4 of the roadmap, which has +had no consumer until now. The third fact passes the order to the limit. + +The Doeblin data appears as hypotheses at both rates. It is the same minorisation the uniqueness +theorem runs on: an atom at the borrowing constraint. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +variable {assetFloor assetCap : ℝ} + +/-! ### Stochastic dominance in the asset coordinate -/ + +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] [MeasurableSpace Z] [BorelSpace Z] in +/-- Bounded continuous functions that rise with assets, at each income state separately. These +are the test functions of first-order stochastic dominance in the asset coordinate. -/ +def MonoAsset (h : (↥(Icc assetFloor assetCap) × Z) →ᵇ ℝ) : Prop := + ∀ (z : Z) (a b : ↥(Icc assetFloor assetCap)), a ≤ b → h (a, z) ≤ h (b, z) + +/-- **First-order stochastic dominance in assets.** -/ +def Dominates (μ ν : ProbabilityMeasure (↥(Icc assetFloor assetCap) × Z)) : Prop := + ∀ h : (↥(Icc assetFloor assetCap) × Z) →ᵇ ℝ, MonoAsset h → + ∫ s, h s ∂(μ : Measure _) ≤ ∫ s, h s ∂(ν : Measure _) + +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] [BorelSpace Z] in +theorem Dominates.refl (μ : ProbabilityMeasure (↥(Icc assetFloor assetCap) × Z)) : + Dominates μ μ := fun _ _ => le_rfl + +omit [Fintype Z] [Nonempty Z] in +/-- **Dominance survives weak limits**, because the test functions are bounded and +continuous. -/ +theorem Dominates.of_tendsto [Finite Z] + {μs νs : ℕ → ProbabilityMeasure (↥(Icc assetFloor assetCap) × Z)} + {μ ν : ProbabilityMeasure (↥(Icc assetFloor assetCap) × Z)} + (hμ : Tendsto μs atTop (𝓝 μ)) (hν : Tendsto νs atTop (𝓝 ν)) + (hd : ∀ n, Dominates (μs n) (νs n)) : Dominates μ ν := by + let _ := Fintype.ofFinite Z + exact fun h hh => le_of_tendsto_of_tendsto + (ProbabilityMeasure.tendsto_iff_forall_integral_tendsto.mp hμ h) + (ProbabilityMeasure.tendsto_iff_forall_integral_tendsto.mp hν h) + (Eventually.of_forall fun n => hd n h hh) + +/-! ### The operator preserves the test class, and respects a larger policy -/ + +variable (P Q : IncomeFluctuation Z assetFloor assetCap) + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The Markov operator preserves the test class.** The expected value tomorrow of a payoff +that rises with assets itself rises with assets, because the policy does. -/ +theorem monoAsset_markovOp {h : P.State →ᵇ ℝ} (hh : MonoAsset h) : + MonoAsset (P.markovOp h) := by + intro z a b hab + simp only [markovOp_apply] + refine Finset.sum_le_sum fun z' _ => ?_ + refine mul_le_mul_of_nonneg_left ?_ (P.prob_nonneg _ _) + exact hh z' _ _ (P.policy_mono a.2 b.2 hab) + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **A uniformly larger policy gives a larger operator**, on the test class. -/ +theorem markovOp_le_of_policy_le + (hprob : ∀ z z' : Z, P.transitionMatrix z z' = Q.transitionMatrix z z') + (hpol : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → P.policy s ≤ Q.policy s) + {h : P.State →ᵇ ℝ} (hh : MonoAsset h) (s : P.State) : + P.markovOp h s ≤ Q.markovOp h s := by + simp only [markovOp_apply] + refine Finset.sum_le_sum fun z' _ => ?_ + rw [show P.prob s z' = Q.prob s z' from hprob _ _] + refine mul_le_mul_of_nonneg_left ?_ (Q.prob_nonneg _ _) + exact hh z' _ _ (hpol (P.incl s) (P.incl_mem s)) + +/-- **One period preserves dominance.** -/ +theorem Dominates.pushProb + (hprob : ∀ z z' : Z, P.transitionMatrix z z' = Q.transitionMatrix z z') + (hpol : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → P.policy s ≤ Q.policy s) + {μ ν : ProbabilityMeasure P.State} (hd : Dominates μ ν) : + Dominates (P.pushProb μ) (Q.pushProb ν) := by + intro h hh + rw [coe_pushProb, coe_pushProb, P.integral_push _ h, Q.integral_push _ h] + refine le_trans (integral_mono ((P.markovOp h).integrable _) + ((Q.markovOp h).integrable _) ?_) (hd _ (Q.monoAsset_markovOp hh)) + exact fun s => markovOp_le_of_policy_le P Q hprob hpol hh s + +/-! ### Theorem 2 -/ + +/-- **Stationary distributions are ordered when policies are.** Both economies are run forward +from the same starting distribution and stay ordered at every date, so their limits are ordered. + +Convergence is the hypothesis rather than the Doeblin data, because that is all the argument +uses; `dominates_stationary_of_policy_le` supplies it from the minorisation. -/ +theorem dominates_of_policy_le + (hprob : ∀ z z' : Z, P.transitionMatrix z z' = Q.transitionMatrix z z') + (hpol : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → P.policy s ≤ Q.policy s) + {μ₀ μ ν : ProbabilityMeasure P.State} + (hP : Tendsto (fun m => P.pushProb^[m] μ₀) atTop (𝓝 μ)) + (hQ : Tendsto (fun m => Q.pushProb^[m] μ₀) atTop (𝓝 ν)) : + Dominates μ ν := by + refine Dominates.of_tendsto hP hQ fun n => ?_ + induction n with + | zero => exact Dominates.refl μ₀ + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact ih.pushProb P Q hprob hpol + +/-- The same, with convergence supplied by the Doeblin minorisation at each rate — the atom at +the borrowing constraint that `Distribution.Uniqueness` runs on. -/ +theorem dominates_stationary_of_policy_le + (hprob : ∀ z z' : Z, P.transitionMatrix z z' = Q.transitionMatrix z z') + (hpol : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → P.policy s ≤ Q.policy s) + {zP : Z} {sP : P.State} {pP : ℝ} {NP : ℕ} (hpP0 : 0 < pP) + (hpP : ∀ s : P.State, pP ≤ P.prob s zP) (hbadP : ∀ s, (P.badStep zP)^[NP] s = sP) + {zQ : Z} {sQ : Q.State} {pQ : ℝ} {NQ : ℕ} (hpQ0 : 0 < pQ) + (hpQ : ∀ s : Q.State, pQ ≤ Q.prob s zQ) (hbadQ : ∀ s, (Q.badStep zQ)^[NQ] s = sQ) + {μ ν : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) (hν : Q.IsStationary ν) : + Dominates μ ν := by + obtain ⟨s₀⟩ : Nonempty P.State := inferInstance + exact dominates_of_policy_le P Q hprob hpol + (μ₀ := ⟨Measure.dirac s₀, inferInstance⟩) + (P.tendsto_pushProb_iterate hpP0 hpP hbadP _ hμ) + (Q.tendsto_pushProb_iterate hpQ0 hpQ hbadQ _ hν) + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem monoAsset_assetCoord : MonoAsset P.assetCoord := fun _ _ _ hab => hab + +/-- **Light (2018) Theorem 2.** If every household saves at least as much in the second economy +as in the first, aggregate capital is at least as large. -/ +theorem aggregateCapital_le_of_policy_le + (hprob : ∀ z z' : Z, P.transitionMatrix z z' = Q.transitionMatrix z z') + (hpol : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → P.policy s ≤ Q.policy s) + {zP : Z} {sP : P.State} {pP : ℝ} {NP : ℕ} (hpP0 : 0 < pP) + (hpP : ∀ s : P.State, pP ≤ P.prob s zP) (hbadP : ∀ s, (P.badStep zP)^[NP] s = sP) + {zQ : Z} {sQ : Q.State} {pQ : ℝ} {NQ : ℕ} (hpQ0 : 0 < pQ) + (hpQ : ∀ s : Q.State, pQ ≤ Q.prob s zQ) (hbadQ : ∀ s, (Q.badStep zQ)^[NQ] s = sQ) + {μ ν : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) (hν : Q.IsStationary ν) : + P.aggregateCapital μ ≤ Q.aggregateCapital ν := + dominates_stationary_of_policy_le P Q hprob hpol hpP0 hpP hbadP hpQ0 hpQ hbadQ hμ hν + P.assetCoord (monoAsset_assetCoord P) + +/-- **Capital supply is monotone in the interest rate** whenever the household policy is. + +This is exactly the hypothesis `hS` of `equilibriumRate_unique`, so with Light's Theorem 1 in +hand — the policy rising in the rate — the equilibrium rate is unique. Theorem 1 is the only +thing still missing, and `IncomeFluctuationCarrollKimball` has reduced it to one step. -/ +theorem monotoneOn_capitalSupply_of_policy_mono {rlo rhi : ℝ} + (Pf : ℝ → IncomeFluctuation Z assetFloor assetCap) + (hprob : ∀ r r' : ℝ, ∀ z z' : Z, + (Pf r).transitionMatrix z z' = (Pf r').transitionMatrix z z') + (hpol : ∀ r ∈ Icc rlo rhi, ∀ r' ∈ Icc rlo rhi, r ≤ r' → + ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → (Pf r).policy s ≤ (Pf r').policy s) + (μ₀ : ProbabilityMeasure P.State) (ν : ℝ → ProbabilityMeasure P.State) + (hconv : ∀ r ∈ Icc rlo rhi, Tendsto (fun m => (Pf r).pushProb^[m] μ₀) atTop (𝓝 (ν r))) : + MonotoneOn (fun r => P.aggregateCapital (ν r)) (Icc rlo rhi) := fun r hr r' hr' hrr => + dominates_of_policy_le (Pf r) (Pf r') (hprob r r') (hpol r hr r' hr' hrr) + (hconv r hr) (hconv r' hr') P.assetCoord (monoAsset_assetCoord P) + +/-- **Capital supply is ordered when the policies are**, across two FAMILIES of economies rather +than across two rates of one. The patience comparative static consumes this: a more patient +population saves more at every rate, so its supply schedule lies above. -/ +theorem capitalSupply_le_of_policy_le {rlo rhi : ℝ} + (Pf Qf : ℝ → IncomeFluctuation Z assetFloor assetCap) + (hprob : ∀ r : ℝ, ∀ z z' : Z, (Pf r).transitionMatrix z z' = (Qf r).transitionMatrix z z') + (hpol : ∀ r ∈ Icc rlo rhi, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → + (Pf r).policy s ≤ (Qf r).policy s) + (μ₀ : ProbabilityMeasure P.State) (ν ρ : ℝ → ProbabilityMeasure P.State) + (hconvP : ∀ r ∈ Icc rlo rhi, Tendsto (fun m => (Pf r).pushProb^[m] μ₀) atTop (𝓝 (ν r))) + (hconvQ : ∀ r ∈ Icc rlo rhi, Tendsto (fun m => (Qf r).pushProb^[m] μ₀) atTop (𝓝 (ρ r))) + {r : ℝ} (hr : r ∈ Icc rlo rhi) : + P.aggregateCapital (ν r) ≤ P.aggregateCapital (ρ r) := + dominates_of_policy_le (Pf r) (Qf r) (hprob r) (hpol r hr) (hconvP r hr) (hconvQ r hr) + P.assetCoord (monoAsset_assetCoord P) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean new file mode 100644 index 000000000..79f415e6d --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean @@ -0,0 +1,207 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity + + +/-! +# The firm side + +`exists_aiyagari_equilibrium` takes any continuous capital demand schedule. This file supplies +one that is DERIVED from a production function rather than posited, so the equilibrium theorem +becomes a statement about a model rather than about a schedule. + +The technology is Cobb–Douglas with `α = 1/2`, one unit of labour and total factor +productivity `A`, so output is `A √K` and the marginal product of capital is `A / (2√K)`. +Setting the net marginal product equal to the interest rate, + + A / (2√K) - δ = r, + +and solving gives `K = A² / (4(r+δ)²)`. `capitalDemand_marginalProduct` checks that the +solution really does satisfy the first-order condition, which is what stops the formula from +being an arbitrary decreasing function with a suggestive name. + +Productivity is a PARAMETER rather than fixed at one because the demand schedule otherwise +carries a scale the household side has to be stretched to meet. `exists_firm_of_bounds` is what +that buys: given any rate interval and any two-sided bound on capital supply, some firm meets it +there. The firm can be calibrated to the households rather than the other way round. + +`α = 1/2` is chosen so the inversion is algebraic. A general exponent would need `rpow` and +would change nothing conceptually. + +## What this does and does not close + +Equilibrium needs excess demand to change sign across `[rlo, rhi]`. The LOW end is now a +theorem: capital demand diverges as `r` approaches `-δ`, while capital supply can never exceed +the asset cap, so `capitalDemand δ rlo ≥ assetCap ≥ S rlo` for `rlo` close enough to `-δ`. + +The HIGH end is not, and the obstruction is economic rather than technical. It needs +`capitalDemand δ rhi ≤ S rhi`, and since Cobb–Douglas demand is strictly positive that forces +`S rhi > 0` -- households must hold capital at high interest rates. That is obvious +economically and unproved here. Worse, it is in tension with the only calibration whose +Doeblin hypotheses are verified: the impatient household of `NonVacuity` is driven to the +borrowing constraint at EVERY asset level, so its stationary distribution sits at zero assets +and its capital supply is identically zero. A calibration with a genuine equilibrium needs the +constraint to bind at the bad income state but not the good one, which the uniform corner +condition of `policy_eq_zero_of_corner` cannot express. +-/ + +@[expose] public section + +open Set Filter Topology MeasureTheory + +namespace LeanEconomics + +/-- Capital demand from a Cobb–Douglas firm with `α = 1/2`, unit labour and unit productivity: +the capital stock at which the net marginal product equals `r`. -/ +noncomputable def capitalDemand (A δ r : ℝ) : ℝ := A ^ 2 / (4 * (r + δ) ^ 2) + +/-- **The demand schedule solves the firm's first-order condition.** -/ +theorem capitalDemand_marginalProduct {A δ r : ℝ} (hA : 0 < A) (h : 0 < r + δ) : + A / (2 * Real.sqrt (capitalDemand A δ r)) - δ = r := by + have hsq : capitalDemand A δ r = (A / (2 * (r + δ))) ^ 2 := by + simp only [capitalDemand]; field_simp; ring + rw [hsq, Real.sqrt_sq (by positivity)] + field_simp + ring + +theorem capitalDemand_pos {A δ r : ℝ} (hA : 0 < A) (h : 0 < r + δ) : + 0 < capitalDemand A δ r := by + simp only [capitalDemand]; positivity + +theorem continuousOn_capitalDemand {A δ rlo rhi : ℝ} (h : 0 < rlo + δ) : + ContinuousOn (capitalDemand A δ) (Icc rlo rhi) := by + refine ContinuousOn.div continuousOn_const ?_ fun r hr => ?_ + · exact (continuous_const.mul ((continuous_id.add continuous_const).pow 2)).continuousOn + · have : 0 < r + δ := lt_of_lt_of_le h (by linarith [hr.1]) + positivity + +/-- Capital demand falls as the interest rate rises. -/ +theorem capitalDemand_antitoneOn {A δ : ℝ} {rlo rhi : ℝ} (h : 0 < rlo + δ) : + AntitoneOn (capitalDemand A δ) (Icc rlo rhi) := by + intro a ha b hb hab + have ha' : 0 < a + δ := lt_of_lt_of_le h (by linarith [ha.1]) + have hb' : 0 < b + δ := lt_of_lt_of_le h (by linarith [hb.1]) + simp only [capitalDemand] + gcongr + +/-- **Capital demand diverges as the rate approaches `-δ`.** Given any bound -- in particular +the asset cap, which capital supply can never exceed -- some admissible rate demands more. -/ +theorem exists_capitalDemand_ge (δ : ℝ) {M : ℝ} (hM : 0 ≤ M) : + ∃ rlo : ℝ, 0 < rlo + δ ∧ M ≤ capitalDemand 1 δ rlo := by + have hM1 : (0 : ℝ) < M + 1 := by linarith + refine ⟨(1 / (2 * (M + 1))) - δ, by simp only [sub_add_cancel]; positivity, ?_⟩ + have hrw : (1 / (2 * (M + 1)) - δ) + δ = 1 / (2 * (M + 1)) := by ring + simp only [capitalDemand, hrw, one_pow] + rw [le_div_iff₀ (by positivity)] + have hexp : 4 * (1 / (2 * (M + 1))) ^ 2 = 1 / (M + 1) ^ 2 := by field_simp; ring + rw [hexp, mul_one_div, div_le_one (by positivity)] + nlinarith + +/-! ### Reading the two sides of the sign change off the technology + +`exists_capitalDemand_ge` and `exists_capitalDemand_le` FIND a rate at which demand clears a +bound. The two lemmas here do the opposite and more useful thing: given the rate, they say what +the technology has to satisfy. Both are the same inequality `4(r+δ)²K ≤ A²` read in the two +directions, which in root form is `2(r+δ)√K ≤ A` — the marginal product of `K` units at rate `r`. +-/ + +/-- **The technology is productive enough to demand `K` at rate `r`.** In root form, +`2(r+δ)√K ≤ A`. -/ +theorem le_capitalDemand_of_sq {A δ r K : ℝ} (hr : 0 < r + δ) + (h : 4 * (r + δ) ^ 2 * K ≤ A ^ 2) : K ≤ capitalDemand A δ r := by + simp only [capitalDemand] + rw [le_div_iff₀ (by positivity)] + linarith + +/-- **The technology is weak enough to demand no more than `K` at rate `r`.** In root form, +`A ≤ 2(r+δ)√K`. -/ +theorem capitalDemand_le_of_sq {A δ r K : ℝ} (hr : 0 < r + δ) + (h : A ^ 2 ≤ 4 * (r + δ) ^ 2 * K) : capitalDemand A δ r ≤ K := by + simp only [capitalDemand] + rw [div_le_iff₀ (by positivity)] + linarith + +/-! ### The implied rate + +Equilibrium is often stated the other way round: a rate `r` induces a capital stock, the capital +stock induces a marginal product, and equilibrium is where the induced rate returns `r`. That is +the same condition — `impliedRate` inverts `capitalDemand` — and `capitalDemand_eq_iff` says so. -/ + +/-- The net marginal product of capital at stock `K`: the rate a firm holding `K` would pay. -/ +noncomputable def impliedRate (A δ K : ℝ) : ℝ := A / (2 * Real.sqrt K) - δ + +/-- **`impliedRate` inverts `capitalDemand`.** Demanding `K` at rate `r` is the same as `K` +implying `r`. -/ +theorem capitalDemand_eq_iff {A δ r K : ℝ} (hA : 0 < A) (hr : 0 < r + δ) (hK : 0 < K) : + K = capitalDemand A δ r ↔ impliedRate A δ K = r := by + constructor + · rintro rfl + exact capitalDemand_marginalProduct hA hr + · intro h + have hsK : 0 < Real.sqrt K := Real.sqrt_pos.mpr hK + have hrw : A / (2 * Real.sqrt K) = r + δ := by + simp only [impliedRate] at h; linarith + have hsq : Real.sqrt K = A / (2 * (r + δ)) := by + field_simp at hrw ⊢ + linarith [hrw] + have hKeq : K = (Real.sqrt K) ^ 2 := (Real.sq_sqrt hK.le).symm + rw [hKeq, hsq] + simp only [capitalDemand] + field_simp + ring + + +/-- **Equilibrium in implied-rate form.** A rate is an equilibrium exactly when the capital its +households hold implies it back. -/ +theorem isAiyagariEquilibrium_iff_impliedRate {Z : Type*} [Fintype Z] [Nonempty Z] + [TopologicalSpace Z] [DiscreteTopology Z] [MeasurableSpace Z] [BorelSpace Z] + {assetFloor assetCap : ℝ} + {Pf : ℝ → IncomeFluctuation Z assetFloor assetCap} {A δ r : ℝ} (hA : 0 < A) (hr : 0 < r + δ) : + IsAiyagariEquilibrium Pf (capitalDemand A δ) r ↔ + ∃ μ : ProbabilityMeasure (Pf r).State, (Pf r).IsStationary μ ∧ + 0 < (Pf r).aggregateCapital μ ∧ impliedRate A δ ((Pf r).aggregateCapital μ) = r := by + constructor + · rintro ⟨μ, hμ, hK⟩ + have hpos : 0 < (Pf r).aggregateCapital μ := by rw [hK]; exact capitalDemand_pos hA hr + exact ⟨μ, hμ, hpos, (capitalDemand_eq_iff hA hr hpos).mp hK⟩ + · rintro ⟨μ, hμ, hpos, himp⟩ + exact ⟨μ, hμ, (capitalDemand_eq_iff hA hr hpos).mpr himp⟩ + + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **Capital supply never exceeds the asset cap**, because every household's assets are +capped. This is what makes the low end of the sign change a theorem. -/ +theorem aggregateCapital_le (μ : ProbabilityMeasure P.State) : + P.aggregateCapital μ ≤ assetCap := by + have hle : ∀ s : P.State, P.assetCoord s ≤ assetCap := fun s => s.1.2.2 + calc P.aggregateCapital μ ≤ ∫ _s, assetCap ∂(μ : Measure P.State) := + integral_mono (P.assetCoord.integrable _) (integrable_const _) hle + _ = assetCap := by simp + +/-- Capital supply never falls below the borrowing limit. -/ +theorem assetFloor_le_aggregateCapital (μ : ProbabilityMeasure P.State) : + assetFloor ≤ P.aggregateCapital μ := by + have hle : ∀ s : P.State, assetFloor ≤ P.assetCoord s := fun s => s.1.2.1 + calc assetFloor = ∫ _s, assetFloor ∂(μ : Measure P.State) := by simp + _ ≤ P.aggregateCapital μ := + integral_mono (integrable_const _) (P.assetCoord.integrable _) hle + +/-- **The low end of the sign change is a theorem.** -/ +theorem exists_rate_capitalDemand_ge (δ : ℝ) (μ : ProbabilityMeasure P.State) : + ∃ rlo : ℝ, 0 < rlo + δ ∧ P.aggregateCapital μ ≤ capitalDemand 1 δ rlo := by + obtain ⟨rlo, hrlo, hge⟩ := exists_capitalDemand_ge δ P.assetCap_nonneg + exact ⟨rlo, hrlo, le_trans (P.aggregateCapital_le μ) hge⟩ + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean new file mode 100644 index 000000000..500079cd4 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean @@ -0,0 +1,544 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Distribution.Feller +public import LeanPool.AiyagariEquilibrium.Equilibrium.Aiyagari +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone + + +/-! +# Aggregate capital is strictly positive + +Every equilibrium result so far is conditional on excess demand changing sign, and nothing in +the development says that households save anything at all. This file supplies the missing +economics: under log utility, households rich enough in income save strictly, and the stationary +distribution then carries strictly positive aggregate capital. + +## Capital is the mean of the policy + +`markovOp_assetCoord` is the identity the whole argument runs on. The expected asset coordinate +next period is the policy today, because the asset the household carries forward does not depend +on which income shock arrives: + + `markovOp assetCoord = policyCoord`. + +With stationarity that gives `aggregateCapital μ = ∫ policy dμ` — aggregate capital is the mean +saving, not merely something computed from the distribution. Everything after is a lower bound +on that integral. + +## Rich households save, and the argument is the crudest deviation again + +Saving `h` rather than nothing costs `u resources - u (resources - h)` and gains +`β (cont h - cont 0)`. For LOG the cost is `log (m / (m - h))`, which SHRINKS as resources grow, +while the gain is a fixed positive number — positive because the value function is strictly +increasing in assets, which `valueFunction_lt_of_lt` proves from strict monotonicity of `u`, +itself recovered from strict concavity. So above an explicit income level the household saves +strictly, with no envelope theorem and no Euler equation. + +Since the cost falls with resources, it is enough to check the inequality at the household's +POOREST state in that income, `a = 0`, and it then holds at every asset level. + +## Reaching those households + +The income coordinate of the stationary distribution is bounded below by the worst transition +probability into that state, and that too is pure duality: the indicator of an income state is +CONTINUOUS here, `Z` being discrete, so it is a legitimate test function and +`markovOp (incomeIndicator z₁) s = transitionMatrix s.2 z₁`. Stationarity turns a uniform lower +bound on the column of the transition matrix into a lower bound on the mass. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### Strict monotonicity -/ + +/-- Period utility is STRICTLY increasing: a concave function that is flat on an interval is +affine there, which strict concavity forbids. -/ +theorem strictMonoOn_u : StrictMonoOn P.u (Ioi 0) := by + intro x hx y hy hxy + rcases lt_or_eq_of_le (P.monotoneOn_u hx hy hxy.le) with h | h + · exact h + · exfalso + have hx0 : (0 : ℝ) < x := hx + have hy0 : (0 : ℝ) < y := hy + have hhalf : (0 : ℝ) < 1 / 2 := by norm_num + have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num + have hstrict := P.strictConcaveOn_u.2 hx hy (ne_of_lt hxy) hhalf hhalf hsum + simp only [smul_eq_mul] at hstrict + have hmid : (1 / 2 : ℝ) * x + (1 / 2 : ℝ) * y ∈ Ioi (0 : ℝ) := mem_Ioi.mpr (by linarith) + have hle : P.u ((1 / 2 : ℝ) * x + (1 / 2 : ℝ) * y) ≤ P.u y := + P.monotoneOn_u hmid hy (by linarith) + linarith + +/-- **The value function is strictly increasing in assets.** More assets buy strictly more +consumption at the same saving plan. -/ +theorem valueFunction_lt_of_lt (hpc : P.PositiveConsumption) {x y : ℝ} {z : Z} + (hx : x ∈ Icc assetFloor assetCap) + (hy : y ∈ Icc assetFloor assetCap) (hxy : x < y) : + P.toExtended.valueFunction (x, z) < P.toExtended.valueFunction (y, z) := by + have hbm : P.policy (x, z) ∈ P.toExtended.feasible (x, z) := P.policy_mem _ + have hbm' : P.policy (x, z) ∈ P.toExtended.feasible (y, z) := P.feasible_mono hxy.le hbm + have hcx : 0 < P.consumption (x, z) (P.policy (x, z)) := P.consumption_policy_pos hpc hx + have hresl : P.resources (x, z) < P.resources (y, z) := by + simp only [resources, max_eq_right hx.1, max_eq_right hy.1] + nlinarith [P.interest_gt_neg_one] + have hcy : 0 < P.consumption (y, z) (P.policy (x, z)) := by + simp only [consumption] at hcx ⊢; linarith + have hstep : P.objR (x, z) (P.policy (x, z)) < P.objR (y, z) (P.policy (x, z)) := by + simp only [objR] + have := P.strictMonoOn_u (mem_Ioi.mpr hcx) (mem_Ioi.mpr hcy) + (by simp only [consumption] at hcx ⊢; linarith) + linarith + have h1 := P.objR_le_of_mem hy hbm' (P.mem_dom_of_pos hcy) + have e1 : P.objR (x, z) (P.policy (x, z)) = P.toExtended.valueFunction (x, z) := by + simp only [objR, cont]; exact (P.valueFunction_eq_policy hx).symm + have e2 : P.objR (y, z) (P.policy (y, z)) = P.toExtended.valueFunction (y, z) := by + simp only [objR, cont]; exact (P.valueFunction_eq_policy hy).symm + rw [← e1, ← e2] + linarith + +/-- **The continuation is strictly increasing**, since some income state has positive +probability. -/ +theorem cont_lt_of_lt (hpc : P.PositiveConsumption) (z : Z) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : x < y) : P.cont z x < P.cont z y := by + obtain ⟨z₀, hz₀⟩ : ∃ z₀ : Z, 0 < P.transitionMatrix z z₀ := by + by_contra hcon + push Not at hcon + have hz : ∑ z', P.transitionMatrix z z' = 0 := + Finset.sum_eq_zero fun z' _ => + le_antisymm (hcon z') (P.transitionMatrix_nonneg _ _) + rw [P.transitionMatrix_sum] at hz + norm_num at hz + simp only [cont] + refine Finset.sum_lt_sum (fun z' _ => ?_) ⟨z₀, Finset.mem_univ _, ?_⟩ + · exact mul_le_mul_of_nonneg_left (P.valueFunction_lt_of_lt hpc hx hy hxy).le + (P.transitionMatrix_nonneg _ _) + · exact mul_lt_mul_of_pos_left (P.valueFunction_lt_of_lt hpc hx hy hxy) hz₀ + +/-! ### Capital is the mean of the policy -/ + +theorem norm_policy_incl_le (s : P.State) : + ‖P.policy (P.incl s)‖ ≤ |assetFloor| + |assetCap| := by + have h := P.policy_mem_region (P.incl s) + rw [Real.norm_eq_abs, abs_le] + exact ⟨by linarith [h.1, neg_abs_le assetFloor, abs_nonneg assetCap], + by linarith [h.2, le_abs_self assetCap, abs_nonneg assetFloor]⟩ + +/-- Saving carried forward, as a bounded continuous function of the state. -/ +noncomputable def policyCoord : P.State →ᵇ ℝ := + ofNormedAddCommGroup (fun s => P.policy (P.incl s)) + (P.continuousOn_policy.comp_continuous P.continuous_incl P.incl_mem) + (|assetFloor| + |assetCap|) P.norm_policy_incl_le + +@[simp] theorem policyCoord_apply (s : P.State) : P.policyCoord s = P.policy (P.incl s) := rfl + +/-- **The expected asset coordinate next period is today's saving.** The asset carried forward +does not depend on which income shock arrives, so the expectation collapses. -/ +theorem markovOp_assetCoord : P.markovOp P.assetCoord = P.policyCoord := by + ext s + simp only [markovOp_apply, policyCoord_apply] + have hz : ∀ z' : Z, P.assetCoord (P.nextState s z') = P.policy (P.incl s) := fun _ => rfl + simp only [hz] + rw [← Finset.sum_mul, P.prob_sum, one_mul] + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- **Aggregate capital is mean saving.** -/ +theorem aggregateCapital_eq_integral_policy {μ : ProbabilityMeasure P.State} + (hμ : P.IsStationary μ) : + P.aggregateCapital μ = ∫ s, P.policyCoord s ∂(μ : Measure P.State) := by + calc P.aggregateCapital μ = P.aggregateCapital (P.pushProb μ) := by rw [hμ] + _ = ∫ s, P.markovOp P.assetCoord s ∂(μ : Measure P.State) := by + rw [aggregateCapital, coe_pushProb, P.integral_push] + _ = ∫ s, P.policyCoord s ∂(μ : Measure P.State) := by rw [P.markovOp_assetCoord] + +/-! ### The stationary distribution reaches every income state -/ + +open Classical in +/-- The indicator of an income state — continuous, because `Z` is discrete. -/ +noncomputable def incomeIndicator (z₁ : Z) : P.State →ᵇ ℝ := + ofNormedAddCommGroup (fun s => if s.2 = z₁ then 1 else 0) + ((continuous_of_discreteTopology (f := fun z : Z => if z = z₁ then (1 : ℝ) else 0)).comp + continuous_snd) 1 + (fun s => by by_cases h : s.2 = z₁ <;> simp [h]) + +omit [MeasurableSpace Z] [BorelSpace Z] in +open Classical in +@[simp] theorem incomeIndicator_apply (z₁ : Z) (s : P.State) : + P.incomeIndicator z₁ s = if s.2 = z₁ then 1 else 0 := rfl + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem markovOp_incomeIndicator (z₁ : Z) (s : P.State) : + P.markovOp (P.incomeIndicator z₁) s = P.transitionMatrix s.2 z₁ := by + classical + simp only [markovOp_apply, incomeIndicator_apply, nextState] + rw [Finset.sum_eq_single z₁ (fun b _ hb => by simp [hb]) (by simp)] + simp [prob] + +/-- **The stationary mass on an income state is at least the worst transition into it.** -/ +theorem le_integral_incomeIndicator {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) + {z₁ : Z} {p₀ : ℝ} (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) : + p₀ ≤ ∫ s, P.incomeIndicator z₁ s ∂(μ : Measure P.State) := by + have hprob : IsProbabilityMeasure (μ : Measure P.State) := μ.2 + calc p₀ = ∫ _s, p₀ ∂(μ : Measure P.State) := by simp + _ ≤ ∫ s, P.markovOp (P.incomeIndicator z₁) s ∂(μ : Measure P.State) := by + refine integral_mono (integrable_const _) ((P.markovOp _).integrable _) fun s => ?_ + rw [P.markovOp_incomeIndicator] + exact hp s.2 + _ = ∫ s, P.incomeIndicator z₁ s ∂(P.push (μ : Measure P.State)) := + (P.integral_push _ _).symm + _ = ∫ s, P.incomeIndicator z₁ s ∂(μ : Measure P.State) := by + rw [← coe_pushProb, hμ] + +/-! ### Rich households save -/ + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **A household with enough income saves strictly.** The cost of saving `h` instead of nothing +is `log (m / (m - h))`, which falls as resources grow, while the gain `β (cont h - cont 0)` is a +fixed positive number. So it is enough to check the comparison at `a = 0`, the poorest state in +that income, and it then holds at every asset level. -/ +theorem policy_pos_of_cost (hfl : assetFloor = 0) (z₁ : Z) {h κ : ℝ} (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hcost : ∀ R : ℝ, P.income z₁ ≤ R → P.u R - P.u (R - h) ≤ κ) + (hgain : κ < P.discount * (P.cont z₁ h - P.cont z₁ 0)) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + 0 < P.policy (a, z₁) := by + subst hfl + rcases lt_or_eq_of_le (P.policy_mem_region (a, z₁)).1 with hpos | hzero + · exact hpos + exfalso + have hm : P.income z₁ ≤ P.resources (a, z₁) := by + simp only [resources, max_eq_right ha.1] + nlinarith [P.interest_gt_neg_one, ha.1] + have hmh : h < P.resources (a, z₁) := lt_of_lt_of_le hhinc hm + have hfeas : h ∈ P.toExtended.feasible (a, z₁) := by + rw [P.feasible_eq, P.maxSaving_eq] + exact ⟨hh0.le, le_min hhcap hmh.le⟩ + have hch : 0 < P.consumption (a, z₁) h := by simp only [consumption]; linarith + have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hch) + rw [← hzero] at hopt + simp only [objR, consumption, sub_zero] at hopt + linarith [hcost (P.resources (a, z₁)) hm] + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- The log instance of `policy_pos_of_cost`: the cost of saving `h` is `log (m / (m - h))`, +which falls as resources grow, so it is enough to check it at the poorest state in that income. -/ +theorem policy_pos_of_income (hfl : assetFloor = 0) (hu : P.u = Real.log) (z₁ : Z) {h : ℝ} + (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hgain : Real.log (P.income z₁ / (P.income z₁ - h)) + < P.discount * (P.cont z₁ h - P.cont z₁ 0)) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + 0 < P.policy (a, z₁) := by + subst hfl + have hinc0 : 0 < P.income z₁ := lt_trans hh0 hhinc + refine P.policy_pos_of_cost rfl z₁ hh0 hhcap hhinc (fun R hm => ?_) hgain ha + rw [hu] + have hR : 0 < R := lt_of_lt_of_le hinc0 hm + have hmono : R / (R - h) ≤ P.income z₁ / (P.income z₁ - h) := by + rw [div_le_div_iff₀ (by linarith) (by linarith)] + nlinarith [hm, hh0] + have hlog := Real.log_le_log (div_pos (by linarith) (by linarith)) hmono + rw [Real.log_div (by linarith) (by linarith)] at hlog + linarith + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- A uniform positive lower bound on saving across the asset region, by compactness. -/ +theorem exists_policy_lower_bound_of_cost (hfl : assetFloor = 0) (z₁ : Z) {h κ : ℝ} (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hcost : ∀ R : ℝ, P.income z₁ ≤ R → P.u R - P.u (R - h) ≤ κ) + (hgain : κ < P.discount * (P.cont z₁ h - P.cont z₁ 0)) : + ∃ ε > 0, ∀ a ∈ Icc assetFloor assetCap, ε ≤ P.policy (a, z₁) := by + subst hfl + have hne : (Icc (0 : ℝ) assetCap).Nonempty := ⟨0, ⟨le_rfl, P.assetFloor_le_assetCap⟩⟩ + have hcont : ContinuousOn (fun a : ℝ => P.policy (a, z₁)) (Icc (0 : ℝ) assetCap) := + P.continuousOn_policy.comp (by fun_prop) fun a ha => ha + obtain ⟨a₀, ha₀, hmin⟩ := isCompact_Icc.exists_isMinOn hne hcont + exact ⟨P.policy (a₀, z₁), P.policy_pos_of_cost rfl z₁ hh0 hhcap hhinc hcost hgain ha₀, + fun a ha => isMinOn_iff.mp hmin a ha⟩ + +/-! ### Positive aggregate capital -/ + +/-- **Aggregate capital is strictly positive.** Households in income state `z₁` save at least +`ε`, the stationary distribution puts at least `p₀` of its mass there, and aggregate capital is +mean saving. -/ +theorem aggregateCapital_pos_of_cost (hfl : assetFloor = 0) {μ : ProbabilityMeasure P.State} + (hμ : P.IsStationary μ) {z₁ : Z} {p₀ : ℝ} (hp0 : 0 < p₀) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + {h κ : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hcost : ∀ R : ℝ, P.income z₁ ≤ R → P.u R - P.u (R - h) ≤ κ) + (hgain : κ < P.discount * (P.cont z₁ h - P.cont z₁ 0)) : + 0 < P.aggregateCapital μ := by + subst hfl + obtain ⟨ε, hε, hεle⟩ := + P.exists_policy_lower_bound_of_cost rfl z₁ hh0 hhcap hhinc hcost hgain + rw [P.aggregateCapital_eq_integral_policy hμ] + have hbound : ∀ s : P.State, ε * P.incomeIndicator z₁ s ≤ P.policyCoord s := by + intro s + by_cases hz : s.2 = z₁ + · have hmem : ((s.1 : ℝ)) ∈ Icc (0 : ℝ) assetCap := s.1.2 + have hge := hεle _ hmem + have hind : P.incomeIndicator z₁ s = 1 := by simp [hz] + have hpc : P.policyCoord s = P.policy ((s.1 : ℝ), s.2) := rfl + rw [hind, mul_one, hpc, hz] + exact hge + · have hind : P.incomeIndicator z₁ s = 0 := by simp [hz] + rw [hind, mul_zero] + exact (P.policy_mem_region (P.incl s)).1 + calc (0 : ℝ) < ε * p₀ := by positivity + _ ≤ ε * ∫ s, P.incomeIndicator z₁ s ∂(μ : Measure P.State) := + mul_le_mul_of_nonneg_left (P.le_integral_incomeIndicator hμ hp) hε.le + _ = ∫ s, ε * P.incomeIndicator z₁ s ∂(μ : Measure P.State) := (integral_const_mul _ _).symm + _ ≤ ∫ s, P.policyCoord s ∂(μ : Measure P.State) := + integral_mono (((P.incomeIndicator z₁).integrable _).const_mul ε) + (P.policyCoord.integrable _) hbound + +/-- The log instance of `aggregateCapital_pos_of_cost`. -/ +theorem aggregateCapital_pos (hfl : assetFloor = 0) (hu : P.u = Real.log) + {μ : ProbabilityMeasure P.State} + (hμ : P.IsStationary μ) {z₁ : Z} {p₀ : ℝ} (hp0 : 0 < p₀) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + {h : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hgain : Real.log (P.income z₁ / (P.income z₁ - h)) + < P.discount * (P.cont z₁ h - P.cont z₁ 0)) : + 0 < P.aggregateCapital μ := by + subst hfl + have hinc0 : 0 < P.income z₁ := lt_trans hh0 hhinc + refine P.aggregateCapital_pos_of_cost rfl hμ hp0 hp hh0 hhcap hhinc (fun R hm => ?_) hgain + rw [hu] + have hmono : R / (R - h) ≤ P.income z₁ / (P.income z₁ - h) := by + rw [div_le_div_iff₀ (by linarith) (by linarith)] + nlinarith [hm, hh0] + have hlog := Real.log_le_log (div_pos (by linarith) (by linarith)) hmono + rw [Real.log_div (by linarith) (by linarith)] at hlog + linarith + +/-! ### The gain, bounded by primitives + +`policy_pos_of_income` asks for a lower bound on `cont z₁ h - cont z₁ 0`, which is an object of +the solved model rather than a primitive. It is bounded below by the same deviation trick applied +one level down, and WHICH TERM OF THE SUM IS KEPT is the economics. + +Bounding by the worst state — every `income z'` replaced by `maxIncome` — gives, for small `h`, a +condition amounting to `β (1 + r) > 1`. That is useless here: the natural asset bound wants +`β (1 + r) < 1`, so the two would collide. Keeping the LOW-income term instead gives +`P z z₀ · log (1 + (1+r) h / income z₀)`, and with `income z₀` small that term is large; the +condition becomes roughly `β (1 + r) · P z z₀ > minIncome / maxIncome`, satisfiable under ordinary +impatience provided income is dispersed. + +That difference is the precautionary motive. The household saves not because it is patient but +because with probability `P z z₀` it lands where consumption is small and the marginal value of +assets is large. The motive enters through the DISPERSION of income rather than through convexity +of `u'`, which is why it is available without a third derivative. -/ + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem valueFunction_le_of_le (hpc : P.PositiveConsumption) {x y : ℝ} {z : Z} + (hx : x ∈ Icc assetFloor assetCap) + (hy : y ∈ Icc assetFloor assetCap) (hxy : x ≤ y) : + P.toExtended.valueFunction (x, z) ≤ P.toExtended.valueFunction (y, z) := by + rcases eq_or_lt_of_le hxy with rfl | hlt + · exact le_rfl + · exact (P.valueFunction_lt_of_lt hpc hx hy hlt).le + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem resources_zero (hfl : assetFloor = 0) (z : Z) : P.resources (0, z) = P.income z := by + subst hfl + simp only [resources]; norm_num + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The value of extra assets is at least the one-period utility gain**, got by carrying the +poorer household's own saving plan forward. -/ +theorem valueFunction_sub_ge (hpc : P.PositiveConsumption) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : x ≤ y) (z : Z) : + P.u (P.consumption (x, z) (P.policy (x, z)) + (1 + P.interest) * (y - x)) + - P.u (P.consumption (x, z) (P.policy (x, z))) + ≤ P.toExtended.valueFunction (y, z) - P.toExtended.valueFunction (x, z) := by + have hbm : P.policy (x, z) ∈ P.toExtended.feasible (x, z) := P.policy_mem _ + have hbm' : P.policy (x, z) ∈ P.toExtended.feasible (y, z) := P.feasible_mono hxy hbm + have hc0 : 0 < P.consumption (x, z) (P.policy (x, z)) := P.consumption_policy_pos hpc hx + have hres : P.resources (y, z) = P.resources (x, z) + (1 + P.interest) * (y - x) := by + simp only [resources, max_eq_right hx.1, max_eq_right hy.1]; ring + have hch : P.consumption (y, z) (P.policy (x, z)) + = P.consumption (x, z) (P.policy (x, z)) + (1 + P.interest) * (y - x) := by + simp only [consumption, hres]; ring + have hchpos : 0 < P.consumption (y, z) (P.policy (x, z)) := by + rw [hch] + have : 0 ≤ (1 + P.interest) * (y - x) := + mul_nonneg P.interest_gt_neg_one.le (by linarith) + linarith + have hopt := P.objR_le_of_mem hy hbm' (P.mem_dom_of_pos hchpos) + have e0 : P.objR (x, z) (P.policy (x, z)) = P.toExtended.valueFunction (x, z) := by + simp only [objR, cont]; exact (P.valueFunction_eq_policy hx).symm + have eh : P.objR (y, z) (P.policy (y, z)) = P.toExtended.valueFunction (y, z) := by + simp only [objR, cont]; exact (P.valueFunction_eq_policy hy).symm + simp only [objR, cont, hch] at hopt + simp only [objR, cont] at e0 eh + rw [← e0, ← eh] + linarith + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- The continuation rises with saving. -/ +theorem cont_le_of_le (hpc : P.PositiveConsumption) (z : Z) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : x ≤ y) : P.cont z x ≤ P.cont z y := by + simp only [cont] + refine Finset.sum_le_sum fun z' _ => ?_ + exact mul_le_mul_of_nonneg_left (P.valueFunction_le_of_le hpc hx hy hxy) + (P.transitionMatrix_nonneg _ _) + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The continuation's gain between two saving levels, in primitives.** -/ +theorem log_cont_sub_ge_gen (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + (hu : P.u = Real.log) (z z₀ : Z) + {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (hxy : x ≤ y) : + P.transitionMatrix z z₀ * Real.log (1 + (1 + P.interest) * (y - x) + / (P.income z₀ + (1 + P.interest) * x)) + ≤ P.cont z y - P.cont z x := by + subst hfl + have hRh : 0 ≤ (1 + P.interest) * (y - x) := + mul_nonneg P.interest_gt_neg_one.le (by linarith) + have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + have hterm : P.transitionMatrix z z₀ * + (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) + ≤ P.cont z y - P.cont z x := by + rw [hsum] + refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) + (fun z' _ => ?_) (Finset.mem_univ z₀) + exact mul_nonneg (P.transitionMatrix_nonneg _ _) + (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) + have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx + have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by + simp only [resources, max_eq_right hx.1] + have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) + ≤ P.income z₀ + (1 + P.interest) * x := by + simp only [consumption, hresx] + linarith [(P.policy_mem_region (x, z₀)).1] + have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ + rw [hu] at hgain + have hden : 0 < P.income z₀ + (1 + P.interest) * x := lt_of_lt_of_le hc0 hcle + have hfrac : 0 ≤ (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x) := + div_nonneg hRh hden.le + have hlog : Real.log (1 + (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x)) + ≤ Real.log (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) + - Real.log (P.consumption (x, z₀) (P.policy (x, z₀))) := by + rw [← Real.log_div (by linarith) hc0.ne'] + refine Real.log_le_log (by linarith) ?_ + rw [le_div_iff₀ hc0] + have hkey : (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x) + * P.consumption (x, z₀) (P.policy (x, z₀)) ≤ (1 + P.interest) * (y - x) := by + rw [div_mul_eq_mul_div, div_le_iff₀ hden] + nlinarith [hRh, hcle] + nlinarith [hkey] + exact le_trans (mul_le_mul_of_nonneg_left (le_trans hlog hgain) + (P.transitionMatrix_nonneg z z₀)) hterm + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **The continuation's gain from zero, in primitives.** Only the term for income state `z₀` is +kept; the rest are non-negative. For a LOW income `z₀` the logarithm is large, and that is the +precautionary motive. -/ +theorem log_cont_sub_ge (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + (hu : P.u = Real.log) (z z₀ : Z) {h : ℝ} + (hh : h ∈ Icc assetFloor assetCap) : + P.transitionMatrix z z₀ * Real.log (1 + (1 + P.interest) * h / P.income z₀) + ≤ P.cont z h - P.cont z 0 := by + subst hfl + have h0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + have := P.log_cont_sub_ge_gen rfl hpc hu z z₀ h0 hh hh.1 + simpa using this + +/-- **Aggregate capital is strictly positive, from primitives alone.** Every hypothesis is stated +in `β`, `r`, the income levels and the transition matrix. -/ +theorem aggregateCapital_pos_of_primitives (hfl : assetFloor = 0) + (hpc : P.PositiveConsumption) (hu : P.u = Real.log) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ : ℝ} (hp0 : 0 < p₀) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + {h : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hgain : Real.log (P.income z₁ / (P.income z₁ - h)) + < P.discount * (P.transitionMatrix z₁ z₀ + * Real.log (1 + (1 + P.interest) * h / P.income z₀))) : + 0 < P.aggregateCapital μ := by + subst hfl + refine P.aggregateCapital_pos rfl hu hμ hp0 hp hh0 hhcap hhinc (lt_of_lt_of_le hgain ?_) + exact mul_le_mul_of_nonneg_left (P.log_cont_sub_ge rfl hpc hu z₁ z₀ ⟨hh0.le, hhcap⟩) + P.discount.coe_nonneg + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- **A quantitative lower bound on saving.** A household with enough income saves at least +`h/2`, not merely something positive — which is what a sign change needs, since the rate at +which demand falls below supply has to be chosen from a number. -/ +theorem policy_ge_of_cost (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) (z₁ : Z) + {h κ : ℝ} (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hcost : ∀ R b : ℝ, P.income z₁ ≤ R → 0 ≤ b → b ≤ h → P.u (R - b) - P.u (R - h) ≤ κ) + (hgain : κ < P.discount * (P.cont z₁ h - P.cont z₁ (h / 2))) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + h / 2 ≤ P.policy (a, z₁) := by + subst hfl + by_contra hcon + rw [not_le] at hcon + have hinc0 : 0 < P.income z₁ := lt_trans hh0 hhinc + have hm : P.income z₁ ≤ P.resources (a, z₁) := by + simp only [resources, max_eq_right ha.1] + nlinarith [P.interest_gt_neg_one, ha.1] + have hmh : h < P.resources (a, z₁) := lt_of_lt_of_le hhinc hm + have hfeas : h ∈ P.toExtended.feasible (a, z₁) := by + rw [P.feasible_eq, P.maxSaving_eq] + exact ⟨hh0.le, le_min hhcap hmh.le⟩ + have hch : 0 < P.consumption (a, z₁) h := by simp only [consumption]; linarith + have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hch) + have hbreg : P.policy (a, z₁) ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ + have hhalfreg : h / 2 ∈ Icc (0 : ℝ) assetCap := ⟨by linarith, by linarith⟩ + have hmono : P.cont z₁ (P.policy (a, z₁)) ≤ P.cont z₁ (h / 2) := + P.cont_le_of_le hpc z₁ hbreg hhalfreg hcon.le + have hcostle := hcost (P.resources (a, z₁)) (P.policy (a, z₁)) hm hbreg.1 (by linarith) + simp only [objR, consumption] at hopt + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hmono hβ + have hdist : (P.discount : ℝ) * (P.cont z₁ h - P.cont z₁ (h / 2)) + = (P.discount : ℝ) * P.cont z₁ h - (P.discount : ℝ) * P.cont z₁ (h / 2) := by ring + linarith [hopt, hcostle, hgain, hscaled, hdist.le, hdist.ge] + +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- The log instance of `policy_ge_of_cost`. -/ +theorem policy_ge_of_gain (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + (hu : P.u = Real.log) (z₁ : Z) + {h : ℝ} (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hgain : Real.log (P.income z₁ / (P.income z₁ - h)) + < P.discount * (P.cont z₁ h - P.cont z₁ (h / 2))) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + h / 2 ≤ P.policy (a, z₁) := by + subst hfl + have hinc0 : 0 < P.income z₁ := lt_trans hh0 hhinc + refine P.policy_ge_of_cost rfl hpc z₁ hh0 hhcap hhinc (fun R b hm hb0 hbh => ?_) hgain ha + rw [hu] + have hratio : (R - b) / (R - h) ≤ P.income z₁ / (P.income z₁ - h) := by + rw [div_le_div_iff₀ (by linarith) (by linarith)] + nlinarith [hm, hh0, hb0, hhinc] + rw [← Real.log_div (by linarith) (by linarith)] + exact Real.log_le_log (div_pos (by linarith) (by linarith)) hratio + + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean new file mode 100644 index 000000000..18843c9f4 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean @@ -0,0 +1,451 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Equilibrium.Firm +public import LeanPool.AiyagariEquilibrium.Equilibrium.PositiveCapital +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption +public import LeanPool.AiyagariEquilibrium.Models.MinimalMPC + + +/-! +# The sign change, both ends + +`Equilibrium.Firm` proved the LOW end of the sign change — capital demand diverges as the rate +approaches `-δ`, while capital supply can never exceed the asset cap — and recorded that the HIGH +end was open, needing `capitalDemand δ rhi ≤ S rhi` and hence `S rhi > 0`, which it called +"obvious economically and unproved here". + +That is now proved. `PositiveCapital` gives strictly positive capital supply, and what remains is +to make the bound QUANTITATIVE: the sign change needs a number, not merely positivity, because +`rhi` has to be chosen from it. `le_aggregateCapital` supplies the number from a uniform lower +bound on saving, and `exists_capitalDemand_le` is the mirror of `exists_capitalDemand_ge` — +Cobb–Douglas demand vanishes at high rates, so any positive supply floor is eventually met. + +`exists_sign_change` then brackets the equilibrium from two-sided bounds on capital supply, and +`exists_equilibrium_of_bounds` hands the bracket to `exists_aiyagari_equilibrium`. + +## What is still assumed, and it is no longer the demand side + +The bounds `m ≤ S r ≤ M` are required at every rate. The upper one is free, from +`aggregateCapital_le`. +The lower one is the live hypothesis: `PositiveCapital` proves it at a rate, and making it uniform +over an interval wide enough for the curves to cross is what the crude constants make hard — the +decline condition alone needs `ε > r/(1+r)`, and with `ε ≈ 0.27` that caps the interval near +`r = 0.37`. So the remaining obstruction is a QUANTITATIVE one about the household side, not a +structural gap in the equilibrium argument. +-/ + +@[expose] public section + +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +/-- **Capital demand vanishes at high rates.** The mirror of `exists_capitalDemand_ge`, and what +makes the high end of the sign change a theorem. -/ +theorem exists_capitalDemand_le (A δ : ℝ) {m : ℝ} (hm : 0 < m) (r₀ : ℝ) : + ∃ rhi : ℝ, r₀ ≤ rhi ∧ 0 < rhi + δ ∧ capitalDemand A δ rhi ≤ m := by + have hsq : 0 < Real.sqrt m := Real.sqrt_pos.mpr hm + set t : ℝ := (|A| + 1) / (2 * Real.sqrt m) with ht + have ht0 : 0 < t := by rw [ht]; positivity + refine ⟨max r₀ (t - δ), le_max_left _ _, ?_, ?_⟩ + · have := le_max_right r₀ (t - δ) + linarith + · have hge : t ≤ max r₀ (t - δ) + δ := by + have := le_max_right r₀ (t - δ); linarith + have hpos : 0 < max r₀ (t - δ) + δ := lt_of_lt_of_le ht0 hge + have hts : t ^ 2 * (4 * m) = (|A| + 1) ^ 2 := by + rw [ht] + field_simp + rw [Real.sq_sqrt hm.le] + ring + have hsqle : t ^ 2 ≤ (max r₀ (t - δ) + δ) ^ 2 := by nlinarith [hge, ht0.le] + have hA : A ^ 2 ≤ (|A| + 1) ^ 2 := by nlinarith [abs_nonneg A, sq_abs A] + simp only [capitalDemand] + rw [div_le_iff₀ (by positivity)] + nlinarith [hsqle, hm, hts, hA] + +/-- **The sign change, from two-sided bounds on capital supply.** -/ +theorem exists_sign_change (δ : ℝ) {S : ℝ → ℝ} {m M : ℝ} (hm : 0 < m) + (hlb : ∀ r, m ≤ S r) (hub : ∀ r, S r ≤ M) : + ∃ rlo rhi : ℝ, 0 < rlo + δ ∧ rlo ≤ rhi ∧ S rlo ≤ capitalDemand 1 δ rlo + ∧ capitalDemand 1 δ rhi ≤ S rhi := by + have hM : 0 ≤ M := le_trans hm.le (le_trans (hlb 0) (hub 0)) + obtain ⟨rlo, hrlo, hD⟩ := exists_capitalDemand_ge δ hM + obtain ⟨rhi, hge, _, hD'⟩ := exists_capitalDemand_le 1 δ hm rlo + exact ⟨rlo, rhi, hrlo, hge, le_trans (hub rlo) hD, le_trans hD' (hlb rhi)⟩ + +/-- **The firm can be calibrated to the households**, rather than the other way round. Given any +rate interval and any two-sided bound on capital supply, some productivity and depreciation put +capital demand above supply at the low end and below it at the high end. + +This is what the productivity parameter buys. With `A` fixed at one the demand schedule carries a +scale of its own, and the household economy has to be stretched — in practice to absurd +depreciation rates — for the curves to meet. Here `A` sets the scale and `δ` the curvature, and +the construction is explicit: `η = d √m / (√M + √m)` with `d = rhi - rlo`, then `δ = η - rlo` and +`A = 2 η √M`, at which the low end holds with EQUALITY. -/ +theorem exists_firm_of_bounds {rlo rhi m M : ℝ} (hlt : rlo < rhi) (hm : 0 < m) (hmM : m ≤ M) : + ∃ A δ : ℝ, 0 < A ∧ 0 < rlo + δ ∧ M ≤ capitalDemand A δ rlo ∧ capitalDemand A δ rhi ≤ m := by + have hM : 0 < M := lt_of_lt_of_le hm hmM + set sm : ℝ := Real.sqrt m with hsmdef + set sM : ℝ := Real.sqrt M with hsMdef + have hsm0 : 0 < sm := Real.sqrt_pos.mpr hm + have hsM0 : 0 < sM := Real.sqrt_pos.mpr hM + have hsm2 : sm ^ 2 = m := Real.sq_sqrt hm.le + have hsM2 : sM ^ 2 = M := Real.sq_sqrt hM.le + set d : ℝ := rhi - rlo with hddef + have hd0 : 0 < d := by rw [hddef]; linarith + set η : ℝ := d * sm / (sM + sm) with hηdef + have hη0 : 0 < η := by rw [hηdef]; positivity + have hηkey : η * (sM + sm) = d * sm := by rw [hηdef]; field_simp + refine ⟨2 * η * sM, η - rlo, by positivity, by linarith, ?_, ?_⟩ + · have hr : rlo + (η - rlo) = η := by ring + simp only [capitalDemand, hr] + rw [le_div_iff₀ (by positivity)] + nlinarith [hsM2, hη0] + · have hr : rhi + (η - rlo) = d + η := by rw [hddef]; ring + have hkey : η * sM ≤ sm * (d + η) := by nlinarith [hηkey, hη0, hsm0] + have hsq : (η * sM) ^ 2 ≤ (sm * (d + η)) ^ 2 := by + nlinarith [hkey, mul_pos hη0 hsM0, mul_pos hsm0 (by linarith : (0 : ℝ) < d + η)] + simp only [capitalDemand, hr] + rw [div_le_iff₀ (by positivity)] + nlinarith [hsq, hsM2, hsm2] + + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable [MeasurableSpace Z] [BorelSpace Z] +-- Stated at a zero borrowing limit: the quantitative saving bounds and Light's rescaling +-- that these results rest on are proved there. +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-- **A quantitative lower bound on capital supply.** Households in income state `z₁` save at +least `ε`, the stationary distribution puts at least `p₀` of its mass there, and capital is mean +saving — so capital is at least `ε p₀`. Positivity alone would not do: the sign change needs a +number from which to choose `rhi`. -/ +theorem le_aggregateCapital {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) + {z₁ : Z} {p₀ ε : ℝ} (hε : 0 ≤ ε) (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + (hpol : ∀ a ∈ Icc (0 : ℝ) assetCap, ε ≤ P.policy (a, z₁)) : + ε * p₀ ≤ P.aggregateCapital μ := by + rw [P.aggregateCapital_eq_integral_policy hμ] + have hbound : ∀ s : P.State, ε * P.incomeIndicator z₁ s ≤ P.policyCoord s := by + intro s + by_cases hz : s.2 = z₁ + · have hge := hpol _ (s.1.2 : ((s.1 : ℝ)) ∈ Icc (0 : ℝ) assetCap) + have hind : P.incomeIndicator z₁ s = 1 := by simp [hz] + have hpc : P.policyCoord s = P.policy ((s.1 : ℝ), s.2) := rfl + rw [hind, mul_one, hpc, hz] + exact hge + · have hind : P.incomeIndicator z₁ s = 0 := by simp [hz] + rw [hind, mul_zero] + exact (P.policy_mem_region (P.incl s)).1 + calc ε * p₀ ≤ ε * ∫ s, P.incomeIndicator z₁ s ∂(μ : Measure P.State) := + mul_le_mul_of_nonneg_left (P.le_integral_incomeIndicator hμ hp) hε + _ = ∫ s, ε * P.incomeIndicator z₁ s ∂(μ : Measure P.State) := (integral_const_mul _ _).symm + _ ≤ ∫ s, P.policyCoord s ∂(μ : Measure P.State) := + integral_mono (((P.incomeIndicator z₁).integrable _).const_mul ε) + (P.policyCoord.integrable _) hbound + +/-- **The supply floor, in primitives.** Combining the quantitative saving bound with the +primitive bound on the continuation's gain: every stationary distribution carries at least +`h/2 · p₀` of capital. -/ +theorem le_aggregateCapital_of_gain (hpc : P.PositiveConsumption) (hu : P.u = Real.log) + {μ : ProbabilityMeasure P.State} + (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ h : ℝ} (hh0 : 0 < h) + (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + (hgain : Real.log (P.income z₁ / (P.income z₁ - h)) + < P.discount * (P.transitionMatrix z₁ z₀ * Real.log (1 + (1 + P.interest) * (h / 2) + / (P.income z₀ + (1 + P.interest) * (h / 2))))) : + h / 2 * p₀ ≤ P.aggregateCapital μ := by + refine P.le_aggregateCapital hμ (by linarith) hp fun a ha => ?_ + refine P.policy_ge_of_gain rfl hpc hu z₁ hh0 hhcap hhinc ?_ ha + refine lt_of_lt_of_le hgain (mul_le_mul_of_nonneg_left ?_ P.discount.coe_nonneg) + have hhalf : h / 2 ∈ Icc (0 : ℝ) assetCap := ⟨by linarith, by linarith⟩ + have hfull : h ∈ Icc (0 : ℝ) assetCap := ⟨hh0.le, hhcap⟩ + have hgen := P.log_cont_sub_ge_gen rfl hpc hu z₁ z₀ hhalf hfull (by linarith) + rw [show h - h / 2 = h / 2 from by ring] at hgen + exact hgen + +/-! ### The equilibrium, from uniqueness and a floor + +Everything the equilibrium theorem wants is now available on the household side: uniqueness of the +stationary distribution at each rate (from the uniform corner and decline conditions) and a +positive floor under capital supply (from the gain condition, checked at the low end). This +assembles them. + +The selection `ν` is built by choosing the unique stationary distribution at the CLAMPED rate, so +it is defined at every real number while agreeing with the intended one on the interval — the same +device `rateFamily` uses, and for the same reason. Uniqueness then gives both halves of what +`exists_equilibrium_of_selection` asks: that `ν r` is stationary, and that nothing else is. + +`exists_equilibrium_of_technology` is the primitive statement: the technology `(A, δ)` is GIVEN, +and two inequalities on it — enough demand at `rlo` to clear the asset cap, little enough at `rhi` +to fall under the supply floor — deliver the equilibrium. +`exists_equilibrium_of_uniqueness_and_floor` is then the corollary in which the firm is chosen +last, by `exists_firm_of_bounds`, from the supply floor `m` and the asset cap. +-/ + +omit [MeasurableSpace Z] [BorelSpace Z] in +theorem withRate_congr {a b : ℝ} (hab : a = b) (h₁ : P.RateOK a) (h₂ : P.RateOK b) : + P.withRate a h₁ = P.withRate b h₂ := by + subst hab; rfl + +/-- **The same for any continuous demand schedule.** The ceiling at `rlo` and the floor at +`rhi` are compared with `D` there; the selection of stationary distributions supplies the +continuity of supply in between. -/ +theorem exists_equilibrium_of_ceiling_and_floor_of_demand {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) + (hlt : rlo < rhi) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, + ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ) + {m M : ℝ} + (hceil : ∀ hrlo' : P.RateOK rlo, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rlo hrlo').IsStationary μ → P.aggregateCapital μ ≤ M) + (hfloor : ∀ hrhi : P.RateOK rhi, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rhi hrhi).IsStationary μ → m ≤ P.aggregateCapital μ) + (D : ℝ → ℝ) (hD : ContinuousOn D (Icc rlo rhi)) (hDlo : M ≤ D rlo) (hDhi : D rhi ≤ m) : + ∃ r ∈ Icc rlo rhi, IsAiyagariEquilibrium (P.rateFamily hrlo hlt.le) D r := by + classical + have hle : rlo ≤ rhi := hlt.le + have hclamp : ∀ r : ℝ, clampRate rlo rhi r ∈ Icc rlo rhi := clampRate_mem hle + have hpos : ∀ r : ℝ, P.RateOK (clampRate rlo rhi r) := + fun r => rateOK_of_floor_zero (one_add_clampRate_pos hrlo hle r) + have hE : ∀ r : ℝ, ∃! μ : ProbabilityMeasure P.State, + (P.withRate (clampRate rlo rhi r) (hpos r)).IsStationary μ := + fun r => huniq _ (hclamp r) (hpos r) + set ν : ℝ → ProbabilityMeasure P.State := fun r => (hE r).choose with hνdef + have hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r) := by + intro r hr hrr + have hc : clampRate rlo rhi r = r := clampRate_eq hr + rw [← P.withRate_congr hc (hpos r) hrr] + exact (hE r).choose_spec.1 + have hunique : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, + (P.withRate r hrr).IsStationary μ → μ = ν r := by + intro r hr hrr μ hμ + have hc : clampRate rlo rhi r = r := clampRate_eq hr + refine (hE r).choose_spec.2 μ ?_ + rw [P.withRate_congr hc (hpos r) hrr] + exact hμ + have hlo' : rlo ∈ Icc rlo rhi := ⟨le_rfl, hle⟩ + have hhi' : rhi ∈ Icc rlo rhi := ⟨hle, le_rfl⟩ + have hrlo' : P.RateOK rlo := rateOK_of_floor_zero hrlo + have hrhi : P.RateOK rhi := rateOK_of_floor_zero (by linarith) + have hub : P.aggregateCapital (ν rlo) ≤ M := hceil hrlo' _ (hν rlo hlo' hrlo') + have hlbhi : m ≤ P.aggregateCapital (ν rhi) := hfloor hrhi _ (hν rhi hhi' hrhi) + exact P.exists_equilibrium_of_selection hrlo hle ν hν hunique D hD + (le_trans hub hDlo) (le_trans hDhi hlbhi) + +/-- **An Aiyagari equilibrium with the technology GIVEN.** The firm is no longer chosen to fit +the household: any `(A, δ)` whose demand clears the asset cap at the bottom of the interval and +falls below the supply floor at the top produces an equilibrium inside it. + +The two hypotheses are inequalities on the technology and nothing else — `le_capitalDemand_of_sq` +and `capitalDemand_le_of_sq` turn them into `4(rlo+δ)²·assetCap ≤ A²` and +`A² ≤ 4(rhi+δ)²·m`, which is the band `2(rlo+δ)√assetCap ≤ A ≤ 2(rhi+δ)√m`. Such an `A` exists +exactly when `(rlo+δ)√assetCap ≤ (rhi+δ)√m`, so the band is non-empty for small `δ` whenever the +household has a positive supply floor: that is the content of +`exists_equilibrium_of_uniqueness_and_floor`, which is now a corollary. + +Note which bound is used where. The ceiling is the free one, `aggregateCapital_le`; only the floor +`m` is household work. -/ +theorem exists_equilibrium_of_ceiling_and_floor {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) + (hlt : rlo < rhi) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, + ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ) + {m M : ℝ} + (hceil : ∀ hrlo' : P.RateOK rlo, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rlo hrlo').IsStationary μ → P.aggregateCapital μ ≤ M) + (hfloor : ∀ hrhi : P.RateOK rhi, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rhi hrhi).IsStationary μ → m ≤ P.aggregateCapital μ) + (A δ : ℝ) (hδ : 0 < rlo + δ) + (hDlo : M ≤ capitalDemand A δ rlo) (hDhi : capitalDemand A δ rhi ≤ m) : + ∃ r ∈ Icc rlo rhi, + IsAiyagariEquilibrium (P.rateFamily hrlo hlt.le) (capitalDemand A δ) r := + P.exists_equilibrium_of_ceiling_and_floor_of_demand hrlo hlt huniq hceil hfloor + (capitalDemand A δ) (continuousOn_capitalDemand hδ) hDlo hDhi + +/-- **The same with the free ceiling.** Capital supply can never exceed the asset cap, so a +technology that clears the cap at `rlo` needs no household-side ceiling at all. This is the form +every witness used before `crra_aggregateCapital_le_of_minMPC` gave a sharper one. -/ +theorem exists_equilibrium_of_technology {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) (hlt : rlo < rhi) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, + ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ) + {m : ℝ} + (hfloor : ∀ hrhi : P.RateOK rhi, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rhi hrhi).IsStationary μ → m ≤ P.aggregateCapital μ) + (A δ : ℝ) (hδ : 0 < rlo + δ) + (hDlo : assetCap ≤ capitalDemand A δ rlo) (hDhi : capitalDemand A δ rhi ≤ m) : + ∃ r ∈ Icc rlo rhi, + IsAiyagariEquilibrium (P.rateFamily hrlo hlt.le) (capitalDemand A δ) r := + P.exists_equilibrium_of_ceiling_and_floor hrlo hlt huniq + (fun _ μ _ => P.aggregateCapital_le μ) hfloor A δ hδ hDlo hDhi + + +/-- **An Aiyagari equilibrium from household-side hypotheses alone.** Uniqueness of the stationary +distribution at each rate, and a positive floor under capital supply AT THE TOP OF THE INTERVAL, +suffice: the firm is then calibrated to meet them. + +The floor is needed only at `rhi`, which matters: `gain_term_mono` says the gain condition is +easier at higher rates, so the floor may be taken at the best rate rather than the worst. -/ +theorem exists_equilibrium_of_uniqueness_and_floor {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) + (hlt : rlo < rhi) + (huniq : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, + ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ) + {m : ℝ} (hm : 0 < m) + (hfloor : ∀ hrhi : P.RateOK rhi, ∀ μ : ProbabilityMeasure P.State, + (P.withRate rhi hrhi).IsStationary μ → m ≤ P.aggregateCapital μ) : + ∃ A δ : ℝ, 0 < A ∧ 0 < rlo + δ ∧ ∃ r ∈ Icc rlo rhi, + IsAiyagariEquilibrium (P.rateFamily hrlo hlt.le) (capitalDemand A δ) r := by + classical + have hle : rlo ≤ rhi := hlt.le + have hrhi : P.RateOK rhi := rateOK_of_floor_zero (by linarith) + -- the household side supplies the two numbers the firm has to be squeezed between + obtain ⟨μ₀, hμ₀⟩ := (huniq rhi ⟨hle, le_rfl⟩ hrhi).exists + have hmM : m ≤ assetCap := + le_trans (hfloor hrhi μ₀ hμ₀) (P.aggregateCapital_le _) + obtain ⟨A, δ, hA, hrδ, hDlo, hDhi⟩ := exists_firm_of_bounds hlt hm hmM + exact ⟨A, δ, hA, hrδ, + P.exists_equilibrium_of_technology hrlo hlt huniq hfloor A δ hrδ hDlo hDhi⟩ + + +/-- **Capital supply is bounded above by the impatience of the household.** Every household saves +at most `1 - ε` of its resources, capital is mean saving, and resources are income plus the return +on capital — so the bound closes on itself. + +This is the quantitative form of "assets are small when the household is impatient", and it blows +up exactly as `(1-ε)(1+r) → 1`, which is where the household stops running its assets down. -/ +theorem aggregateCapital_le_of_consumption_bound {μ : ProbabilityMeasure P.State} + (hμ : P.IsStationary μ) {ε : ℝ} (hε1 : ε ≤ 1) + (hlt : (1 - ε) * (1 + P.interest) < 1) + (hlb : ∀ (z : Z), ∀ a ∈ Icc (0 : ℝ) assetCap, ε * P.resources (a, z) ≤ P.consumptionFn z a) : + P.aggregateCapital μ ≤ (1 - ε) * P.maxIncome / (1 - (1 - ε) * (1 + P.interest)) := by + have hε0 : (0 : ℝ) ≤ 1 - ε := by linarith + have hr : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + set K : ℝ := P.aggregateCapital μ with hKdef + have hK : K = ∫ s, P.policyCoord s ∂(μ : Measure P.State) := + P.aggregateCapital_eq_integral_policy hμ + have hA : ∫ s, P.assetCoord s ∂(μ : Measure P.State) = K := rfl + have hbound : ∀ s : P.State, P.policyCoord s + ≤ (1 - ε) * P.maxIncome + (1 - ε) * (1 + P.interest) * P.assetCoord s := by + intro s + have hmem : ((s.1 : ℝ)) ∈ Icc (0 : ℝ) assetCap := s.1.2 + have hlbs := hlb s.2 _ hmem + have hres : P.resources ((s.1 : ℝ), s.2) = P.income s.2 + (1 + P.interest) * (s.1 : ℝ) := by + simp only [resources, max_eq_right hmem.1] + have hpol : P.policyCoord s + = P.resources ((s.1 : ℝ), s.2) - P.consumptionFn s.2 (s.1 : ℝ) := by + simp only [policyCoord_apply, incl, consumptionFn, consumption]; ring + have hinc : P.income s.2 ≤ P.maxIncome := P.le_maxIncome _ + have hacoord : P.assetCoord s = (s.1 : ℝ) := rfl + rw [hpol, hres, hacoord] + rw [hres] at hlbs + nlinarith [hlbs, hinc, hε0, hr, hmem.1] + have hsplit : ∫ s, ((1 - ε) * P.maxIncome + (1 - ε) * (1 + P.interest) * P.assetCoord s) + ∂(μ : Measure P.State) + = (1 - ε) * P.maxIncome + (1 - ε) * (1 + P.interest) * K := by + have hprob : IsProbabilityMeasure (μ : Measure P.State) := μ.2 + rw [integral_add (integrable_const _) ((P.assetCoord.integrable _).const_mul _)] + have h1 : ∫ _s : P.State, (1 - ε) * P.maxIncome ∂(μ : Measure P.State) + = (1 - ε) * P.maxIncome := by simp + have h2 : ∫ s, (1 - ε) * (1 + P.interest) * P.assetCoord s ∂(μ : Measure P.State) + = (1 - ε) * (1 + P.interest) * K := by rw [integral_const_mul, hA] + rw [h1, h2] + have hint : ∫ s, P.policyCoord s ∂(μ : Measure P.State) + ≤ (1 - ε) * P.maxIncome + (1 - ε) * (1 + P.interest) * K := by + rw [← hsplit] + exact integral_mono (P.policyCoord.integrable _) + ((integrable_const _).add ((P.assetCoord.integrable _).const_mul _)) hbound + rw [← hK] at hint + rw [le_div_iff₀ (by linarith)] + nlinarith [hint] + +/-- **A quantitative ceiling on capital supply, from primitives.** The minimal-MPC bound says the +household consumes at least the share `κ` of cash on hand; feeding that to +`aggregateCapital_le_of_consumption_bound` replaces the asset cap, which is bookkeeping, by a +number the calibration determines. The denominator is `1 - Þ`, so the ceiling blows up exactly as +the household stops being impatient. -/ +theorem crra_aggregateCapital_le_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcapit : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) : + P.aggregateCapital μ + ≤ (1 - P.minMPC γ) * P.maxIncome / (1 - (1 - P.minMPC γ) * (1 + P.interest)) := by + refine P.aggregateCapital_le_of_consumption_bound hμ + (IncomeFluctuation.minMPC_le_one hγ0 hβ) ?_ ?_ + · rw [IncomeFluctuation.one_sub_minMPC_mul] + exact IncomeFluctuation.patience_lt_one hγ0 hβR + · exact fun z a ha => P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hβR hpos hcapit z ha + +/-- **The ceiling for shifted CRRA.** The subsistence level enters through `minMPCShare`, which +charges the constant in `κ(η+m) ≤ η+c` against the smallest cash on hand there can be. At `η = 0` +it is the CRRA statement. -/ +theorem hara_aggregateCapital_le_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hshare : (1 - P.minMPCShare γ η) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcapit : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) : + P.aggregateCapital μ + ≤ (1 - P.minMPCShare γ η) * P.maxIncome + / (1 - (1 - P.minMPCShare γ η) * (1 + P.interest)) := by + refine P.aggregateCapital_le_of_consumption_bound hμ ?_ hshare ?_ + · have hκ : P.minMPC γ ≤ 1 := IncomeFluctuation.minMPC_le_one hγ0 hβ + have hy : 0 < P.minIncome := P.minIncome_pos + have : (0 : ℝ) ≤ (1 - P.minMPC γ) * η / P.minIncome := by + apply div_nonneg (mul_nonneg (by linarith) hη) hy.le + simp only [IncomeFluctuation.minMPCShare] + linarith + · exact fun z a ha => + P.hara_minMPCShare_mul_le_consumptionFn hγ0 hη hu rfl hκ hβ hβR hpos hcapit z ha + +end IncomeFluctuation + +/-! ### Uniformity in the interest rate + +The supply floor has to hold at every rate in the interval, and the gain condition is MONOTONE in +the rate: raising `r` raises `(1+r) t / (y₀ + (1+r) t)` towards one, so the right-hand side grows +while the left does not move. Checking the condition at the LOW end of the interval therefore +covers all of it. -/ + + +theorem gain_term_mono {y₀ t : ℝ} (hy₀ : 0 < y₀) (ht : 0 < t) {s₀ s₁ : ℝ} + (hs₀ : 0 < s₀) (hs : s₀ ≤ s₁) : + Real.log (1 + s₀ * t / (y₀ + s₀ * t)) ≤ Real.log (1 + s₁ * t / (y₀ + s₁ * t)) := by + have h0 : 0 < y₀ + s₀ * t := by positivity + have h1 : 0 < y₀ + s₁ * t := by nlinarith + refine Real.log_le_log (by positivity) ?_ + have hfrac : s₀ * t / (y₀ + s₀ * t) ≤ s₁ * t / (y₀ + s₁ * t) := by + rw [div_le_div_iff₀ h0 h1] + nlinarith [mul_le_mul_of_nonneg_right hs (mul_nonneg ht.le hy₀.le)] + linarith + + +/-- **An Aiyagari equilibrium with the Cobb–Douglas firm**, from two-sided bounds on capital +supply. Both ends of the sign change are now theorems; what is assumed is a positive floor under +capital supply, uniform in the rate. -/ +theorem exists_equilibrium_of_bounds {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] + [DiscreteTopology Z] [MeasurableSpace Z] [BorelSpace Z] {assetFloor assetCap : ℝ} + {Pf : ℝ → IncomeFluctuation Z assetFloor assetCap} {S : ℝ → ℝ} (δ : ℝ) {m M : ℝ} (hm : 0 < m) + (hlb : ∀ r, m ≤ S r) (hub : ∀ r, S r ≤ M) + (hS : ∀ r, ∃ μ : ProbabilityMeasure (Pf r).State, + (Pf r).IsStationary μ ∧ (Pf r).aggregateCapital μ = S r) + (hScont : ∀ rlo rhi : ℝ, ContinuousOn S (Icc rlo rhi)) : + ∃ rlo rhi : ℝ, rlo ≤ rhi ∧ ∃ r ∈ Icc rlo rhi, + IsAiyagariEquilibrium Pf (capitalDemand 1 δ) r := by + obtain ⟨rlo, rhi, hrlo, hle, hloS, hhiS⟩ := exists_sign_change δ hm hlb hub + refine ⟨rlo, rhi, hle, exists_aiyagari_equilibrium hle hS (hScont rlo rhi) + (continuousOn_capitalDemand hrlo) hloS hhiS⟩ + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean new file mode 100644 index 000000000..094fe5a84 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean @@ -0,0 +1,114 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Equilibrium.Firm + + +/-! +# Uniqueness of the equilibrium interest rate + +Existence came from the intermediate value theorem. Uniqueness comes from single crossing: +capital demand falls in the interest rate, so if capital supply RISES in it the two curves +meet at most once. This file proves that skeleton and supplies the demand half. + +## What the literature says, and why only the skeleton is here + +The supply half is the whole difficulty, and it is not a theorem about the model — it is a +restriction on the calibration. + +Light (2018) proves uniqueness for CRRA utility and Cobb–Douglas production when relative +risk aversion `γ ≤ 1`. The argument is exactly the one below: individual savings rise with +the interest rate (his Theorem 1), hence aggregate savings do (Theorem 2), and since the +capital-to-wage ratio `k(R)/w(R)` strictly falls, the curves cross once (Theorem 3). The +economics of `γ ≤ 1` is that the substitution effect dominates the income effect: a higher +rate raises the price of current consumption, but also makes existing wealth worth more, and +only when `γ ≤ 1` does the first dominate. + +For `γ > 1` supply need not rise. Açıkgöz (2018) exhibits multiple stationary equilibria at +`γ = 6.5`. Walsh and Young (2025) search 24,000 parameter draws and find multiplicity in 250 +of them — always exactly three equilibria — and report that they never see it with risk +aversion below about 1.49, depreciation below about 0.19, or income persistence below about +0.47, and only ever with a disaster state for income. Their mechanism is precautionary: with +a disaster state, persistent income and high risk aversion, agents facing a low return still +amass capital to insure against near-zero income, so capital supply bends back and can cross +downward-sloping demand more than once. + +Kirkby (2018) approaches it computationally and finds both Aiyagari (1994) and Pijoan-Mas +(2006) "almost certainly have a unique general equilibrium". + +So the honest formal target is CONDITIONAL uniqueness — uniqueness given monotone supply — +plus, separately, whatever route establishes monotone supply for a restricted class. That is +what is proved here. An unconditional uniqueness theorem for this model would be false. +-/ + +@[expose] public section + +open Set + +namespace LeanEconomics + +/-- **Single crossing.** A non-decreasing supply curve meets a strictly decreasing demand +curve at most once. This is the skeleton of Light (2018) Theorem 3. -/ +theorem eq_of_monotoneOn_of_strictAntiOn {S D : ℝ → ℝ} {s : Set ℝ} + (hS : MonotoneOn S s) (hD : StrictAntiOn D s) {r₁ r₂ : ℝ} (h₁ : r₁ ∈ s) (h₂ : r₂ ∈ s) + (he₁ : S r₁ = D r₁) (he₂ : S r₂ = D r₂) : r₁ = r₂ := by + by_contra hne + rcases lt_or_gt_of_ne hne with h | h + · have hs := hS h₁ h₂ h.le + have hd := hD h₁ h₂ h + rw [he₁, he₂] at hs + exact absurd hs (not_le.mpr hd) + · have hs := hS h₂ h₁ h.le + have hd := hD h₂ h₁ h + rw [he₁, he₂] at hs + exact absurd hs (not_le.mpr hd) + +/-- Capital demand falls STRICTLY in the interest rate, which is the half of single crossing +that is a theorem about the technology rather than about the households. -/ +theorem capitalDemand_strictAntiOn {A δ rlo rhi : ℝ} (hA : A ≠ 0) (h : 0 < rlo + δ) : + StrictAntiOn (capitalDemand A δ) (Icc rlo rhi) := by + intro a ha b hb hab + have ha' : 0 < a + δ := lt_of_lt_of_le h (by linarith [ha.1]) + have hb' : 0 < b + δ := by linarith + have hA2 : 0 < A ^ 2 := by positivity + simp only [capitalDemand] + rw [div_lt_div_iff₀ (by positivity) (by positivity)] + have hsq : (a + δ) ^ 2 < (b + δ) ^ 2 := by nlinarith [ha', hb'] + nlinarith [hA2, hsq] + +/-- **Conditional uniqueness of the equilibrium interest rate.** Given that capital supply is +non-decreasing in the rate, the equilibrium rate is unique. + +The hypothesis `hS` is the entire economic content, and it is restrictive: it holds for CRRA +with `γ ≤ 1` (Light 2018) and fails for the calibrations of Açıkgöz (2018) and Walsh and +Young (2025). See the module docstring. -/ +theorem equilibriumRate_unique {A δ rlo rhi : ℝ} (hA : A ≠ 0) (hδ : 0 < rlo + δ) {S : ℝ → ℝ} + (hS : MonotoneOn S (Icc rlo rhi)) {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (he₁ : S r₁ = capitalDemand A δ r₁) (he₂ : S r₂ = capitalDemand A δ r₂) : r₁ = r₂ := + eq_of_monotoneOn_of_strictAntiOn hS (capitalDemand_strictAntiOn hA hδ) h₁ h₂ he₁ he₂ + +/-- **A larger capital supply means a lower equilibrium rate.** With demand strictly decreasing, +two economies whose supply schedules are ordered have their equilibrium rates ordered the other +way. This is what uniqueness buys: without it "the" equilibrium rate is not a number to compare. + +The hypothesis is only needed at `r₂`, and only monotonicity of the SMALLER supply is used. -/ +theorem equilibriumRate_le_of_supply_le {A δ rlo rhi : ℝ} (hA : A ≠ 0) (hδ : 0 < rlo + δ) + {S T : ℝ → ℝ} (hS : MonotoneOn S (Icc rlo rhi)) (hle : ∀ r ∈ Icc rlo rhi, S r ≤ T r) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (he₁ : S r₁ = capitalDemand A δ r₁) (he₂ : T r₂ = capitalDemand A δ r₂) : r₂ ≤ r₁ := by + by_contra hcon + rw [not_le] at hcon + have hD : capitalDemand A δ r₂ < capitalDemand A δ r₁ := + capitalDemand_strictAntiOn hA hδ h₁ h₂ hcon + have hchain : capitalDemand A δ r₁ ≤ capitalDemand A δ r₂ := by + calc capitalDemand A δ r₁ = S r₁ := he₁.symm + _ ≤ S r₂ := hS h₁ h₂ hcon.le + _ ≤ T r₂ := hle r₂ h₂ + _ = capitalDemand A δ r₂ := he₂ + linarith + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Imports.lean b/LeanPool/AiyagariEquilibrium/Imports.lean new file mode 100644 index 000000000..5a8d445cc --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Imports.lean @@ -0,0 +1,64 @@ +/- +Copyright (c) 2026 Lean Pool contributors. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Lean Pool contributors +-/ +module -- shake: keep-all --deprecated_module: ignore + +-- Generated project imports; run `lake exe mk_all`. +public import LeanPool.AiyagariEquilibrium +public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements +public import LeanPool.AiyagariEquilibrium.Analysis.DifferentiableSandwich +public import LeanPool.AiyagariEquilibrium.Analysis.HARA +public import LeanPool.AiyagariEquilibrium.Analysis.PowerMean +public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion +public import LeanPool.AiyagariEquilibrium.Distribution.Feller +public import LeanPool.AiyagariEquilibrium.Distribution.Stationary +public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Bellman +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Blackwell +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Extended +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.ExtendedStochastic +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Stochastic +public import LeanPool.AiyagariEquilibrium.Equilibrium.Aiyagari +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness +public import LeanPool.AiyagariEquilibrium.Equilibrium.Bisection +public import LeanPool.AiyagariEquilibrium.Equilibrium.CapitalSupplyMonotone +public import LeanPool.AiyagariEquilibrium.Equilibrium.Firm +public import LeanPool.AiyagariEquilibrium.Equilibrium.PositiveCapital +public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange +public import LeanPool.AiyagariEquilibrium.Equilibrium.Uniqueness +public import LeanPool.AiyagariEquilibrium.Models.AiyagariTauchenWitness +public import LeanPool.AiyagariEquilibrium.Models.BoundedIncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Models.CRRA +public import LeanPool.AiyagariEquilibrium.Models.CRRAConstants +public import LeanPool.AiyagariEquilibrium.Models.ConsumptionFloor +public import LeanPool.AiyagariEquilibrium.Models.ConsumptionSavingsUnbounded +public import LeanPool.AiyagariEquilibrium.Models.EulerCorner +public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationAugmented +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCarrollKimball +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCrossRate +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEnvelope +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationHARA +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationLipschitz +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateMonotone +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateStep +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRegion +public import LeanPool.AiyagariEquilibrium.Models.LargeBetaLight +public import LeanPool.AiyagariEquilibrium.Models.LogAiyagari +public import LeanPool.AiyagariEquilibrium.Models.MinimalMPC +public import LeanPool.AiyagariEquilibrium.Models.MonotoneTransitions +public import LeanPool.AiyagariEquilibrium.Models.NaturalAssetBound +public import LeanPool.AiyagariEquilibrium.Models.Tauchen +public import LeanPool.AiyagariEquilibrium.Topology.Berge +public import LeanPool.AiyagariEquilibrium.Topology.BergeEReal +public import LeanPool.AiyagariEquilibrium.Topology.IccCorrespondence diff --git a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean new file mode 100644 index 000000000..6201895ea --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.Tauchen +public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness + + +/-! +# Aiyagari (1994) with his own earnings process + +Aiyagari's labour endowment follows `log l' = ρ log l + σ √(1 - ρ²) ε`, `ε ~ N(0,1)`, discretised +by Tauchen's method on `[-3σ, 3σ]`, and scaled so that its mean is one. He used seven states, +`ρ ∈ {0, 0.3, 0.6, 0.9}` and `σ ∈ {0.2, 0.4}`. The economy here has `n + 2` states for any `n`, +any `ρ ∈ [0, 1)`, any `σ > 0`, and any scale `c > 0` — the normalising constant is whatever it +is; uniqueness does not depend on it except through the cap condition, where it enters via the +highest endowment `c · e^{3σ}`. + +The two facts about the chain that the uniqueness theorem needs — monotone transitions and a +lowest state reachable from everywhere — are `monotoneTransitions_of_tauchen` and +`tauchen_pos_zero`, both consequences of `Φ` being monotone and positive. Everything else is +`rfl` and `norm_num`, as for the two-state witnesses. +-/ + +@[expose] public section + +open Set MeasureTheory + +namespace LeanEconomics + +/-- **Aiyagari (1994)'s economy with a Tauchen earnings chain**: `β = 0.96`, log utility, no +borrowing, `n + 2` grid points on `[-3σ, 3σ]` for the AR(1) with persistence `ρ` and +unconditional standard deviation `σ`, endowment scaled by `c`. -/ +noncomputable def aiyagariTauchen (n : ℕ) (ρ σ c cap : ℝ) (hρ0 : 0 ≤ ρ) (hρ1 : ρ < 1) + (hσ : 0 < σ) (hc : 0 < c) (hcap : 0 ≤ cap) : IncomeFluctuation (Fin (n + 2)) 0 cap where + income i := c * Real.exp (-3 * σ + i * (6 * σ / (n + 1))) + transitionMatrix := tauchen ρ (σ * Real.sqrt (1 - ρ ^ 2)) (-3 * σ) (6 * σ / (n + 1)) (n + 1) + interest := 0 + discount := 24 / 25 + u := crraUtility 1 + minIncome := c * Real.exp (-3 * σ) + maxIncome := c * Real.exp (3 * σ) + minIncome_pos := mul_pos hc (Real.exp_pos _) + minIncome_le i := by + apply mul_le_mul_of_nonneg_left _ hc.le + apply Real.exp_le_exp.mpr + have hi : (0 : ℝ) ≤ i := Nat.cast_nonneg _ + have hw : (0 : ℝ) ≤ 6 * σ / (n + 1) := by positivity + nlinarith + le_maxIncome i := by + apply mul_le_mul_of_nonneg_left _ hc.le + apply Real.exp_le_exp.mpr + have hi : (i : ℝ) ≤ n + 1 := by exact_mod_cast Nat.le_of_lt_succ i.isLt + have hw : (0 : ℝ) ≤ 6 * σ / (n + 1) := by positivity + have h1 : (i : ℝ) * (6 * σ / (n + 1)) ≤ (n + 1) * (6 * σ / (n + 1)) := + mul_le_mul_of_nonneg_right hi hw + have h2 : ((n : ℝ) + 1) * (6 * σ / (n + 1)) = 6 * σ := by field_simp + linarith + transitionMatrix_nonneg i j := + tauchen_nonneg (mul_pos hσ (Real.sqrt_pos.mpr (by nlinarith))) (by positivity) i j + transitionMatrix_sum i := tauchen_sum i + interest_gt_neg_one := by norm_num + assetFloor_le_assetCap := hcap + assetCap_nonneg := hcap + minConsumption_pos := by + have := mul_pos hc (Real.exp_pos (-3 * σ)) + linarith + minConsumption_le_floor := le_rfl + discount_lt_one := by norm_num + dom := Ioi 0 + Ioi_subset_dom := subset_rfl + dom_subset_Ici := Ioi_subset_Ici_self + continuousOn_u_dom := continuousOn_crraUtility 1 + monotoneOn_u_dom := monotoneOn_crraUtility 1 + strictConcaveOn_u_dom := strictConcaveOn_crraUtility one_pos + continuousOn_extendDom := + continuousOn_extendDom_Ioi (continuousOn_crraUtility 1) (tendsto_atBot_crraUtility le_rfl) + +variable {n : ℕ} {ρ σ c cap : ℝ} {hρ0 : 0 ≤ ρ} {hρ1 : ρ < 1} {hσ : 0 < σ} {hc : 0 < c} + {hcap : 0 ≤ cap} + +/-- Income rises strictly with the state. -/ +theorem aiyagariTauchen_income_strictMono : + StrictMono (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).income := by + intro i j hij + apply mul_lt_mul_of_pos_left _ hc + apply Real.exp_lt_exp.mpr + have hw : (0 : ℝ) < 6 * σ / (n + 1) := by positivity + have : (i : ℝ) < j := by exact_mod_cast hij + nlinarith + +/-- The lowest state has the lowest income, which is `minIncome`. -/ +theorem aiyagariTauchen_income_zero : + (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).income 0 + = (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).minIncome := by + change c * Real.exp (-3 * σ + ((0 : Fin (n + 2)) : ℝ) * (6 * σ / (n + 1))) = c * Real.exp (-3 * σ) + simp + +/-- The chain has monotone transitions. -/ +theorem aiyagariTauchen_monotone : + (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).MonotoneTransitions := + monotoneTransitions_of_tauchen _ rfl aiyagariTauchen_income_strictMono hρ0 + (mul_pos hσ (Real.sqrt_pos.mpr (by nlinarith))) (by positivity) + +/-- **Aiyagari (1994) at `μ = 1` with his Tauchen earnings process: two equilibrium rates in +`(-δ, λ)` coincide**, for any number of states, any `ρ ∈ [0, 1)`, any `σ > 0` and any scale, +given the cap condition at the larger rate. -/ +theorem aiyagariTauchen_equilibriumRate_unique_Ioo {r₁ r₂ : ℝ} + (h₁ : r₁ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) (h₂ : r₂ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) + (hcapc : 2 * (24 / 25) * (c * Real.exp (3 * σ)) < (1 - 24 / 25 * (1 + max r₁ r₂)) * cap) + (hrr₁ : (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).RateOK r₁) + (hrr₂ : (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).State} + (hμ₁ : ((aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).withRate r₁ hrr₁).IsStationary μ₁) + (hμ₂ : ((aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).aggregateCapital μ₁ + = normalisedDemand (9 / 25) (2 / 25) r₁) + (he₂ : (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).aggregateCapital μ₂ + = normalisedDemand (9 / 25) (2 / 25) r₂) : r₁ = r₂ := + (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).aiyagari1994_log_equilibriumRate_unique_Ioo + rfl rfl rfl aiyagariTauchen_monotone (z₀ := 0) + (fun z => by + rw [aiyagariTauchen_income_zero] + exact (aiyagariTauchen n ρ σ c cap hρ0 hρ1 hσ hc hcap).minIncome_le z) + aiyagariTauchen_income_zero (fun z => tauchen_pos_zero z) h₁ h₂ hcapc hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean new file mode 100644 index 000000000..179333021 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean @@ -0,0 +1,252 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.CRRA +public import LeanPool.AiyagariEquilibrium.Models.ConsumptionFloor +public import LeanPool.AiyagariEquilibrium.Distribution.Stationary +public import LeanPool.AiyagariEquilibrium.Equilibrium.PositiveCapital +public import Mathlib.Analysis.Convex.SpecificFunctions.Pow + + +/-! +# CES with `γ < 1`: utility bounded below + +`crra_pinch` showed that CRRA was confined to log: `tendsto_atBot_u` forced `γ ≥ 1`, and Light's +uniqueness condition forced `γ ≤ 1`. This file removes the first half of the pincer. + +## What had to change, and where + +The obstruction was never the axiom by itself. `tendsto_atBot_u` is what makes `extendBot u` +CONTINUOUS into `EReal`, and continuity of the reward is what Berge's maximum theorem runs on. +With `u (0⁺)` finite, `extendBot u` drops from a finite limit to `⊥` at zero consumption: not even +upper semicontinuous, so the maximum need not be attained. Deleting the axiom would have broken +`exists_optimal_action`, not weakened it. + +`IncomeFluctuation` was therefore refactored over a utility DOMAIN, with `continuousOn_extendDom` +in place of `tendsto_atBot_u`. All that remains here is to take `dom = Ici 0` and check that CES +with `γ < 1` fits. + +## Two things it costs + +`continuousOn_extendDom` is discharged differently — `continuousOn_extendDom_Ici` below, where the +`⊥` branch is simply unreachable — and that part is free. + +Positive consumption at the optimum is not. In the unbounded model it is a corollary of the reward +being `⊥` there. Here the reward at `c = 0` is `u 0`, a perfectly ordinary number, and the +household has to be shown not to want it. That is `MarginalInadaOn`: marginal utility explodes at +zero even though utility itself does not, and `positiveConsumption_of_marginalInada` turns it into +the `PositiveConsumption` the rest of the development asks for. Note that the condition has to +reach `c = 0` itself, which is why `MarginalInada` was generalised to a domain. + +## What comes for free + +Everything stated for `IncomeFluctuation`: the Bellman equation, uniqueness of the optimal action, +concavity and monotonicity of the value function, `policy_mono`, the Feller property, stationary +distributions, the Aiyagari equilibrium machinery. There is no second structure to carry them to. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology MeasureTheory + +namespace LeanEconomics + +/-! ### The domain `Ici 0` -/ + +/-- **The bounded case discharges the continuity requirement trivially.** On `Ici 0` the `⊥` +branch of `extendDom` is never taken, so what is left is continuity of `u` itself. -/ +theorem continuousOn_extendDom_Ici {u : ℝ → ℝ} (hc : ContinuousOn u (Ici 0)) : + ContinuousOn (extendDom (Ici 0) u) (Ici 0) := + (continuous_coe_real_ereal.comp_continuousOn hc).congr fun _ hx => extendDom_of_mem hx + +/-! ### CES with `0 < γ < 1` -/ + +theorem continuousOn_crraUtility_Ici {γ : ℝ} (hγ1 : γ < 1) : + ContinuousOn (crraUtility γ) (Ici 0) := by + have he : crraUtility γ = fun c : ℝ => c ^ (1 - γ) / (1 - γ) := + funext (crraUtility_of_ne (by linarith)) + rw [he] + refine ContinuousOn.div_const (fun x _ => ?_) _ + exact (Real.continuousAt_rpow_const x (1 - γ) (Or.inr (by linarith))).continuousWithinAt + +theorem monotoneOn_crraUtility_Ici {γ : ℝ} (hγ1 : γ < 1) : + MonotoneOn (crraUtility γ) (Ici 0) := by + intro x hx y hy hxy + rw [crraUtility_of_ne (by linarith), crraUtility_of_ne (by linarith)] + exact div_le_div_of_nonneg_right (Real.rpow_le_rpow hx hxy (by linarith)) (by linarith) + +theorem strictConcaveOn_crraUtility_Ici {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) : + StrictConcaveOn ℝ (Ici 0) (crraUtility γ) := by + have hpos : (0 : ℝ) < 1 - γ := by linarith + refine ⟨convex_Ici 0, fun x hx y hy hxy a b ha hb hab => ?_⟩ + have key := (Real.strictConcaveOn_rpow hpos (by linarith)).2 hx hy hxy ha hb hab + simp only [smul_eq_mul] at key ⊢ + simp only [crraUtility_of_ne (show γ ≠ 1 by linarith)] + rw [show a * (x ^ (1 - γ) / (1 - γ)) + b * (y ^ (1 - γ) / (1 - γ)) + = (a * x ^ (1 - γ) + b * y ^ (1 - γ)) / (1 - γ) from by ring] + gcongr + +theorem crraUtility_zero {γ : ℝ} (hγ1 : γ < 1) : crraUtility γ 0 = 0 := by + rw [crraUtility_of_ne (by linarith), Real.zero_rpow (by linarith)] + simp + +/-- **CES with `γ < 1` has unbounded marginal value AT zero, not merely near it.** The level +condition `tendsto_atBot_u` fails here, and this is what replaces it: the household still refuses +to consume nothing, because the first unit of consumption is worth more than any bounded gain from +the saving it displaces. -/ +theorem marginalInadaOn_Ici_crraUtility {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) : + MarginalInadaOn (Ici 0) (crraUtility γ) := by + intro M + obtain ⟨δ₁, hδ₁, hspec⟩ := marginalInada_crraUtility hγ0 M + obtain ⟨δ₂, hδ₂M, hδ₂0⟩ := + (((tendsto_rpow_neg_atTop hγ0).eventually_ge_atTop M).and self_mem_nhdsWithin).exists + refine ⟨min δ₁ δ₂, lt_min hδ₁ hδ₂0, fun c c' hc hcc' hc'δ => ?_⟩ + rcases (mem_Ici.mp hc).lt_or_eq with hcpos | hc0 + · exact hspec c c' hcpos hcc' (le_trans hc'δ (min_le_left _ _)) + -- the new case: `c = 0`, where the classical condition has nothing to say + have hc' : 0 < c' := hc0 ▸ hcc' + have hM : M ≤ c' ^ (-γ) := + hδ₂M.trans (rpow_neg_antitone hγ0 hc' (le_trans hc'δ (min_le_right _ _))) + have hmul : c' ^ (-γ) * c' = c' ^ (1 - γ) := by + have hadd := Real.rpow_add hc' (-γ) 1 + rw [Real.rpow_one] at hadd + rw [← hadd, show -γ + 1 = 1 - γ from by ring] + have h1 : M * c' ≤ c' ^ (1 - γ) := by + rw [← hmul]; exact mul_le_mul_of_nonneg_right hM hc'.le + have h2 : (0 : ℝ) ≤ c' ^ (1 - γ) := (Real.rpow_pos_of_pos hc' _).le + rw [← hc0, crraUtility_zero hγ1, crraUtility_of_ne (show γ ≠ 1 by linarith)] + rw [sub_zero, sub_zero] + rw [le_div_iff₀ (show (0 : ℝ) < 1 - γ by linarith)] + nlinarith + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- Utility is bounded below: the domain is the closed half-line. -/ +def Bounded : Prop := P.dom = Ici 0 + +/-- **Positive consumption in the bounded case.** The `⊥` at zero consumption is gone, so the +argument that used to be free is replaced by a marginal Inada condition. -/ +theorem positiveConsumption_of_bounded_crra {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hb : P.Bounded) (hu : P.u = crraUtility γ) : P.PositiveConsumption := by + refine P.positiveConsumption_of_marginalInada ?_ + rw [hb, hu] + exact marginalInadaOn_Ici_crraUtility hγ0 hγ1 + +end IncomeFluctuation + +/-! ### A CES economy with `γ = 1/2` + +`crraUtility (1/2) c = 2 √c`, which is finite at `c = 0` and therefore inadmissible under the old +structure. The parameters are those of `dispersed`, so the two economies differ only in the +curvature of `u`. -/ + +/-- A household with CES utility at `γ = 1/2`: square-root utility, bounded below. -/ +noncomputable def sqrtCES : IncomeFluctuation (Fin 2) 0 1 where + income z := if z = 0 then 1 / 100 else 1 + transitionMatrix _ _ := 1 / 2 + interest := 0 + discount := 1 / 8 + u := crraUtility (1 / 2) + minIncome := 1 / 100 + maxIncome := 1 + minIncome_pos := by norm_num + minIncome_le z := by fin_cases z <;> norm_num + le_maxIncome z := by fin_cases z <;> norm_num + transitionMatrix_nonneg _ _ := by norm_num + transitionMatrix_sum _ := by simp + interest_gt_neg_one := by norm_num + assetFloor_le_assetCap := by norm_num + assetCap_nonneg := by norm_num + minConsumption_pos := by norm_num + minConsumption_le_floor := le_rfl + discount_lt_one := by norm_num + dom := Ici 0 + Ioi_subset_dom := Ioi_subset_Ici_self + dom_subset_Ici := subset_rfl + continuousOn_u_dom := continuousOn_crraUtility_Ici (by norm_num) + monotoneOn_u_dom := monotoneOn_crraUtility_Ici (by norm_num) + strictConcaveOn_u_dom := strictConcaveOn_crraUtility_Ici (by norm_num) (by norm_num) + continuousOn_extendDom := continuousOn_extendDom_Ici (continuousOn_crraUtility_Ici (by norm_num)) + +@[simp] theorem sqrtCES_u : sqrtCES.u = crraUtility (1 / 2) := rfl + +theorem sqrtCES_bounded : sqrtCES.Bounded := rfl + +/-- Utility at zero consumption is finite. This is the fact the old structure forbade. -/ +theorem sqrtCES_u_zero : sqrtCES.u 0 = 0 := crraUtility_zero (by norm_num) + +/-- And it is not admissible under the level condition: `crra_pinch` rejected exactly this. -/ +theorem sqrtCES_not_tendsto_atBot : + ¬ Tendsto (crraUtility (1 / 2)) (𝓝[>] (0 : ℝ)) atBot := + not_tendsto_atBot_crraUtility (by norm_num) + +/-- The reward is nonetheless real at zero consumption, so the household could in principle +choose it -- and `sqrtCES_positiveConsumption` is the proof that it does not. -/ +theorem sqrtCES_not_unbounded : ¬ sqrtCES.Unbounded := by + intro h + have : (0 : ℝ) ∈ sqrtCES.dom := mem_Ici.mpr le_rfl + rw [h] at this + exact absurd this (by simp) + +/-- **Positive consumption, earned from the margin.** -/ +theorem sqrtCES_positiveConsumption : sqrtCES.PositiveConsumption := + sqrtCES.positiveConsumption_of_bounded_crra (by norm_num) (by norm_num) rfl rfl + +/-! ### The whole development applies, unchanged -/ + +/-- The Bellman equation, with honest utility. -/ +theorem sqrtCES_bellman {s : ℝ × Fin 2} (hs : s.1 ∈ Icc (0 : ℝ) 1) : + ∃ a' ∈ Icc 0 (sqrtCES.maxSaving s), sqrtCES.consumption s a' ∈ sqrtCES.dom ∧ + sqrtCES.toExtended.valueFunction s + = sqrtCES.u (sqrtCES.consumption s a') + + sqrtCES.discount * ∑ z', sqrtCES.transitionMatrix s.2 z' + * sqrtCES.toExtended.valueFunction (a', z') := + sqrtCES.exists_optimal_saving hs + +/-- The optimal action is unique, from strict concavity on `Ici 0`. -/ +theorem sqrtCES_optimal_action_unique {s : ℝ × Fin 2} (hs : s.1 ∈ Icc (0 : ℝ) 1) {a₀ a₁ : ℝ} + (h₀ : a₀ ∈ sqrtCES.toExtended.feasible s) (h₁ : a₁ ∈ sqrtCES.toExtended.feasible s) + (hm₀ : sqrtCES.toExtended.objectiveE sqrtCES.toExtended.valueFunction s a₀ + = ((sqrtCES.toExtended.bellmanFn sqrtCES.toExtended.valueFunction s : ℝ) : EReal)) + (hm₁ : sqrtCES.toExtended.objectiveE sqrtCES.toExtended.valueFunction s a₁ + = ((sqrtCES.toExtended.bellmanFn sqrtCES.toExtended.valueFunction s : ℝ) : EReal)) : + a₀ = a₁ := + sqrtCES.optimal_action_unique hs h₀ h₁ hm₀ hm₁ + +/-- **Light's Theorem 1 holds for it**: the policy is increasing in assets. This is a result +about `IncomeFluctuation`, applied with no porting at all. -/ +theorem sqrtCES_policy_mono {a a' : ℝ} {z : Fin 2} (ha : a ∈ Icc (0 : ℝ) 1) + (ha' : a' ∈ Icc (0 : ℝ) 1) (hle : a ≤ a') : + sqrtCES.policy (a, z) ≤ sqrtCES.policy (a', z) := + sqrtCES.policy_mono ha ha' hle + +/-- Consumption is positive, and increasing in assets. -/ +theorem sqrtCES_consumptionFn_pos {a : ℝ} (ha : a ∈ Icc (0 : ℝ) 1) (z : Fin 2) : + 0 < sqrtCES.consumptionFn z a := + sqrtCES.consumptionFn_pos sqrtCES_positiveConsumption ha z + +theorem sqrtCES_monotoneOn_consumptionFn (z : Fin 2) : + MonotoneOn (sqrtCES.consumptionFn z) (Icc 0 1) := + sqrtCES.monotoneOn_consumptionFn z + +/-- **A stationary agent distribution exists**, by Krylov-Bogolyubov, with aggregate capital +equal to mean saving. The distribution half of the development needs no porting either. -/ +theorem sqrtCES_exists_isStationary (μ₀ : ProbabilityMeasure sqrtCES.State) : + ∃ μ : ProbabilityMeasure sqrtCES.State, sqrtCES.IsStationary μ := + sqrtCES.exists_isStationary μ₀ + +theorem sqrtCES_aggregateCapital_eq_integral_policy {μ : ProbabilityMeasure sqrtCES.State} + (hμ : sqrtCES.IsStationary μ) : + sqrtCES.aggregateCapital μ + = ∫ s, sqrtCES.policy (sqrtCES.incl s) ∂(μ : Measure sqrtCES.State) := + sqrtCES.aggregateCapital_eq_integral_policy hμ + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean new file mode 100644 index 000000000..20e1ae7bf --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean @@ -0,0 +1,239 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion + + +/-! +# CRRA utility, and where it fits + +Two separate strands of the development each ask something of `u`, and this file specialises to +the CRRA family to see what they ask together. + +* The income fluctuation structure requires `tendsto_atBot_u`: utility falls to `-∞` as + consumption vanishes. That is what makes consumption positive at the optimum without a floor, + and everything downstream — the policy, the distribution, the equilibrium — rests on it. +* Light (2018) requires relative risk aversion at most one, `RelativeRiskAversionLeOne`. + +Within CRRA the first holds exactly when `γ ≥ 1` and the second exactly when `γ ≤ 1`, so +**together they hold only at `γ = 1`: log utility**. That is `crra_pinch`, the point of this file. + +The pinch is sharp in both directions and neither side is an artefact of the proofs. For `γ < 1` +period utility is bounded below by zero, so it cannot tend to `-∞` under any argument +(`not_tendsto_atBot_crraUtility`); for `γ > 1` the utility is strictly CONCAVE in log consumption, +so `RelativeRiskAversionLeOne` fails as badly as it can +(`strictConcaveOn_crra_comp_exp`). + +## What this says about the uniqueness programme + +The formalisation's only worked witness, `NonVacuity.impatient`, uses `u c = -c⁻¹`, which is CRRA +with `γ = 2` (`crraUtility_two`). It satisfies the existence machinery and fails Light's +condition. Log utility is the one CRRA calibration where both can hold, and it sits on the +boundary of each — `u ∘ exp` is affine there, neither convex-strictly nor concave-strictly. + +This is the parametric cost of the uniqueness argument stated exactly, and it matches what the +survey literature reports: Toda and Walsh (2024) call `RRA ≤ 1` "a quite strong parametric +restriction". Escaping `γ = 1` means giving up `tendsto_atBot_u`, which means carrying a +consumption floor or an Inada-free treatment of the corner — a different development, not a +different proof. + +## The single derivative formula + +`hasDerivAt_crraUtility` gives `u' c = c ^ (-γ)` for every `γ`, log included, since `log' c = c⁻¹` +is the `γ = 1` case. Monotonicity, strict concavity and the Inada condition are all read off it, +so the two branches of the definition are only ever separated once. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +/-- **CRRA period utility.** `c ^ (1 - γ) / (1 - γ)`, with the removable case `γ = 1` filled in +by `log`, which is its limit. -/ +noncomputable def crraUtility (γ c : ℝ) : ℝ := + if γ = 1 then Real.log c else c ^ (1 - γ) / (1 - γ) + +theorem crraUtility_of_ne {γ : ℝ} (h : γ ≠ 1) (c : ℝ) : + crraUtility γ c = c ^ (1 - γ) / (1 - γ) := by simp [crraUtility, h] + +@[simp] theorem crraUtility_one : crraUtility 1 = Real.log := by + funext c; simp [crraUtility] + +/-- The existing non-vacuity witness `u c = -c⁻¹` is CRRA with `γ = 2`. -/ +theorem crraUtility_two : crraUtility 2 = fun c : ℝ => -c⁻¹ := by + funext c + rw [crraUtility_of_ne (by norm_num), show (1 : ℝ) - 2 = -1 by norm_num, Real.rpow_neg_one] + ring + +/-! ### The derivative, and everything read off it -/ + +/-- **Marginal utility is `c ^ (-γ)`**, log included. -/ +theorem hasDerivAt_crraUtility (γ : ℝ) {c : ℝ} (hc : 0 < c) : + HasDerivAt (crraUtility γ) (c ^ (-γ)) c := by + rcases eq_or_ne γ 1 with rfl | hγ + · rw [crraUtility_one, show -(1 : ℝ) = -1 from rfl, Real.rpow_neg_one] + exact Real.hasDerivAt_log hc.ne' + · have h : HasDerivAt (fun x : ℝ => x ^ (1 - γ)) ((1 - γ) * c ^ (1 - γ - 1)) c := + Real.hasDerivAt_rpow_const (Or.inl hc.ne') + have h2 := h.div_const (1 - γ) + have hne : (1 : ℝ) - γ ≠ 0 := sub_ne_zero.mpr (Ne.symm hγ) + rw [show (1 - γ) * c ^ (1 - γ - 1) / (1 - γ) = c ^ (-γ) by + rw [show (1 : ℝ) - γ - 1 = -γ by ring]; field_simp] at h2 + exact h2.congr_of_eventuallyEq (Filter.Eventually.of_forall fun x => crraUtility_of_ne hγ x) + +theorem deriv_crraUtility (γ : ℝ) {c : ℝ} (hc : 0 < c) : + deriv (crraUtility γ) c = c ^ (-γ) := (hasDerivAt_crraUtility γ hc).deriv + +theorem continuousOn_crraUtility (γ : ℝ) : ContinuousOn (crraUtility γ) (Ioi 0) := + fun _ hc => ((hasDerivAt_crraUtility γ hc).continuousAt).continuousWithinAt + +theorem strictMonoOn_crraUtility (γ : ℝ) : StrictMonoOn (crraUtility γ) (Ioi 0) := by + refine strictMonoOn_of_deriv_pos (convex_Ioi 0) (continuousOn_crraUtility γ) fun c hc => ?_ + rw [interior_Ioi] at hc + rw [deriv_crraUtility γ hc] + exact Real.rpow_pos_of_pos hc _ + +theorem monotoneOn_crraUtility (γ : ℝ) : MonotoneOn (crraUtility γ) (Ioi 0) := + (strictMonoOn_crraUtility γ).monotoneOn + +theorem strictConcaveOn_crraUtility {γ : ℝ} (hγ : 0 < γ) : + StrictConcaveOn ℝ (Ioi 0) (crraUtility γ) := by + refine StrictAntiOn.strictConcaveOn_of_deriv (convex_Ioi 0) (continuousOn_crraUtility γ) + fun x hx y hy hxy => ?_ + rw [interior_Ioi] at hx hy + rw [deriv_crraUtility γ hx, deriv_crraUtility γ hy, Real.rpow_neg hx.le, Real.rpow_neg hy.le] + rw [inv_lt_inv₀ (Real.rpow_pos_of_pos hy _) (Real.rpow_pos_of_pos hx _)] + exact Real.rpow_lt_rpow hx.le hxy hγ + +/-! ### The Inada condition holds exactly for `γ ≥ 1` -/ + +theorem tendsto_atBot_crraUtility {γ : ℝ} (hγ : 1 ≤ γ) : + Tendsto (crraUtility γ) (𝓝[>] 0) atBot := by + rcases eq_or_lt_of_le hγ with rfl | hγ1 + · rw [crraUtility_one]; exact Real.tendsto_log_nhdsGT_zero + · have hpos : (0 : ℝ) < γ - 1 := by linarith + have hcont : Tendsto (fun c : ℝ => c ^ (γ - 1)) (𝓝[>] 0) (𝓝 0) := by + have h : Tendsto (fun x : ℝ => x ^ (γ - 1)) (𝓝[>] 0) (𝓝 ((0 : ℝ) ^ (γ - 1))) := + (Real.continuousAt_rpow_const 0 (γ - 1) (Or.inr hpos.le)).continuousWithinAt + rwa [Real.zero_rpow hpos.ne'] at h + have hwithin : Tendsto (fun c : ℝ => c ^ (γ - 1)) (𝓝[>] 0) (𝓝[>] 0) := + tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _ hcont + (eventually_nhdsWithin_of_forall fun c hc => Real.rpow_pos_of_pos hc _) + have hinv : Tendsto (fun c : ℝ => (c ^ (γ - 1))⁻¹) (𝓝[>] 0) atTop := + tendsto_inv_nhdsGT_zero.comp hwithin + have hrw : Tendsto (fun c : ℝ => c ^ (1 - γ)) (𝓝[>] 0) atTop := by + refine hinv.congr' (eventually_nhdsWithin_of_forall fun c hc => ?_) + have hcc : c ^ (1 - γ) = (c ^ (γ - 1))⁻¹ := by + rw [show (1 : ℝ) - γ = -(γ - 1) by ring, Real.rpow_neg (le_of_lt hc)] + exact hcc.symm + have hneg : (1 : ℝ) / (1 - γ) < 0 := by + apply div_neg_of_pos_of_neg one_pos; linarith + refine (hrw.const_mul_atTop_of_neg hneg).congr fun c => ?_ + rw [crraUtility_of_ne (by linarith : γ ≠ 1)] + ring + +/-- **For `γ < 1` the Inada condition fails**, and not for want of an argument: period utility is +then bounded below by zero, so it cannot tend to `-∞` along any filter that is not trivial. -/ +theorem not_tendsto_atBot_crraUtility {γ : ℝ} (hγ : γ < 1) : + ¬ Tendsto (crraUtility γ) (𝓝[>] 0) atBot := by + intro h + have hnn : ∀ᶠ c : ℝ in 𝓝[>] 0, (0 : ℝ) ≤ crraUtility γ c := + eventually_nhdsWithin_of_forall fun c hc => by + rw [crraUtility_of_ne (by linarith : γ ≠ 1)] + exact div_nonneg (Real.rpow_nonneg (le_of_lt hc) _) (by linarith) + have hlt : ∀ᶠ c : ℝ in 𝓝[>] 0, crraUtility γ c < 0 := h.eventually (eventually_lt_atBot 0) + obtain ⟨c, hc1, hc2⟩ := (hnn.and hlt).exists + linarith + +/-! ### The pinch -/ + +/-- **Relative risk aversion at most one holds for CRRA exactly when `γ ≤ 1`.** -/ +theorem relativeRiskAversionLeOne_crraUtility_iff {γ : ℝ} : + RelativeRiskAversionLeOne (crraUtility γ) ↔ γ ≤ 1 := by + rcases lt_trichotomy γ 1 with h | rfl | h + · refine ⟨fun _ => h.le, fun _ => ?_⟩ + have he : crraUtility γ = fun c => c ^ (1 - γ) / (1 - γ) := + funext (crraUtility_of_ne (by linarith)) + rw [he] + exact relativeRiskAversionLeOne_crra h + · simpa using relativeRiskAversionLeOne_log + · refine ⟨fun hcon => absurd hcon ?_, fun hle => absurd h (by linarith)⟩ + have he : crraUtility γ = fun c => c ^ (1 - γ) / (1 - γ) := + funext (crraUtility_of_ne (by linarith)) + rw [he] + exact not_relativeRiskAversionLeOne_crra h + +/-- **The two requirements pinch CRRA to log utility.** The income fluctuation structure needs +utility to fall to `-∞` at zero consumption, which forces `γ ≥ 1`; Light's uniqueness argument +needs relative risk aversion at most one, which forces `γ ≤ 1`. -/ +theorem crra_pinch {γ : ℝ} : + (Tendsto (crraUtility γ) (𝓝[>] 0) atBot ∧ RelativeRiskAversionLeOne (crraUtility γ)) + ↔ γ = 1 := by + constructor + · rintro ⟨hinada, hrra⟩ + have hge : 1 ≤ γ := by + by_contra hlt + exact not_tendsto_atBot_crraUtility (by linarith) hinada + have hle : γ ≤ 1 := relativeRiskAversionLeOne_crraUtility_iff.mp hrra + linarith + · rintro rfl + exact ⟨tendsto_atBot_crraUtility le_rfl, relativeRiskAversionLeOne_crraUtility_iff.mpr le_rfl⟩ + +/-- The existing witness fails Light's condition, as `γ = 2 > 1` requires. -/ +theorem not_relativeRiskAversionLeOne_neg_inv : + ¬ RelativeRiskAversionLeOne fun c : ℝ => -c⁻¹ := by + rw [← crraUtility_two] + exact fun h => absurd (relativeRiskAversionLeOne_crraUtility_iff.mp h) (by norm_num) + +/-! ### A log-utility economy + +The pinch is not vacuous: `γ = 1` really is an income fluctuation problem. This is +`NonVacuity.impatient` with its `-c⁻¹` — which is `γ = 2` by `crraUtility_two` — replaced by log, +so the two witnesses sit on either side of Light's condition, and every utility axiom is now +discharged by a single lemma about the CRRA family. -/ + +/-- An impatient household with log utility: `β = 1/100`, `r = 0`, income in `{1, 2}`, cap `1`. -/ +noncomputable def logImpatient : IncomeFluctuation (Fin 2) 0 1 where + income z := if z = 0 then 1 else 2 + transitionMatrix _ _ := 1 / 2 + interest := 0 + discount := 1 / 100 + u := crraUtility 1 + minIncome := 1 + maxIncome := 2 + minIncome_pos := by norm_num + minIncome_le z := by fin_cases z <;> norm_num + le_maxIncome z := by fin_cases z <;> norm_num + transitionMatrix_nonneg _ _ := by norm_num + transitionMatrix_sum _ := by simp + interest_gt_neg_one := by norm_num + assetFloor_le_assetCap := by norm_num + assetCap_nonneg := by norm_num + minConsumption_pos := by norm_num + minConsumption_le_floor := le_rfl + discount_lt_one := by norm_num + dom := Ioi 0 + Ioi_subset_dom := subset_rfl + dom_subset_Ici := Ioi_subset_Ici_self + continuousOn_u_dom := continuousOn_crraUtility 1 + monotoneOn_u_dom := monotoneOn_crraUtility 1 + strictConcaveOn_u_dom := strictConcaveOn_crraUtility one_pos + continuousOn_extendDom := + continuousOn_extendDom_Ioi (continuousOn_crraUtility 1) (tendsto_atBot_crraUtility le_rfl) + +@[simp] theorem logImpatient_u : logImpatient.u = Real.log := crraUtility_one + +/-- **The log economy satisfies Light's condition**, and by `crra_pinch` it is the only CRRA +economy that can. -/ +theorem logImpatient_relativeRiskAversionLeOne : + RelativeRiskAversionLeOne logImpatient.u := + relativeRiskAversionLeOne_crraUtility_iff.mpr le_rfl + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean new file mode 100644 index 000000000..91114f09d --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -0,0 +1,706 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.BoundedIncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Models.NaturalAssetBound + + +/-! +# The quantitative constants for CES + +The equilibrium programme runs on four explicit constants: a lower bound on the secant slope of +`u` (which drives the corner condition and the precautionary gain), an upper bound on it (which +gives the value function its Lipschitz constant), a bound on saving (which gives the decline +condition), and the calibration arithmetic that checks they fit together. All four were computed +for `Real.log`, and `hu : P.u = Real.log` propagated through the whole chain. + +This file does them for CES. The pleasant surprise is that most of it is not a duplication: +`hasDerivAt_crraUtility` already covers every `γ > 0`, log included, so the CES lemmas here +GENERALISE their log counterparts rather than sitting beside them. `crra_marginal_bound` at +`γ = 1` is `log_marginal_bound`, and `crraSlopeBound` at `γ = 1` is `2 log 2 / m`. + +## The one place the shape changes + +For log, marginal utility is `1/c`, so the slope bound available at resources `R` is `1/R` — it +DEGRADES as resources grow, and the multiplicative bound `c ≥ R / (1 + 2G)` is the right output. +For CES with `γ < 1` marginal utility is `c^{-γ}`, and on a capped problem `c ≤ maxConsumption`, +so there is a single slope `maxConsumption ^ (-γ)` that works everywhere. That turns the same +deviation argument into a UNIFORM bound on saving — `policy s ≤ K` for one constant `K` — which +is a cleaner route to the decline condition than the multiplicative bound, and the natural asset +bound falls straight out of it. + +`policy_le_of_marginal_bound` is stated for any `u` with a uniform slope bound, so log gets the +uniform version too, with `m = 1 / maxConsumption`. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology MeasureTheory + +namespace LeanEconomics + +/-! ### Rational exponents reduce to integer powers + +At `γ = 1 - 1/n` every exponent the CES constants produce is `m/n` for integers `m, n`, and a +comparison of `x ^ (m/n)` against a rational is equivalent to one of `x ^ m` against `c ^ n`. +These two lemmas are the whole bridge, and they are what keeps `γ` near 1 arithmetically +tractable: `norm_num` settles the integer form. -/ + +theorem rpow_le_of_pow_le {x c p : ℝ} {m n : ℕ} (hn : n ≠ 0) (hx : 0 ≤ x) (hc : 0 ≤ c) + (hp : p = (m : ℝ) / n) (h : x ^ m ≤ c ^ n) : x ^ p ≤ c := by + have hn' : (0 : ℝ) < n := Nat.cast_pos.mpr (Nat.pos_of_ne_zero hn) + have h1 : x ^ p = (x ^ m) ^ ((n : ℝ)⁻¹) := by + rw [hp, div_eq_mul_inv, Real.rpow_mul hx, Real.rpow_natCast] + have h2 : c = (c ^ n) ^ ((n : ℝ)⁻¹) := by + rw [← Real.rpow_natCast c n, ← Real.rpow_mul hc, mul_inv_cancel₀ hn'.ne', Real.rpow_one] + calc x ^ p = (x ^ m) ^ ((n : ℝ)⁻¹) := h1 + _ ≤ (c ^ n) ^ ((n : ℝ)⁻¹) := Real.rpow_le_rpow (by positivity) h (by positivity) + _ = c := h2.symm + +/-! ### Negative exponents without a root + +The gain and cost sides of the supply floor are both `x ^ (-γ)`. When the base sits on the right +side of one these need no root at all: a base above one makes the power at most one, and a base +below one makes it at most the reciprocal (for `γ ≤ 1`). That covers the COST side of every +calibration, where the base is `η + income - t` with `t` small. The GAIN side, where the base is +small and the bound has to be large, is where a root is genuinely needed — +`le_rpow_neg_of_rpow_le` hands it back to `rpow_le_of_pow_le`. -/ + +/-- A base of at least one makes the negative power at most one. -/ +theorem rpow_neg_le_one_of_one_le {x γ : ℝ} (hx : 1 ≤ x) (hγ : 0 ≤ γ) : x ^ (-γ) ≤ 1 := + Real.rpow_le_one_of_one_le_of_nonpos hx (by linarith) + +/-- A base of at most one makes the negative power at most the reciprocal, when `γ ≤ 1`. -/ +theorem rpow_neg_le_inv_of_le_one {x γ : ℝ} (hx : 0 < x) (hx1 : x ≤ 1) (hγ : γ ≤ 1) : + x ^ (-γ) ≤ x⁻¹ := by + have h := Real.rpow_le_rpow_of_exponent_ge hx hx1 (show (-1 : ℝ) ≤ -γ by linarith) + rwa [Real.rpow_neg_one] at h + +/-- The gain side: a bound on `x ^ γ` from above is a bound on `x ^ (-γ)` from below. -/ +theorem le_rpow_neg_of_rpow_le {x q γ : ℝ} (hx : 0 < x) (hq : 0 < q) (h : x ^ γ ≤ q⁻¹) : + q ≤ x ^ (-γ) := by + have hpos : (0 : ℝ) < x ^ γ := Real.rpow_pos_of_pos hx _ + rw [Real.rpow_neg hx.le, le_inv_comm₀ hq hpos] + exact h + +theorem le_rpow_of_pow_le {x c p : ℝ} {m n : ℕ} (hn : n ≠ 0) (hx : 0 ≤ x) (hc : 0 ≤ c) + (hp : p = (m : ℝ) / n) (h : c ^ n ≤ x ^ m) : c ≤ x ^ p := by + have hn' : (0 : ℝ) < n := Nat.cast_pos.mpr (Nat.pos_of_ne_zero hn) + have h1 : x ^ p = (x ^ m) ^ ((n : ℝ)⁻¹) := by + rw [hp, div_eq_mul_inv, Real.rpow_mul hx, Real.rpow_natCast] + have h2 : c = (c ^ n) ^ ((n : ℝ)⁻¹) := by + rw [← Real.rpow_natCast c n, ← Real.rpow_mul hc, mul_inv_cancel₀ hn'.ne', Real.rpow_one] + calc c = (c ^ n) ^ ((n : ℝ)⁻¹) := h2 + _ ≤ (x ^ m) ^ ((n : ℝ)⁻¹) := Real.rpow_le_rpow (by positivity) h (by positivity) + _ = x ^ p := h1.symm + +/-! ### Secant slopes of CRRA, bounded below -/ + +/-- **The secant slope of CRRA on `(0, R]` is at least `R ^ (-γ)`.** This is +`log_marginal_bound` for every `γ > 0` at once: at `γ = 1` the bound `R ^ (-1)` is `1 / R`. -/ +theorem crra_marginal_bound {γ R : ℝ} (hγ : 0 < γ) {c d : ℝ} (hd : 0 < d) (hdc : d ≤ c) + (hcR : c ≤ R) : R ^ (-γ) * (c - d) ≤ crraUtility γ c - crraUtility γ d := by + rcases eq_or_lt_of_le hdc with rfl | hlt + · simp + obtain ⟨ξ, hξ, hslope⟩ := exists_hasDerivAt_eq_slope (crraUtility γ) + (fun x => x ^ (-γ)) hlt + ((continuousOn_crraUtility γ).mono fun x hx => lt_of_lt_of_le hd hx.1) + (fun x hx => hasDerivAt_crraUtility γ (hd.trans hx.1)) + have hmono : R ^ (-γ) ≤ ξ ^ (-γ) := + rpow_neg_antitone hγ (hd.trans hξ.1) (hξ.2.le.trans hcR) + rw [hslope, le_div_iff₀ (by linarith)] at hmono + linarith + +/-- **The bound reaches `d = 0` when `γ < 1`.** Utility is finite there, so the statement makes +sense, and the first unit of consumption is worth at least as much as any later one. This is the +case log cannot have. -/ +theorem crra_marginal_bound_zero {γ R : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) {c : ℝ} + (hc : 0 < c) (hcR : c ≤ R) : + R ^ (-γ) * (c - 0) ≤ crraUtility γ c - crraUtility γ 0 := by + have hM : R ^ (-γ) ≤ c ^ (-γ) := rpow_neg_antitone hγ0 hc hcR + have hmul : c ^ (-γ) * c = c ^ (1 - γ) := by + have hadd := Real.rpow_add hc (-γ) 1 + rw [Real.rpow_one] at hadd + rw [← hadd, show -γ + 1 = 1 - γ from by ring] + have h1 : R ^ (-γ) * c ≤ c ^ (1 - γ) := by + rw [← hmul]; exact mul_le_mul_of_nonneg_right hM hc.le + have h2 : (0 : ℝ) ≤ c ^ (1 - γ) := (Real.rpow_pos_of_pos hc _).le + rw [crraUtility_zero hγ1, crraUtility_of_ne (show γ ≠ 1 by linarith), sub_zero, sub_zero, + le_div_iff₀ (show (0 : ℝ) < 1 - γ by linarith)] + nlinarith + +/-- The slope bound on all of `Ici 0`, which is the domain CES with `γ < 1` uses. -/ +theorem crra_marginal_bound_Ici {γ R : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) {c d : ℝ} + (hd : d ∈ Ici (0 : ℝ)) (hdc : d ≤ c) (hcR : c ≤ R) : + R ^ (-γ) * (c - d) ≤ crraUtility γ c - crraUtility γ d := by + rcases (mem_Ici.mp hd).lt_or_eq with hdpos | hd0 + · exact crra_marginal_bound hγ0 hdpos hdc hcR + rcases eq_or_lt_of_le (hd0 ▸ hdc : (0 : ℝ) ≤ c) with hc0 | hcpos + · rw [← hd0, ← hc0]; simp + · rw [← hd0]; exact crra_marginal_bound_zero hγ0 hγ1 hcpos hcR + +/-- **The gain from `d` extra consumption, bounded below.** Taking `R = c + d` in +`crra_marginal_bound` gives the CES form of `log_sub_log_ge`. -/ +theorem crra_sub_ge {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) {c d : ℝ} (hc : 0 ≤ c) (hd : 0 ≤ d) : + (c + d) ^ (-γ) * d ≤ crraUtility γ (c + d) - crraUtility γ c := by + have := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hc) (le_add_of_nonneg_right hd) + (le_refl (c + d)) + simpa using this + +/-- **The secant slope of CRRA on `[d, c]` is at most `d ^ (-γ)`.** The mirror image of +`crra_marginal_bound`, and what caps the COST of saving. -/ +theorem crra_marginal_bound_above {γ : ℝ} (hγ : 0 < γ) {c d : ℝ} (hd : 0 < d) (hdc : d ≤ c) : + crraUtility γ c - crraUtility γ d ≤ d ^ (-γ) * (c - d) := by + rcases eq_or_lt_of_le hdc with rfl | hlt + · simp + obtain ⟨ξ, hξ, hslope⟩ := exists_hasDerivAt_eq_slope (crraUtility γ) + (fun x => x ^ (-γ)) hlt + ((continuousOn_crraUtility γ).mono fun x hx => lt_of_lt_of_le hd hx.1) + (fun x hx => hasDerivAt_crraUtility γ (hd.trans hx.1)) + have hmono : ξ ^ (-γ) ≤ d ^ (-γ) := rpow_neg_antitone hγ hd hξ.1.le + rw [hslope, div_le_iff₀ (by linarith)] at hmono + linarith + +/-- **The cost of saving `h`, capped uniformly in resources.** Resources are at least `m`, so what +is left after saving is at least `m - h`, and marginal utility there is the largest that can +apply. The log form of the same bound is `log (m / (m - h))`. -/ +theorem crra_cost_of_saving {γ : ℝ} (hγ : 0 < γ) {m h R b : ℝ} (hh0 : 0 < h) (hhm : h < m) + (hmR : m ≤ R) (hb0 : 0 ≤ b) (hbh : b ≤ h) : + crraUtility γ (R - b) - crraUtility γ (R - h) ≤ (m - h) ^ (-γ) * h := by + have hRh : 0 < R - h := by linarith + have hstep := crra_marginal_bound_above hγ hRh (show R - h ≤ R - b by linarith) + have hanti : (R - h) ^ (-γ) ≤ (m - h) ^ (-γ) := + rpow_neg_antitone hγ (by linarith) (by linarith) + have hpos : (0 : ℝ) < (R - h) ^ (-γ) := Real.rpow_pos_of_pos hRh _ + nlinarith [hstep, hanti, hb0] + +/-! ### Secant slopes of CRRA, bounded above -/ + +/-- The closed form of `slopeBound (crraUtility γ)` at the income floor. At `γ = 1` this is +`2 log 2 / m`, which is `log_slopeBoundU`. -/ +noncomputable def crraSlopeBound (γ m : ℝ) : ℝ := + 2 * m ^ (-γ) * (1 - ((2 : ℝ) ^ (1 - γ))⁻¹) / (1 - γ) + +theorem slopeBound_crraUtility {γ m : ℝ} (hγ : γ ≠ 1) (hm : 0 < m) : + slopeBound (crraUtility γ) m = crraSlopeBound γ m := by + have h2 : (0 : ℝ) < (2 : ℝ) ^ (1 - γ) := Real.rpow_pos_of_pos (by norm_num) _ + have hhalf : (m / 2) ^ (1 - γ) = m ^ (1 - γ) / (2 : ℝ) ^ (1 - γ) := + Real.div_rpow hm.le (by norm_num : (0 : ℝ) ≤ 2) (1 - γ) + have hexp : m ^ (1 - γ) = m * m ^ (-γ) := by + have h := Real.rpow_add hm 1 (-γ) + rw [Real.rpow_one, show (1 : ℝ) + -γ = 1 - γ from by ring] at h + exact h + have hγ' : (1 : ℝ) - γ ≠ 0 := sub_ne_zero.mpr (Ne.symm hγ) + simp only [slopeBound, crraSlopeBound, crraUtility_of_ne hγ, hhalf, hexp] + field_simp + +theorem crraSlopeBound_nonneg {γ m : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hm : 0 < m) : + 0 ≤ crraSlopeBound γ m := by + have h2 : (1 : ℝ) ≤ (2 : ℝ) ^ (1 - γ) := + Real.one_le_rpow (by norm_num : (1 : ℝ) ≤ 2) (by linarith) + have hinv : ((2 : ℝ) ^ (1 - γ))⁻¹ ≤ 1 := by + rw [inv_le_one₀ (by linarith)]; exact h2 + have hmp : (0 : ℝ) < m ^ (-γ) := Real.rpow_pos_of_pos hm _ + refine div_nonneg ?_ (by linarith) + have : (0 : ℝ) ≤ 1 - ((2 : ℝ) ^ (1 - γ))⁻¹ := by linarith + positivity + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +-- This layer is the CALIBRATED one: every constant is computed for a household whose borrowing +-- limit is at zero. The general-floor machinery lives upstream; nothing here needs it, and +-- pinning the floor keeps the arithmetic in the primitives. +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-- **A uniform bound on saving from a uniform marginal bound.** Deviate to half the optimal +saving: the extra consumption is worth at least `m` per unit, and concavity of the continuation +caps what the forgone saving could have repaid at `deviationGap`, whatever the state. + +The log development could not use this, because for log the available `m` at resources `R` is +`1 / R`, which is not uniform. It is stated generically anyway — log gets the version with +`m = 1 / maxConsumption`, which is uniform after all, just weaker than what the multiplicative +argument extracts. For CES with `γ < 1` the uniform `m = maxConsumption ^ (-γ)` is all there is, +and it is enough. -/ +theorem policy_le_of_marginal_bound {m : ℝ} (hm : 0 < m) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + P.policy (a, z) ≤ 2 * P.deviationGap / m := by + have hG : 0 ≤ P.deviationGap := P.deviationGap_nonneg + set b : ℝ := P.policy (a, z) with hbdef + set c : ℝ := P.consumptionFn z a with hcdef + rcases eq_or_lt_of_le (P.policy_mem_region (a, z)).1 with hzero | hbpos + · rw [← hbdef] at hzero + rw [← hzero] + positivity + rw [← hbdef] at hbpos + have hcpos : 0 < c := P.consumptionFn_pos hpc ha z + have hres : P.resources (a, z) = c + b := by simp only [hcdef, consumptionFn, consumption]; ring + -- the deviation: save half as much + have hhalf0 : 0 < b / 2 := by linarith + have hhalfb : b / 2 < b := by linarith + have hbreg : b ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ + have hmem0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ + have hfeas : b / 2 ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hhalf0.le, by linarith [(P.policy_mem (a, z)).2]⟩ + have hcons : P.consumption (a, z) (b / 2) = c + b / 2 := by + simp only [consumption]; rw [hres]; ring + have hdev : 0 < P.consumption (a, z) (b / 2) := by rw [hcons]; linarith + have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hdev) + simp only [objR, hcons] at hopt + rw [show P.consumption (a, z) (P.policy (a, z)) = c from rfl, + show P.policy (a, z) = b from rfl] at hopt + -- concavity of the continuation caps the forgone repayment + have hslope := (P.concaveOn_cont z).slope_anti_adjacent hmem0 hbreg hhalf0 hhalfb + have hb1 := abs_le.mp (P.abs_cont_le z (b / 2)) + have hb0 := abs_le.mp (P.abs_cont_le z 0) + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hloss : P.cont z b - P.cont z (b / 2) ≤ 2 * ‖P.toExtended.valueFunction‖ := by + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + nlinarith [hslope, hb1.1, hb0.2] + have hscaled : (P.discount : ℝ) * (P.cont z b - P.cont z (b / 2)) ≤ P.deviationGap := by + simp only [deviationGap] + nlinarith [hloss, hβ] + -- the marginal gain from the extra consumption + have hcmax : c + b / 2 ≤ P.maxConsumption := by + have := P.consumption_le_maxConsumption (s := (a, z)) ha (le_refl (0 : ℝ)) + simp only [consumption, sub_zero] at this + rw [hres] at this + linarith + have hmg := hmarg (c + b / 2) c (P.mem_dom_of_pos hcpos) (by linarith) hcmax + rw [show c + b / 2 - c = b / 2 from by ring] at hmg + rw [le_div_iff₀ hm] + nlinarith [hopt, hscaled, hmg] + +/-! ### The oscillation gap + +`deviationGap = 2 β ‖V‖` is what the log development compares against, and it is not +translation-invariant. That is fatal for CES: `c ^ (1-γ) / (1-γ)` differs from `log` by the +constant `1 / (1-γ)`, so `‖V‖` blows up as `γ → 1` while the model it describes does not. Every +use of it is against a DIFFERENCE of continuation values, so what is really needed is the +oscillation, and that is `(u maxConsumption - u minIncome) / (1 - β)`. -/ + +/-- The spread of the value function, from the two constant plans. -/ +noncomputable def oscGap : ℝ := + (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) + +theorem valueFunction_le_oscBound (s : ℝ × Z) : + P.toExtended.valueFunction s ≤ P.u P.maxConsumption / (1 - P.discount) := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine P.toExtended.valueFunction_le_const ?_ s + have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith + have key : P.u P.maxConsumption + + (P.discount : ℝ) * (P.u P.maxConsumption / (1 - P.discount)) + = P.u P.maxConsumption / (1 - P.discount) := by field_simp; ring + rw [show P.toExtended.rewardMax = P.u P.maxConsumption from rfl, + show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] + exact le_of_eq key + +theorem oscBound_le_valueFunction (s : ℝ × Z) : + P.u P.minConsumption / (1 - P.discount) ≤ P.toExtended.valueFunction s := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine P.toExtended.const_le_valueFunction ?_ s + have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith + have key : P.u P.minConsumption / (1 - P.discount) + = P.u P.minConsumption + (P.discount : ℝ) * (P.u P.minConsumption / (1 - P.discount)) := by + field_simp; ring + rw [show P.toExtended.rewardMin = P.u P.minConsumption from rfl, + show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] + exact le_of_eq key + +theorem oscGap_nonneg : 0 ≤ P.oscGap := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) + exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) + (P.mem_dom_of_pos P.maxConsumption_pos) P.minConsumption_le_maxConsumption + +/-- **The continuation value moves by at most the oscillation gap**, whatever the two saving +levels. This is what replaces `2 ‖V‖` in every comparison. -/ +theorem cont_sub_le_oscGap (z : Z) (x y : ℝ) : P.cont z x - P.cont z y ≤ P.oscGap := by + have hsub : P.cont z x - P.cont z y + = ∑ z' : Z, P.transitionMatrix z z' * + (P.toExtended.valueFunction (x, z') - P.toExtended.valueFunction (y, z')) := by + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + rw [hsub] + calc ∑ z' : Z, P.transitionMatrix z z' * + (P.toExtended.valueFunction (x, z') - P.toExtended.valueFunction (y, z')) + ≤ ∑ z' : Z, P.transitionMatrix z z' * P.oscGap := by + refine Finset.sum_le_sum fun z' _ => ?_ + refine mul_le_mul_of_nonneg_left ?_ (P.transitionMatrix_nonneg _ _) + simp only [oscGap, sub_div] + linarith [P.valueFunction_le_oscBound (x, z'), P.oscBound_le_valueFunction (y, z')] + _ = P.oscGap := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + +/-- The Lipschitz constant of the value function under CES, mirroring `logLipschitz`. -/ +noncomputable def crraLipschitz (γ : ℝ) : ℝ := + crraSlopeBound γ P.minConsumption * (1 + P.interest) / (1 - P.discount * (1 + P.interest)) + +theorem crraLipschitz_nonneg {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hβR : P.discount * (1 + P.interest) < 1) : 0 ≤ P.crraLipschitz γ := by + have hs := crraSlopeBound_nonneg hγ0 hγ1 P.minConsumption_pos + have hr := P.interest_gt_neg_one + exact div_nonneg (by positivity) (by linarith) + +/-- **The value function is Lipschitz with the explicit CES constant.** -/ +theorem crra_valueFunction_lipschitz {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hu : P.u = crraUtility γ) (hβR : P.discount * (1 + P.interest) < 1) : + ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ y ∈ Icc (0 : ℝ) assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| + ≤ P.crraLipschitz γ * |x - y| := by + refine P.valueFunction_lipschitz (P.crraLipschitz_nonneg hγ0 hγ1 hβR) (le_of_eq ?_) + have hne : (1 : ℝ) - P.discount * (1 + P.interest) ≠ 0 := by linarith + simp only [crraLipschitz, slopeBoundU, hu, + slopeBound_crraUtility (show γ ≠ 1 by linarith) P.minConsumption_pos] + field_simp + ring + +/-- **The borrowing constraint binds where resources are small**, CES version. The threshold is +`resources ^ (-γ)` where the log version had `1 / resources`. -/ +theorem crra_policy_eq_zero_of_resources {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hβR : P.discount * (1 + P.interest) < 1) {s : ℝ × Z} (hs : s.1 ∈ Icc (0 : ℝ) assetCap) + (hlt : P.discount * P.crraLipschitz γ < P.resources s ^ (-γ)) : + P.policy s = 0 := + P.policy_eq_zero_of_corner_at (P.crra_valueFunction_lipschitz hγ0 hγ1 hu hβR) hs + (fun c d hd hdc hc => by + rw [hu] + rw [sub_zero] at hc + exact crra_marginal_bound_Ici hγ0 hγ1 (by rw [hb] at hd; exact hd) hdc hc) + hlt + +/-- **The CES saving bound in closed form.** Saving never exceeds `2 · deviationGap · +maxConsumption ^ γ`, whatever the state. -/ +theorem crra_policy_le {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounded) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) + (z : Z) : P.policy (a, z) ≤ 2 * P.deviationGap * P.maxConsumption ^ γ := by + have hmc := P.maxConsumption_pos + have hm : (0 : ℝ) < P.maxConsumption ^ (-γ) := Real.rpow_pos_of_pos hmc _ + have hkey := P.policy_le_of_marginal_bound hm + (fun c d hd hdc hc => by + rw [hu] + exact crra_marginal_bound_Ici hγ0 hγ1 (by rw [hb] at hd; exact hd) hdc hc) + hpc ha z + have hinv : P.maxConsumption ^ (-γ) = (P.maxConsumption ^ γ)⁻¹ := Real.rpow_neg hmc.le γ + rw [hinv, div_eq_mul_inv, inv_inv] at hkey + exact hkey + +/-- **The natural asset bound for CES.** A uniform cap on saving is a decline condition outright: +above it, assets fall. No linear consumption bound and no `exp` in sight. -/ +theorem crra_exists_decline {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounded) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z : Z) : + ∃ ā : ℝ, ∀ a ∈ Icc (0 : ℝ) assetCap, ā < a → P.policy (a, z) < a := + ⟨2 * P.deviationGap * P.maxConsumption ^ γ, fun _ ha hlt => + lt_of_le_of_lt (P.crra_policy_le hγ0 hγ1 hb hu hpc ha _) hlt⟩ + +/-- **The continuation's gain between two saving levels, CES version.** Where the log form carries +`log (1 + R·(y-x) / (income + R·x))`, this carries `(income + R·y) ^ (-γ) · R·(y-x)`: the extra +resources, valued at the marginal utility of the largest consumption they could buy. -/ +theorem crra_cont_sub_ge_gen {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z z₀ : Z) {x y : ℝ} + (hx : x ∈ Icc (0 : ℝ) assetCap) (hy : y ∈ Icc (0 : ℝ) assetCap) (hxy : x ≤ y) : + P.transitionMatrix z z₀ * ((P.income z₀ + (1 + P.interest) * y) ^ (-γ) + * ((1 + P.interest) * (y - x))) + ≤ P.cont z y - P.cont z x := by + have hRh : 0 ≤ (1 + P.interest) * (y - x) := + mul_nonneg P.interest_gt_neg_one.le (by linarith) + have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + have hterm : P.transitionMatrix z z₀ * + (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) + ≤ P.cont z y - P.cont z x := by + rw [hsum] + refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) + (fun z' _ => ?_) (Finset.mem_univ z₀) + exact mul_nonneg (P.transitionMatrix_nonneg _ _) + (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) + have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx + have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by + simp only [resources, max_eq_right hx.1] + have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) + ≤ P.income z₀ + (1 + P.interest) * x := by + simp only [consumption, hresx] + linarith [(P.policy_mem_region (x, z₀)).1] + have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ + rw [hu] at hgain + -- the marginal bound, at the largest consumption the richer state could reach + have hbound : (P.income z₀ + (1 + P.interest) * y) ^ (-γ) * ((1 + P.interest) * (y - x)) + ≤ crraUtility γ (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) + - crraUtility γ (P.consumption (x, z₀) (P.policy (x, z₀))) := by + have hR : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) + ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith [hcle] + have := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hc0.le) + (le_add_of_nonneg_right hRh) hR + simpa using this + exact le_trans (mul_le_mul_of_nonneg_left (le_trans hbound hgain) + (P.transitionMatrix_nonneg z z₀)) hterm + +/-- **The continuation's gain from zero, CES version.** -/ +theorem crra_cont_sub_ge {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z z₀ : Z) {h : ℝ} + (hh : h ∈ Icc (0 : ℝ) assetCap) : + P.transitionMatrix z z₀ * ((P.income z₀ + (1 + P.interest) * h) ^ (-γ) + * ((1 + P.interest) * h)) + ≤ P.cont z h - P.cont z 0 := by + have h0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ + have := P.crra_cont_sub_ge_gen hγ0 hγ1 hu hpc z z₀ h0 hh hh.1 + simpa using this + +/-! ### The sharp decline constant + +`crra_policy_le` bounds saving by a constant, which is enough for a natural asset bound but far +too weak for the corner condition to meet it: the corner needs the threshold to be comparable to +the LOW income, and an absolute bound does not shrink with it. The log development gets a +PROPORTIONAL bound instead, `c ≥ R / (1 + 2G)`, whose decline threshold is proportional to income. + +The CES analogue deviates to `θ` times the optimal saving rather than to half of it, and prices +the extra consumption at the margin where it lands: `b ≤ (β · oscGap / θ) · (c + (1-θ) b) ^ γ`. +Taking `θ` near 1 makes the right-hand side nearly `(β · oscGap) c ^ γ`, proportional to +consumption, which is the shape the decline condition needs. -/ + +/-- **The proportional CES bound on saving.** -/ +theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) + (_hb : P.Bounded) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + P.policy (a, z) + ≤ (P.discount * P.oscGap / θ) + * (P.consumption (a, z) (P.policy (a, z)) + (1 - θ) * P.policy (a, z)) ^ γ := by + have hosc := P.oscGap_nonneg + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policy (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hcpos : 0 < c := by + rw [hcdef, hbdef]; exact P.consumption_policy_pos hpc ha + have hbase : (0 : ℝ) < c + (1 - θ) * b := by + have : 0 ≤ (1 - θ) * b := mul_nonneg (by linarith) (P.policy_mem_region (a, z)).1 + linarith + have hrpow : (0 : ℝ) < (c + (1 - θ) * b) ^ γ := Real.rpow_pos_of_pos hbase _ + rcases eq_or_lt_of_le (P.policy_mem_region (a, z)).1 with hzero | hbpos + · rw [← hbdef] at hzero + rw [← hzero] + positivity + rw [← hbdef] at hbpos + -- the deviation: save `θ` times as much + have hθb0 : 0 < θ * b := mul_pos hθ0 hbpos + have hθbb : θ * b < b := by nlinarith + have hbreg : b ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ + have hmem0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ + have hfeas : θ * b ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hθb0.le, by linarith [(P.policy_mem (a, z)).2]⟩ + have hcons : P.consumption (a, z) (θ * b) = c + (1 - θ) * b := by + simp only [hcdef, consumption]; ring + have hdev : 0 < P.consumption (a, z) (θ * b) := by rw [hcons]; linarith + have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hdev) + simp only [objR, hcons] at hopt + rw [show P.consumption (a, z) (P.policy (a, z)) = c from rfl, + show P.policy (a, z) = b from rfl] at hopt + -- concavity of the continuation caps what the forgone saving could have repaid + have hslope := (P.concaveOn_cont z).slope_anti_adjacent hmem0 hbreg hθb0 hθbb + have hloss : P.cont z b - P.cont z (θ * b) ≤ (1 - θ) / θ * P.oscGap := by + have hgap := P.cont_sub_le_oscGap z (θ * b) 0 + rw [div_le_div_iff₀ (show (0:ℝ) < b - θ * b by nlinarith) + (show (0:ℝ) < θ * b - 0 by linarith)] at hslope + have h2 : (P.cont z b - P.cont z (θ * b)) * (θ * b) ≤ P.oscGap * ((1 - θ) * b) := by + nlinarith [hslope, hgap, hbpos, hθ0] + rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] + nlinarith [h2, hbpos] + -- the marginal gain from the extra consumption, priced where it lands + have hmg : (c + (1 - θ) * b) ^ (-γ) * ((1 - θ) * b) + ≤ P.u (c + (1 - θ) * b) - P.u c := by + rw [hu] + have := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hcpos.le) + (le_add_of_nonneg_right (mul_nonneg (by linarith) hbpos.le)) (le_refl (c + (1 - θ) * b)) + simpa using this + have hmid : P.u (c + (1 - θ) * b) - P.u c + ≤ (P.discount : ℝ) * (P.cont z b - P.cont z (θ * b)) := by + have hd : (P.discount : ℝ) * (P.cont z b - P.cont z (θ * b)) + = (P.discount : ℝ) * P.cont z b - (P.discount : ℝ) * P.cont z (θ * b) := by ring + linarith [hopt, hd.le, hd.ge] + have hkey : (c + (1 - θ) * b) ^ (-γ) * ((1 - θ) * b) + ≤ (P.discount : ℝ) * ((1 - θ) / θ * P.oscGap) := + le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) + -- divide out `1 - θ` and clear the negative power + have hinv : (c + (1 - θ) * b) ^ (-γ) = ((c + (1 - θ) * b) ^ γ)⁻¹ := + Real.rpow_neg hbase.le γ + rw [hinv] at hkey + have h1θ : (0 : ℝ) < 1 - θ := by linarith + have hexp : ((c + (1 - θ) * b) ^ γ)⁻¹ * ((1 - θ) * b) + = (1 - θ) * (b / (c + (1 - θ) * b) ^ γ) := by field_simp + rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * P.oscGap) + = (1 - θ) * (P.discount * P.oscGap / θ) from by ring] at hkey + have hdiv : b / (c + (1 - θ) * b) ^ γ ≤ P.discount * P.oscGap / θ := + le_of_mul_le_mul_left hkey h1θ + rw [div_le_iff₀ hrpow] at hdiv + linarith + +/-- **Assets fall above an explicit threshold**, in the income state that matters. The threshold +is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`; with `θ` near one the second +factor is close to `income z ^ γ`, so the threshold shrinks with the income of the state -- which +is exactly what lets the corner condition reach it. -/ +theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) + (hθr : θ ≤ 1 + P.interest) (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) + (hlt : (P.discount * P.oscGap / θ) + * (P.income z + (1 + P.interest - θ) * assetCap) ^ γ < a) : + P.policy (a, z) < a := by + by_contra hcon + rw [not_lt] at hcon + have hosc := P.oscGap_nonneg + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policy (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z + rw [← hbdef, ← hcdef] at hkey + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + have hsum : c + (1 - θ) * b = P.income z + (1 + P.interest) * a - θ * b := by + simp only [hcdef, consumption, hres]; ring + have hle : c + (1 - θ) * b ≤ P.income z + (1 + P.interest - θ) * assetCap := by + rw [hsum] + have h1 : θ * a ≤ θ * b := mul_le_mul_of_nonneg_left hcon hθ0.le + have h2 : (1 + P.interest - θ) * a ≤ (1 + P.interest - θ) * assetCap := + mul_le_mul_of_nonneg_left ha.2 (by linarith) + nlinarith [h1, h2] + have hnn : (0 : ℝ) ≤ c + (1 - θ) * b := by + have hc0 : 0 < c := by rw [hcdef, hbdef]; exact P.consumption_policy_pos hpc ha + have : 0 ≤ (1 - θ) * b := mul_nonneg (by linarith) (P.policy_mem_region (a, z)).1 + linarith + have hmono : (c + (1 - θ) * b) ^ γ + ≤ (P.income z + (1 + P.interest - θ) * assetCap) ^ γ := + Real.rpow_le_rpow hnn hle hγ0.le + have hscale : (P.discount * P.oscGap / θ) * (c + (1 - θ) * b) ^ γ + ≤ (P.discount * P.oscGap / θ) + * (P.income z + (1 + P.interest - θ) * assetCap) ^ γ := + mul_le_mul_of_nonneg_left hmono (by positivity) + linarith + +/-! ### The asset cap never binds + +`not_concaveOn_consumptionFn_of_cap_binds` makes Carroll and Kimball's conclusion FALSE wherever +saving reaches the cap, so the cap is the obstruction to Light's uniqueness argument, not the +utility class. It can be removed by calibration rather than by weakening the theory: run +`crra_policy_le_mul` by contradiction. If saving reached the cap then consumption would be at +most `maxIncome + (1 + r - θ) · assetCap`, and the saving bound evaluated THERE has to exceed +the cap. + +The point of doing it this way is that the bound does not degrade as the cap grows -- the +right-hand side rises with `assetCap` while the left rises only through `oscGap` -- so raising +the cap always eventually works. A cruder estimate that keeps `maxConsumption ^ γ` on the left +grows on both sides and closes the window for `γ` near one, which is an artefact of the +estimate. + +This is Marcet, Obiols-Homs and Weil (2007), Remark R4: "the upper bound on capital that was +introduced to obtain existence and uniqueness of the value function is, in fact, not binding". +Their route is different -- with endogenous labour supply, savings have an absorbing point `k_bar` +below the bound, so the bound is slack for any initial condition below it -- but the purpose is +identical, and it is reassuring that the standard treatment regards the cap as an artefact to be +discharged rather than a modelling choice to be defended. + +The converse is `policy_assetCap_of_patient` (their Proposition 3, in `Impatience`): without +impatience the cap BINDS. So the cap is an artefact precisely when `β (1 + r) < 1`, and the +hypothesis this file carries everywhere is exactly the one that makes it one. -/ + +/-- **Saving never reaches the asset cap.** -/ +theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) + (hθ1 : θ < 1) (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hpc : P.PositiveConsumption) + (hlt : (P.discount * P.oscGap / θ) + * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + P.policy (a, z) < assetCap := by + by_contra hcon + rw [not_lt] at hcon + have hcap := P.assetCap_nonneg + have hosc := P.oscGap_nonneg + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policy (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z + rw [← hbdef, ← hcdef] at hkey + have hc0 : 0 < c := by rw [hcdef, hbdef]; exact P.consumption_policy_pos hpc ha + -- if saving reached the cap, what is left to consume is small + have hres : P.resources (a, z) ≤ P.maxConsumption := by + have := P.consumption_le_maxConsumption (s := (a, z)) ha (le_refl (0 : ℝ)) + simpa only [IncomeFluctuation.consumption, sub_zero] using this + have hsum : c + (1 - θ) * b = P.resources (a, z) - θ * b := by + simp only [hcdef, IncomeFluctuation.consumption]; ring + have hle : c + (1 - θ) * b + ≤ P.maxIncome + (1 + P.interest - θ) * assetCap := by + rw [hsum] + have hθb : θ * assetCap ≤ θ * b := mul_le_mul_of_nonneg_left hcon hθ0.le + simp only [IncomeFluctuation.maxConsumption] at hres + nlinarith [hres, hθb] + have hnn : (0 : ℝ) ≤ c + (1 - θ) * b := by + have : 0 ≤ (1 - θ) * b := mul_nonneg (by linarith) (P.policy_mem_region (a, z)).1 + linarith + have hmono : (c + (1 - θ) * b) ^ γ + ≤ (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ := + Real.rpow_le_rpow hnn hle hγ0.le + have hscale : (P.discount * P.oscGap / θ) * (c + (1 - θ) * b) ^ γ + ≤ (P.discount * P.oscGap / θ) + * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ := + mul_le_mul_of_nonneg_left hmono (by positivity) + linarith + +/-- **The disproof of Carroll and Kimball is disarmed.** Where saving never reaches the cap, the +hypothesis of `not_concaveOn_consumptionFn_of_cap_binds` -- a flat stretch of the policy at the +top of the asset region -- cannot occur, so nothing in this development contradicts concavity of +the consumption function. That is not a proof of concavity; it is the removal of the obstruction +that made one impossible. -/ +theorem no_flat_at_cap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) + (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hpc : P.PositiveConsumption) + (hlt : (P.discount * P.oscGap / θ) + * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + P.policy (a, z) ≠ assetCap := + ne_of_lt (P.crra_policy_lt_assetCap hγ0 hγ1 hθ0 hθ1 hb hu hpc hlt ha z) + +/-- **A quantitative CES saving floor**, the form the sign-change argument consumes. -/ +theorem crra_policy_ge_of_gain {γ : ℝ} (hγ : 0 < γ) (hu : P.u = crraUtility γ) + (hpc : P.PositiveConsumption) (z₁ : Z) {h : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) + (hhinc : h < P.income z₁) + (hgain : (P.income z₁ - h) ^ (-γ) * h < P.discount * (P.cont z₁ h - P.cont z₁ (h / 2))) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + h / 2 ≤ P.policy (a, z₁) := by + refine P.policy_ge_of_cost rfl hpc z₁ hh0 hhcap hhinc (fun R b hm hb0 hbh => ?_) hgain ha + rw [hu] + exact crra_cost_of_saving hγ hh0 hhinc hm hb0 hbh + +section Measure + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- **Positive aggregate capital for CES, from primitives alone.** The log condition +`log (m / (m - h)) < β · P z₁ z₀ · log (1 + R h / income z₀)` becomes +`(m - h) ^ (-γ) · h < β · P z₁ z₀ · (income z₀ + R h) ^ (-γ) · R h`: the saved unit priced at +the margin where consumption is lowest, against its return in the bad state. -/ +theorem crra_aggregateCapital_pos_of_primitives {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ : ℝ} (hp0 : 0 < p₀) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + {h : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hgain : (P.income z₁ - h) ^ (-γ) * h + < P.discount * (P.transitionMatrix z₁ z₀ + * ((P.income z₀ + (1 + P.interest) * h) ^ (-γ) * ((1 + P.interest) * h)))) : + 0 < P.aggregateCapital μ := by + refine P.aggregateCapital_pos_of_cost rfl hμ hp0 hp hh0 hhcap hhinc (fun R hm => ?_) + (lt_of_lt_of_le hgain ?_) + · rw [hu] + simpa using crra_cost_of_saving hγ0 hh0 hhinc hm (le_refl (0 : ℝ)) hh0.le + · exact mul_le_mul_of_nonneg_left (P.crra_cont_sub_ge hγ0 hγ1 hu hpc z₁ z₀ ⟨hh0.le, hhcap⟩) + P.discount.coe_nonneg + +end Measure + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean new file mode 100644 index 000000000..b01277026 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean @@ -0,0 +1,228 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds +public import LeanPool.AiyagariEquilibrium.Models.CRRA + + +/-! +# A consumption floor from the margin, not from the level + +`crra_pinch` traced the restriction `γ = 1` to `tendsto_atBot_u`, the requirement that utility +fall to `-∞` at zero consumption. This file replaces the use of that condition in the one place +where it does economic work — the uniform lower bound on consumption at the optimum — by a +condition on the MARGIN, which CRRA satisfies for every `γ > 0`, `γ < 1` included. + +## Minimum income is necessary and not sufficient + +A positive lower bound on income does not by itself keep consumption away from zero. It bounds +RESOURCES below, not consumption: `maxSaving = min assetCap resources`, so whenever the cap is +slack the household may save everything and consume nothing. Nothing about the income process +says it will not. + +What minimum income does is make the CONTINUATION Lipschitz where the argument needs it. The +deviation argument compares the optimum with saving `h` less: + + `u (c + h) - u c ≤ β * (cont a - cont (a - h))`, + +and the right-hand side has to be bounded by a multiple of `h`. The continuation is concave and +bounded, so its slope on `[0, x]` is at most `2‖V‖ / x` — which blows up as `x → 0`. Minimum +income kills that case: if consumption is small then saving is nearly all of resources, so +`a ≥ 3 minIncome / 4` and the slope constant is `4‖V‖ / minIncome`, depending on the state +through nothing at all. That is `contSlope_le`. + +With a state-independent constant `L`, any consumption level whose marginal value beats `β L` +cannot be optimal, and `MarginalInada` supplies one. The floor is explicit: +`min δ (minIncome / 4)`. + +## What this does NOT do, and why + +The plan was to swap `tendsto_atBot_u` out of the structure. It cannot simply be swapped, and +the reason is worth recording. That condition does a SECOND job: `continuous_rewardFn` uses it +to make `extendBot u` continuous into `EReal`, and continuity of the reward is what Berge's +maximum theorem runs on. With `u (0⁺)` finite, `extendBot u` jumps from a finite limit down to +`⊥` at zero consumption — it is not even upper semicontinuous there, so the maximum is not +attained by the general argument and the whole `ExtendedStochasticProgram` apparatus lapses. + +So admitting `γ < 1` needs the reward to stop being `extendBot u` and start being `u` itself, +real-valued and continuous on `[0, ∞)` — a different reward, hence a different structure, not a +different axiom. The floor proved here is what that structure will need and is stated so that it +transfers: it uses concavity and boundedness of the continuation and nothing else, and in +particular it never mentions `tendsto_atBot_u`. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +/-- **Marginal Inada.** Consumption near zero has unboundedly large marginal value, measured by +secant slopes so that no derivative is needed. CRRA satisfies this for every `γ > 0`, including +the `γ < 1` range where utility itself stays bounded. -/ +def MarginalInadaOn (D : Set ℝ) (u : ℝ → ℝ) : Prop := + ∀ M : ℝ, ∃ δ > 0, ∀ c c' : ℝ, c ∈ D → c < c' → c' ≤ δ → M * (c' - c) ≤ u c' - u c + +/-- The classical form, on the positives. -/ +abbrev MarginalInada (u : ℝ → ℝ) : Prop := MarginalInadaOn (Ioi 0) u + +/-- **A marginal lower bound near zero.** `MarginalInadaOn` is this for every `M`; a utility +with finite marginal value at zero -- CARA, or a shifted CRRA -- satisfies it for the `M` its +slope near zero allows, and the consumption floor then has to be earned by an inequality. -/ +def MarginalBoundOn (D : Set ℝ) (u : ℝ → ℝ) (M : ℝ) : Prop := + ∃ δ > 0, ∀ c c' : ℝ, c ∈ D → c < c' → c' ≤ δ → M * (c' - c) ≤ u c' - u c + +theorem marginalBoundOn_of_inada {D : Set ℝ} {u : ℝ → ℝ} (h : MarginalInadaOn D u) (M : ℝ) : + MarginalBoundOn D u M := h M + +/-- Marginal utility explodes at zero. -/ +theorem tendsto_rpow_neg_atTop {γ : ℝ} (hγ : 0 < γ) : + Tendsto (fun c : ℝ => c ^ (-γ)) (𝓝[>] 0) atTop := by + have hcont : Tendsto (fun c : ℝ => c ^ γ) (𝓝[>] 0) (𝓝 0) := by + have h : Tendsto (fun x : ℝ => x ^ γ) (𝓝[>] 0) (𝓝 ((0 : ℝ) ^ γ)) := + (Real.continuousAt_rpow_const 0 γ (Or.inr hγ.le)).continuousWithinAt + rwa [Real.zero_rpow hγ.ne'] at h + have hwithin : Tendsto (fun c : ℝ => c ^ γ) (𝓝[>] 0) (𝓝[>] 0) := + tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _ hcont + (eventually_nhdsWithin_of_forall fun c hc => Real.rpow_pos_of_pos hc _) + refine (tendsto_inv_nhdsGT_zero.comp hwithin).congr' + (eventually_nhdsWithin_of_forall fun c hc => ?_) + exact (Real.rpow_neg (le_of_lt hc) γ).symm + +theorem rpow_neg_antitone {γ : ℝ} (hγ : 0 < γ) {x y : ℝ} (hx : 0 < x) (hxy : x ≤ y) : + y ^ (-γ) ≤ x ^ (-γ) := by + rw [Real.rpow_neg hx.le, Real.rpow_neg (hx.trans_le hxy).le] + rw [inv_le_inv₀ (Real.rpow_pos_of_pos (hx.trans_le hxy) _) (Real.rpow_pos_of_pos hx _)] + exact Real.rpow_le_rpow hx.le hxy hγ.le + +/-- **CRRA has unbounded marginal value at zero for every `γ > 0`** — the `γ < 1` case included, +where the LEVEL condition `tendsto_atBot_u` fails. This is the whole point. -/ +theorem marginalInada_crraUtility {γ : ℝ} (hγ : 0 < γ) : MarginalInada (crraUtility γ) := by + intro M + obtain ⟨δ, hδM, hδ0⟩ := + (((tendsto_rpow_neg_atTop hγ).eventually_ge_atTop M).and self_mem_nhdsWithin).exists + refine ⟨δ, hδ0, fun c c' hc hcc' hc'δ => ?_⟩ + have hc' : 0 < c' := hc.trans hcc' + obtain ⟨ξ, hξ, hslope⟩ := exists_hasDerivAt_eq_slope (crraUtility γ) + (fun x => x ^ (-γ)) hcc' + ((continuousOn_crraUtility γ).mono fun x hx => lt_of_lt_of_le hc hx.1) + (fun x hx => hasDerivAt_crraUtility γ (hc.trans hx.1)) + have hmono : M ≤ ξ ^ (-γ) := + hδM.trans (rpow_neg_antitone hγ (hc.trans hξ.1) (le_trans hξ.2.le hc'δ)) + rw [hslope, le_div_iff₀ (by linarith)] at hmono + linarith + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The state-independent bound on the continuation's slope, which is where the positive income +floor does its work. -/ +noncomputable def contSlopeConst : ℝ := 4 * ‖P.toExtended.valueFunction‖ / P.minConsumption + +theorem contSlopeConst_nonneg : 0 ≤ P.contSlopeConst := + div_nonneg (by positivity) P.minConsumption_pos.le + +theorem abs_cont_le (z : Z) (x : ℝ) : |P.cont z x| ≤ ‖P.toExtended.valueFunction‖ := + P.abs_expectation_le x z + +/-- **The continuation's slope is bounded by a state-independent constant**, provided we stay +above `assetFloor + minConsumption / 2` in savings — which is exactly where the deviation +argument lives. -/ +theorem cont_sub_le (z : Z) {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.cont z y - P.cont z x ≤ P.contSlopeConst * (y - x) := by + have hmin := P.minConsumption_pos + have hx0 : assetFloor < x := by linarith + have hxmem : x ∈ Icc assetFloor assetCap := ⟨hx0.le, le_trans hxy.le hy.2⟩ + have hslope := (P.concaveOn_cont z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy + have hb1 := abs_le.mp (P.abs_cont_le z x) + have hb2 := abs_le.mp (P.abs_cont_le z assetFloor) + have hhalf : P.contSlopeConst * (P.minConsumption / 2) + = 2 * ‖P.toExtended.valueFunction‖ := by + rw [contSlopeConst]; field_simp; ring + have hbig : P.cont z x - P.cont z assetFloor ≤ P.contSlopeConst * (x - assetFloor) := by + have hstep : P.contSlopeConst * (P.minConsumption / 2) + ≤ P.contSlopeConst * (x - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) P.contSlopeConst_nonneg + rw [hhalf] at hstep + linarith [hb1.2, hb2.1] + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + have hprod : (P.cont z y - P.cont z x) * (x - assetFloor) + ≤ (P.contSlopeConst * (x - assetFloor)) * (y - x) := by + nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] + nlinarith [hprod, hx0] + +/-- **A consumption floor from the margin.** No use is made of `tendsto_atBot_u`: the argument +runs on concavity and boundedness of the continuation, a positive income floor, and unbounded +marginal value at zero consumption. -/ +theorem exists_consumption_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : + ∃ δ > 0, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → δ ≤ P.consumption s (P.policy s) := by + have hmin := P.minConsumption_pos + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + obtain ⟨δ₀, hδ₀, hδ⟩ := hu (P.discount * P.contSlopeConst + 1) + refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun s hs => ?_⟩ + by_contra hlt + rw [not_le] at hlt + set d : ℝ := min δ₀ (P.minConsumption / 4) with hd + set a : ℝ := P.policy s with hadef + set c : ℝ := P.consumption s a with hcdef + have hdδ : d ≤ δ₀ := min_le_left _ _ + have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ + have hcmem : c ∈ P.dom := P.consumption_policy_mem_dom hs + have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem + have hres : P.minConsumption ≤ P.resources s - assetFloor := + P.minConsumption_le_consumption_floor s + have hca : c = P.resources s - a := rfl + -- consumption small forces saving large, which is what bounds the continuation's slope + have ha : assetFloor + 3 * P.minConsumption / 4 ≤ a := by rw [hca] at hlt; linarith + set h : ℝ := d - c with hhdef + have hh0 : 0 < h := by rw [hhdef]; linarith + have hhle : h ≤ P.minConsumption / 4 := by rw [hhdef]; linarith + have hlow : assetFloor + P.minConsumption / 2 ≤ a - h := by linarith + have hamem : a ∈ Icc assetFloor assetCap := P.policy_mem_region s + -- the deviation is feasible and its consumption is `d` + have hfeas : a - h ∈ P.toExtended.feasible s := by + rw [P.feasible_eq] + exact ⟨by linarith [hamem.1], by linarith [(P.policy_mem s).2]⟩ + have hcd : P.consumption s (a - h) = d := by + simp only [consumption] at hca ⊢; linarith + have hdpos : 0 < P.consumption s (a - h) := by rw [hcd]; linarith + -- optimality of `a` against the deviation + have hopt := P.objR_le_of_mem hs hfeas (P.mem_dom_of_pos hdpos) + simp only [objR, hcd] at hopt + -- the continuation cannot repay more than `L * h` + have hslope := P.cont_sub_le s.2 hlow (by linarith) hamem + rw [show a - (a - h) = h from by ring] at hslope + have hscaled := mul_le_mul_of_nonneg_left hslope hβ + have hutil : P.u d - P.u c ≤ (P.discount * P.contSlopeConst) * (d - c) := by + have e : (P.discount : ℝ) * (P.contSlopeConst * h) + = (P.discount * P.contSlopeConst) * (d - c) := by rw [hhdef]; ring + linarith [hopt, hscaled, e.le, e.ge] + -- but the marginal condition says it must repay strictly more + have hmarg := hδ c d hcmem (by linarith) hdδ + rw [show ((P.discount : ℝ) * P.contSlopeConst + 1) * (d - c) + = (P.discount * P.contSlopeConst) * (d - c) + (d - c) from by ring] at hmarg + rw [hhdef] at hh0 + linarith [hmarg, hutil, hh0] + +/-- **The second route to positive consumption.** When utility is unbounded below the reward is +`⊥` at zero consumption and positivity is free; when it is bounded — CES with `γ < 1` — it has to +be earned, and this is where. The condition is on MARGINS, which is why it survives the loss of +the level condition. -/ +theorem positiveConsumption_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : + P.PositiveConsumption := by + obtain ⟨δ, hδ, hspec⟩ := P.exists_consumption_floor_of_marginalInada hu + exact fun s hs => lt_of_lt_of_le hδ (hspec s hs) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean new file mode 100644 index 000000000..cc393ff1b --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean @@ -0,0 +1,542 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Extended +public import LeanPool.AiyagariEquilibrium.Topology.IccCorrespondence +public import Mathlib.Analysis.SpecialFunctions.Pow.Continuity +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.Analysis.Convex.SpecificFunctions.Basic + + +/-! +# Consumption and savings with period utility unbounded below + +Period utility may fall to `-∞` at zero consumption, which covers `σ ≥ 1` in the CES family +and log utility. Assets are capped. + +## This file used to use a floor + +It replaced `u` by `max floor u` to keep the reward real-valued and bounded, and then had +to prove the floor never binds: bounds on the value function above and below, then +`floor_lt_rewardFn_optimal`, then `cFloor_le_consumption_optimal`. Four structure fields +(`floor`, `cFloor`, `u_cFloor_le_floor`, `floor_lt`) and about a hundred and fifty lines of +proof existed only to establish that a device introduced for technical reasons was +invisible in the answer. + +With `LeanEconomics.DynamicProgramming.Extended` the reward is simply `-∞` there. All of +that machinery is gone. What replaces it is a single hypothesis, `tendsto_atBot_u`, saying +utility falls to `-∞` at zero consumption -- which is not an extra assumption at all, since +it is precisely the situation that forced the floor. + +**Positive consumption at the optimum now comes for free.** It used to be the conclusion of +the floor argument. Now it follows from the value function being real: if the optimal +action left zero consumption the reward would be `-∞`, so the value would be `-∞`, and it +is not. That is `exists_optimal_saving`, which returns `0 < consumption` as part of its +conclusion rather than requiring a separate development. + +## What is unchanged + +Consumption is still clamped *above*, and assets are still capped. Neither has anything to +do with `-∞`; both are the ordinary requirement that the reward be bounded above, which the +weighted theory addresses separately. + +Cash on hand is now computed from `max 0 a` rather than `a`. With a floor, a state with +negative cash on hand received the floor value; without one it would receive `-∞`, which +the framework does not allow. Reading assets as nonnegative keeps the value finite +everywhere, and changes nothing on `[0, assetCap]`, which is where the results are stated. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +/-- `c ↦ -1/c`, that is CES utility at `σ = 2`, is strictly concave on the positives. +Mathlib has no convexity lemma for `x⁻¹`, and its `rpow` convexity results cover only +exponents `≥ 1`, so this is proved by hand: the difference of the two sides is +`a * b * (x - y)² / (x * y * (a * x + b * y))`. -/ +theorem strictConcaveOn_neg_inv : StrictConcaveOn ℝ (Ioi 0) (fun c : ℝ => -c⁻¹) := by + refine ⟨convex_Ioi 0, fun x hx y hy hxy a b ha hb hab => ?_⟩ + have hx' : (0 : ℝ) < x := hx + have hy' : (0 : ℝ) < y := hy + have hs : (0 : ℝ) < a * x + b * y := by positivity + have hne : x - y ≠ 0 := sub_ne_zero.mpr hxy + have hpos : 0 < a * b * (x - y) ^ 2 := by positivity + have key : (a * x + b * y)⁻¹ < a * x⁻¹ + b * y⁻¹ := by + rw [← sub_pos] + have hb' : b = 1 - a := by linarith + subst hb' + have heq : a * x⁻¹ + (1 - a) * y⁻¹ - (a * x + (1 - a) * y)⁻¹ + = (a * (1 - a) * (x - y) ^ 2) / (x * y * (a * x + (1 - a) * y)) := by + field_simp + ring + rw [heq] + positivity + simp only [smul_eq_mul] + linarith + +/-- A consumption-savings problem whose period utility is unbounded below. -/ +structure ConsumptionSavingsUnbounded where + /-- Constant labour income. -/ + income : ℝ + /-- The interest rate on assets. -/ + interest : ℝ + /-- The upper bound imposed on asset holdings. -/ + assetCap : ℝ + /-- The discount factor. -/ + discount : ℝ≥0 + /-- Period utility, required to behave only on positive consumption. -/ + u : ℝ → ℝ + income_pos : 0 < income + interest_gt_neg_one : 0 < 1 + interest + assetCap_nonneg : 0 ≤ assetCap + discount_lt_one : discount < 1 + continuousOn_u : ContinuousOn u (Ioi 0) + monotoneOn_u : MonotoneOn u (Ioi 0) + /-- Utility falls to `-∞` as consumption vanishes. This single hypothesis replaces the + four fields the floored version needed. -/ + tendsto_atBot_u : Tendsto u (𝓝[>] 0) atBot + /-- Diminishing marginal utility. Needed for a *unique* optimal policy, hence for an + agent distribution to be a well-defined object. -/ + strictConcaveOn_u : StrictConcaveOn ℝ (Ioi 0) u + +namespace ConsumptionSavingsUnbounded + +variable (P : ConsumptionSavingsUnbounded) + +/-- Cash on hand, with assets read as nonnegative. -/ +noncomputable def resources (a : ℝ) : ℝ := P.income + (1 + P.interest) * max 0 a + +/-- The largest consumption the capped problem allows. -/ +noncomputable def maxConsumption : ℝ := P.income + (1 + P.interest) * P.assetCap + +/-- The largest asset holding that can be carried forward. -/ +noncomputable def maxSaving (a : ℝ) : ℝ := max 0 (min P.assetCap (P.resources a)) + +/-- Consumption on the budget line. -/ +noncomputable def consumption (a a' : ℝ) : ℝ := P.resources a - a' + +/-- The reward: utility of consumption clamped above, and `-∞` where consumption vanishes. +No floor. -/ +noncomputable def rewardFn (p : ℝ × ℝ) : EReal := + extendBot P.u (min P.maxConsumption (P.consumption p.1 p.2)) + +theorem income_le_resources (a : ℝ) : P.income ≤ P.resources a := by + have : 0 ≤ (1 + P.interest) * max 0 a := + mul_nonneg P.interest_gt_neg_one.le (le_max_left _ _) + simp only [resources]; linarith + +theorem resources_pos (a : ℝ) : 0 < P.resources a := + lt_of_lt_of_le P.income_pos (P.income_le_resources a) + +theorem income_le_maxConsumption : P.income ≤ P.maxConsumption := by + have : 0 ≤ (1 + P.interest) * P.assetCap := + mul_nonneg P.interest_gt_neg_one.le P.assetCap_nonneg + simp only [maxConsumption]; linarith + +theorem maxConsumption_pos : 0 < P.maxConsumption := + lt_of_lt_of_le P.income_pos P.income_le_maxConsumption + +theorem continuous_resources : Continuous P.resources := + continuous_const.add (continuous_const.mul (continuous_const.max continuous_id)) + +theorem continuous_maxSaving : Continuous P.maxSaving := + continuous_const.max (continuous_const.min P.continuous_resources) + +theorem continuous_rewardFn : Continuous P.rewardFn := by + refine (continuous_extendBot P.continuousOn_u P.tendsto_atBot_u).comp + (continuous_const.min ?_) + exact (P.continuous_resources.comp continuous_fst).sub continuous_snd + +/-- The problem as a dynamic program with an extended-real reward. -/ +noncomputable def toExtended : ExtendedProgram ℝ ℝ where + feasible a := Icc 0 (P.maxSaving a) + isCompact_feasible _ := isCompact_Icc + feasible_nonempty _ := nonempty_Icc.mpr (le_max_left _ _) + upperHemicontinuous_feasible := upperHemicontinuous_Icc continuous_const P.continuous_maxSaving + lowerHemicontinuous_feasible := + lowerHemicontinuous_Icc continuous_const P.continuous_maxSaving fun _ => le_max_left _ _ + reward := ⟨P.rewardFn, P.continuous_rewardFn⟩ + rewardMax := P.u P.maxConsumption + reward_le := by + intro s a _ + rcases le_or_gt (min P.maxConsumption (P.consumption s a)) 0 with h | h + · simp only [ContinuousMap.coe_mk, rewardFn] + rw [extendBot_of_nonpos h] + exact bot_le + · simp only [ContinuousMap.coe_mk, rewardFn] + rw [extendBot_of_pos h, EReal.coe_le_coe_iff] + exact P.monotoneOn_u h P.maxConsumption_pos (min_le_left _ _) + select := ⟨fun _ => 0, continuous_const⟩ + select_mem := fun _ => ⟨le_rfl, le_max_left _ _⟩ + rewardMin := P.u P.income + le_reward_select := by + intro s + have h : P.income ≤ min P.maxConsumption (P.consumption s 0) := by + refine le_min P.income_le_maxConsumption ?_ + simp only [consumption, sub_zero] + exact P.income_le_resources s + simp only [ContinuousMap.coe_mk, rewardFn] + rw [extendBot_of_pos (lt_of_lt_of_le P.income_pos h), EReal.coe_le_coe_iff] + exact P.monotoneOn_u P.income_pos (lt_of_lt_of_le P.income_pos h) h + transition := ⟨fun p => p.2, continuous_snd⟩ + discount := P.discount + discount_lt_one := P.discount_lt_one + +@[simp] +theorem feasible_eq (a : ℝ) : P.toExtended.feasible a = Icc 0 (P.maxSaving a) := rfl + +theorem consumption_le_maxConsumption {a a' : ℝ} (ha : a ∈ Icc 0 P.assetCap) (ha' : 0 ≤ a') : + P.consumption a a' ≤ P.maxConsumption := by + have hmax : max 0 a = a := max_eq_right ha.1 + have : (1 + P.interest) * a ≤ (1 + P.interest) * P.assetCap := + mul_le_mul_of_nonneg_left ha.2 P.interest_gt_neg_one.le + simp only [consumption, resources, maxConsumption, hmax] + linarith + +/-- **The Bellman equation, with honest utility and positive consumption.** That +consumption is positive at the optimum is not assumed and not separately proved: a zero +would make the reward `-∞` and hence the value `-∞`, and the value is real. -/ +theorem exists_optimal_saving {a : ℝ} (ha : a ∈ Icc 0 P.assetCap) : + ∃ a' ∈ Icc 0 (P.maxSaving a), 0 < P.consumption a a' ∧ + P.toExtended.valueFunction a + = P.u (P.consumption a a') + P.discount * P.toExtended.valueFunction a' := by + obtain ⟨a', ha', heq⟩ := P.toExtended.exists_optimal_policy a + -- the reward at the optimum cannot be `⊥`, or the value would not be real + have hne : P.rewardFn (a, a') ≠ ⊥ := by + intro hb + rw [show P.toExtended.reward (a, a') = P.rewardFn (a, a') from rfl, hb, + EReal.bot_add _] at heq + exact EReal.coe_ne_bot _ heq + have hpos : 0 < min P.maxConsumption (P.consumption a a') := by + by_contra hle + push Not at hle + exact hne (extendBot_of_nonpos hle) + have hc : 0 < P.consumption a a' := lt_of_lt_of_le hpos (min_le_right _ _) + have hclamp : min P.maxConsumption (P.consumption a a') = P.consumption a a' := + min_eq_right (P.consumption_le_maxConsumption ha ha'.1) + refine ⟨a', ha', hc, ?_⟩ + have hrw : P.toExtended.reward (a, a') = ((P.u (P.consumption a a') : ℝ) : EReal) := + calc P.toExtended.reward (a, a') + = extendBot P.u (min P.maxConsumption (P.consumption a a')) := rfl + _ = extendBot P.u (P.consumption a a') := by rw [hclamp] + _ = _ := extendBot_of_pos hc + rw [hrw, ← EReal.coe_add, EReal.coe_eq_coe_iff] at heq + exact heq + +/-! ### Concavity and the optimal policy + +The `σ < 1` model gets concavity from the general criterion in +`LeanEconomics.DynamicProgramming.Bellman`. That criterion is stated for a real-valued +reward and does not apply here, because `ConcaveOn` over `EReal` would need an ordered +module structure that does not sit well. The way round is that the `-∞` never appears where +it matters: at an optimum consumption is strictly positive, so every value the argument +touches is finite, and the reasoning can be carried out in the reals. -/ + +theorem resources_eq_affine {x : ℝ} (hx : 0 ≤ x) : + P.resources x = P.income + (1 + P.interest) * x := by + simp only [resources, max_eq_right hx] + +theorem resources_affine_comb {x y θ φ : ℝ} (hx : 0 ≤ x) (hy : 0 ≤ y) (hθ : 0 ≤ θ) + (hφ : 0 ≤ φ) (hθφ : θ + φ = 1) : + P.resources (θ * x + φ * y) = θ * P.resources x + φ * P.resources y := by + have hmix : 0 ≤ θ * x + φ * y := by positivity + rw [P.resources_eq_affine hmix, P.resources_eq_affine hx, P.resources_eq_affine hy] + linear_combination (-P.income) * hθφ + +theorem maxSaving_le_assetCap (a : ℝ) : P.maxSaving a ≤ P.assetCap := + max_le P.assetCap_nonneg (min_le_left _ _) + +theorem feasible_subset_region {x a : ℝ} (ha : a ∈ P.toExtended.feasible x) : + a ∈ Icc 0 P.assetCap := + ⟨ha.1, ha.2.trans (P.maxSaving_le_assetCap x)⟩ + +theorem maxSaving_eq (x : ℝ) : P.maxSaving x = min P.assetCap (P.resources x) := + max_eq_right (le_min P.assetCap_nonneg (P.resources_pos x).le) + +theorem feasible_convex {x y : ℝ} (hx : x ∈ Icc 0 P.assetCap) (hy : y ∈ Icc 0 P.assetCap) + {ax ay θ φ : ℝ} (hax : ax ∈ P.toExtended.feasible x) (hay : ay ∈ P.toExtended.feasible y) + (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) (hθφ : θ + φ = 1) : + θ * ax + φ * ay ∈ P.toExtended.feasible (θ * x + φ * y) := by + obtain ⟨hax0, haxm⟩ := hax + obtain ⟨hay0, haym⟩ := hay + rw [P.maxSaving_eq x] at haxm + rw [P.maxSaving_eq y] at haym + have hmix : (0 : ℝ) ≤ θ * x + φ * y := + add_nonneg (mul_nonneg hθ hx.1) (mul_nonneg hφ hy.1) + refine ⟨add_nonneg (mul_nonneg hθ hax0) (mul_nonneg hφ hay0), ?_⟩ + rw [P.maxSaving_eq (θ * x + φ * y)] + refine le_min ?_ ?_ + · have h1 : ax ≤ P.assetCap := haxm.trans (min_le_left _ _) + have h2 : ay ≤ P.assetCap := haym.trans (min_le_left _ _) + nlinarith + · have h1 : ax ≤ P.resources x := haxm.trans (min_le_right _ _) + have h2 : ay ≤ P.resources y := haym.trans (min_le_right _ _) + rw [P.resources_affine_comb hx.1 hy.1 hθ hφ hθφ] + nlinarith + +/-- A finite reward means strictly positive consumption. -/ +theorem consumption_pos_of_ne_bot {x a : ℝ} (h : P.toExtended.reward (x, a) ≠ ⊥) : + 0 < P.consumption x a := by + by_contra hle + push Not at hle + refine h ?_ + calc P.toExtended.reward (x, a) + = extendBot P.u (min P.maxConsumption (P.consumption x a)) := rfl + _ = ⊥ := extendBot_of_nonpos ((min_le_right _ _).trans hle) + +/-- Where consumption is positive the reward is an ordinary real utility. -/ +theorem reward_eq_coe {x a : ℝ} (hx : x ∈ Icc 0 P.assetCap) (ha : a ∈ P.toExtended.feasible x) + (hc : 0 < P.consumption x a) : + P.toExtended.reward (x, a) = ((P.u (P.consumption x a) : ℝ) : EReal) := + calc P.toExtended.reward (x, a) + = extendBot P.u (min P.maxConsumption (P.consumption x a)) := rfl + _ = extendBot P.u (P.consumption x a) := by + rw [min_eq_right (P.consumption_le_maxConsumption hx ha.1)] + _ = _ := extendBot_of_pos hc + +/-- At an optimum consumption is positive and the value is an ordinary real expression. -/ +theorem bellmanFn_eq_of_optimal {v : ℝ →ᵇ ℝ} {x a : ℝ} (hx : x ∈ Icc 0 P.assetCap) + (ha : a ∈ P.toExtended.feasible x) + (heq : P.toExtended.objectiveE v x a = ((P.toExtended.bellmanFn v x : ℝ) : EReal)) : + 0 < P.consumption x a ∧ + P.toExtended.bellmanFn v x = P.u (P.consumption x a) + P.discount * v a := by + have hne : P.toExtended.reward (x, a) ≠ ⊥ := by + intro hb + rw [ExtendedProgram.objectiveE, hb, EReal.bot_add _] at heq + exact EReal.coe_ne_bot _ heq.symm + have hc := P.consumption_pos_of_ne_bot hne + refine ⟨hc, ?_⟩ + rw [ExtendedProgram.objectiveE, P.reward_eq_coe hx ha hc, ← EReal.coe_add, + EReal.coe_eq_coe_iff] at heq + exact heq.symm + +/-- **The Bellman operator preserves concavity on the region.** -/ +theorem concaveOn_bellman (v : ℝ →ᵇ ℝ) (hv : ConcaveOn ℝ (Icc 0 P.assetCap) ⇑v) : + ConcaveOn ℝ (Icc 0 P.assetCap) ⇑(P.toExtended.bellman v) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + obtain ⟨ax, hax, heqx⟩ := P.toExtended.exists_optimal_action v x + obtain ⟨ay, hay, heqy⟩ := P.toExtended.exists_optimal_action v y + obtain ⟨hcx, hbx⟩ := P.bellmanFn_eq_of_optimal hx hax heqx + obtain ⟨hcy, hby⟩ := P.bellmanFn_eq_of_optimal hy hay heqy + have hxy : θ * x + φ * y ∈ Icc 0 P.assetCap := by + simpa using convex_Icc (0 : ℝ) P.assetCap hx hy hθ hφ hθφ + have hmix := P.feasible_convex hx hy hax hay hθ hφ hθφ + have hcmix : P.consumption (θ * x + φ * y) (θ * ax + φ * ay) + = θ * P.consumption x ax + φ * P.consumption y ay := by + simp only [consumption] + rw [P.resources_affine_comb hx.1 hy.1 hθ hφ hθφ] + ring + have hcm : 0 < P.consumption (θ * x + φ * y) (θ * ax + φ * ay) := by + rw [hcmix] + rcases lt_or_eq_of_le hθ with h | h + · exact add_pos_of_pos_of_nonneg (mul_pos h hcx) (mul_nonneg hφ hcy.le) + · have hφ1 : φ = 1 := by linarith + rw [← h, hφ1] + simpa using hcy + -- the mixed action is available, so it bounds the mixed value from below + have hle := P.toExtended.le_bellmanFn v hmix + have hobj : P.toExtended.objectiveE v (θ * x + φ * y) (θ * ax + φ * ay) + = ((P.u (P.consumption (θ * x + φ * y) (θ * ax + φ * ay)) + + P.discount * v (θ * ax + φ * ay) : ℝ) : EReal) := by + rw [ExtendedProgram.objectiveE, P.reward_eq_coe hxy hmix hcm, ← EReal.coe_add] + rfl + rw [hobj] at hle + have hle' : P.u (P.consumption (θ * x + φ * y) (θ * ax + φ * ay)) + + P.discount * v (θ * ax + φ * ay) ≤ P.toExtended.bellmanFn v (θ * x + φ * y) := by + exact_mod_cast hle + -- concavity of utility and of the continuation value + have hu := P.strictConcaveOn_u.concaveOn.2 hcx hcy hθ hφ hθφ + have hvv := hv.2 (P.feasible_subset_region hax) (P.feasible_subset_region hay) hθ hφ hθφ + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hvv hβ + simp only [smul_eq_mul] at hu hvv hscaled + rw [hcmix] at hle' + simp only [smul_eq_mul, ExtendedProgram.bellman_apply] + rw [hbx, hby] + linarith + +/-- **The value function is concave on `[0, assetCap]`**, for utility unbounded below. -/ +theorem concaveOn_valueFunction : + ConcaveOn ℝ (Icc 0 P.assetCap) ⇑P.toExtended.valueFunction := + Blackwell.concaveOn_valueFunction (convex_Icc _ _) P.toExtended.blackwell + P.toExtended.discount_lt_one fun v hv => P.concaveOn_bellman v hv + +/-- **The optimal action is unique.** Two distinct optimal actions would be beaten by their +midpoint, since utility is strictly concave and the continuation value concave. -/ +theorem optimal_action_unique {x : ℝ} (hx : x ∈ Icc 0 P.assetCap) {a₀ a₁ : ℝ} + (h₀ : a₀ ∈ P.toExtended.feasible x) (h₁ : a₁ ∈ P.toExtended.feasible x) + (hm₀ : P.toExtended.objectiveE P.toExtended.valueFunction x a₀ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction x : ℝ) : EReal)) + (hm₁ : P.toExtended.objectiveE P.toExtended.valueFunction x a₁ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction x : ℝ) : EReal)) : + a₀ = a₁ := by + by_contra hne + obtain ⟨hc₀, hb₀⟩ := P.bellmanFn_eq_of_optimal hx h₀ hm₀ + obtain ⟨hc₁, hb₁⟩ := P.bellmanFn_eq_of_optimal hx h₁ hm₁ + have hhalf : (0 : ℝ) < 1 / 2 := by norm_num + have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num + have hmem : (1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁ ∈ P.toExtended.feasible x := by + simpa using convex_Icc (0 : ℝ) (P.maxSaving x) h₀ h₁ hhalf.le hhalf.le hsum + have hcmid : P.consumption x ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = (1 / 2 : ℝ) * P.consumption x a₀ + (1 / 2 : ℝ) * P.consumption x a₁ := by + simp only [consumption]; ring + have hcm : 0 < P.consumption x ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) := by + rw [hcmid]; linarith + have hcne : P.consumption x a₀ ≠ P.consumption x a₁ := by + simp only [consumption] + intro h + exact hne (by linarith) + -- strict gain at the midpoint + have hu := P.strictConcaveOn_u.2 hc₀ hc₁ hcne hhalf hhalf hsum + have hvv := P.concaveOn_valueFunction.2 (P.feasible_subset_region h₀) + (P.feasible_subset_region h₁) hhalf.le hhalf.le hsum + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hvv hβ + simp only [smul_eq_mul] at hu hvv hscaled + have hle := P.toExtended.le_bellmanFn P.toExtended.valueFunction hmem + have hobj : P.toExtended.objectiveE P.toExtended.valueFunction x + ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = ((P.u (P.consumption x ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + : ℝ) : EReal) := by + rw [ExtendedProgram.objectiveE, P.reward_eq_coe hx hmem hcm, ← EReal.coe_add] + rfl + rw [hobj] at hle + have hle' : P.u (P.consumption x ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + ≤ P.toExtended.bellmanFn P.toExtended.valueFunction x := by exact_mod_cast hle + rw [hcmid] at hle' + linarith + +/-- The optimal saving choice. By `optimal_action_unique` this is *the* optimal choice on +the region, not merely *an* optimal choice. -/ +noncomputable def policy (x : ℝ) : ℝ := + Classical.choose (P.toExtended.exists_optimal_action P.toExtended.valueFunction x) + +theorem policy_mem (x : ℝ) : P.policy x ∈ P.toExtended.feasible x := + (Classical.choose_spec + (P.toExtended.exists_optimal_action P.toExtended.valueFunction x)).1 + +theorem policy_optimal (x : ℝ) : + P.toExtended.objectiveE P.toExtended.valueFunction x (P.policy x) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction x : ℝ) : EReal) := + (Classical.choose_spec + (P.toExtended.exists_optimal_action P.toExtended.valueFunction x)).2 + +theorem policy_mem_region (x : ℝ) : P.policy x ∈ Icc 0 P.assetCap := + P.feasible_subset_region (P.policy_mem x) + +/-- Consumption under the optimal policy is strictly positive: the household never starves +itself. With a floor this had to be proved; here it follows from the value being real. -/ +theorem consumption_policy_pos {x : ℝ} (hx : x ∈ Icc 0 P.assetCap) : + 0 < P.consumption x (P.policy x) := + (P.bellmanFn_eq_of_optimal hx (P.policy_mem x) (P.policy_optimal x)).1 + +/-- **Any optimal action is the policy.** -/ +theorem eq_policy_of_optimal {x : ℝ} (hx : x ∈ Icc 0 P.assetCap) {a : ℝ} + (ha : a ∈ P.toExtended.feasible x) + (hopt : P.toExtended.objectiveE P.toExtended.valueFunction x a + = ((P.toExtended.bellmanFn P.toExtended.valueFunction x : ℝ) : EReal)) : + a = P.policy x := + P.optimal_action_unique hx ha (P.policy_mem x) hopt (P.policy_optimal x) + +/-- **The Bellman equation at the policy**, with honest utility and positive consumption. -/ +theorem valueFunction_eq_policy {x : ℝ} (hx : x ∈ Icc 0 P.assetCap) : + P.toExtended.valueFunction x + = P.u (P.consumption x (P.policy x)) + + P.discount * P.toExtended.valueFunction (P.policy x) := by + have h := (P.bellmanFn_eq_of_optimal hx (P.policy_mem x) (P.policy_optimal x)).2 + have hfix : P.toExtended.bellmanFn P.toExtended.valueFunction x + = P.toExtended.valueFunction x := by + conv_rhs => rw [← P.toExtended.bellman_valueFunction] + rfl + rw [← hfix, h] + +/-- **The maximiser set is exactly the policy.** -/ +theorem argmax_eq_singleton {x : ℝ} (hx : x ∈ Icc 0 P.assetCap) : + argmax (P.toExtended.objectiveE P.toExtended.valueFunction) P.toExtended.feasible x + = {P.policy x} := by + ext a + simp only [argmax, Set.mem_ofPred_eq, Set.mem_singleton_iff] + constructor + · rintro ⟨ha, hmax⟩ + refine P.eq_policy_of_optimal hx ha (le_antisymm (P.toExtended.le_bellmanFn _ ha) ?_) + rw [← P.policy_optimal x] + exact isMaxOn_iff.mp hmax _ (P.policy_mem x) + · rintro rfl + refine ⟨P.policy_mem x, isMaxOn_iff.mpr fun b hb => ?_⟩ + rw [P.policy_optimal x] + exact P.toExtended.le_bellmanFn _ hb + +/-- **The optimal policy is continuous on the region**, for utility unbounded below. -/ +theorem continuousOn_policy : ContinuousOn P.policy (Icc 0 P.assetCap) := + continuousOn_of_upperHemicontinuous_singleton + (P.toExtended.upperHemicontinuous_argmax P.toExtended.valueFunction) + fun _ hx => P.argmax_eq_singleton hx + +/-! ### Utilities that qualify -/ + +/-- CES period utility. -/ +noncomputable def crra (σ c : ℝ) : ℝ := c ^ (1 - σ) / (1 - σ) + +theorem continuousOn_crra {σ : ℝ} : ContinuousOn (crra σ) (Ioi 0) := fun c hc => + (((Real.continuousAt_rpow_const c (1 - σ) (Or.inl (ne_of_gt hc))).div_const _)).continuousWithinAt + +theorem monotoneOn_crra {σ : ℝ} (hσ : 1 < σ) : MonotoneOn (crra σ) (Ioi 0) := by + intro x hx y _ hxy + have hneg : (1 : ℝ) - σ < 0 := by linarith + simp only [crra] + rw [div_le_div_right_of_neg hneg] + exact Real.rpow_le_rpow_of_nonpos hx hxy hneg.le + +theorem continuousOn_log : ContinuousOn Real.log (Ioi 0) := + Real.continuousOn_log.mono fun _ hx => ne_of_gt hx + +theorem monotoneOn_log : MonotoneOn Real.log (Ioi 0) := Real.strictMonoOn_log.monotoneOn + +/-- A calibration with `σ = 2`, where CES utility is `-1 / c`. -/ +noncomputable def calibrated : ConsumptionSavingsUnbounded where + income := 1 + interest := 1 / 20 + assetCap := 10 + discount := 24 / 25 + u := fun c => -c⁻¹ + income_pos := by norm_num + interest_gt_neg_one := by norm_num + assetCap_nonneg := by norm_num + discount_lt_one := by norm_num + continuousOn_u := (continuousOn_id.inv₀ fun x hx => ne_of_gt hx).neg + monotoneOn_u := by + intro x hx y _ hxy + have : (0 : ℝ) < x := hx + simp only [neg_le_neg_iff] + gcongr + tendsto_atBot_u := tendsto_neg_atTop_atBot.comp tendsto_inv_nhdsGT_zero + strictConcaveOn_u := strictConcaveOn_neg_inv + +/-- A log calibration (`σ = 1`). -/ +noncomputable def calibratedLog : ConsumptionSavingsUnbounded where + income := 1 + interest := 1 / 20 + assetCap := 10 + discount := 24 / 25 + u := Real.log + income_pos := by norm_num + interest_gt_neg_one := by norm_num + assetCap_nonneg := by norm_num + discount_lt_one := by norm_num + continuousOn_u := continuousOn_log + monotoneOn_u := monotoneOn_log + tendsto_atBot_u := Real.tendsto_log_nhdsGT_zero + strictConcaveOn_u := strictConcaveOn_log_Ioi + +end ConsumptionSavingsUnbounded + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean new file mode 100644 index 000000000..8857c0dd5 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline + + +/-! +# The corner condition from the Euler inequality + +`policy_eq_zero_of_corner_at` gets the corner from a LIPSCHITZ bound on the value function, +`β L < m`. The constant `L` is `slopeBound / (1 - βR)`, and at Aiyagari's `β = 0.96` that is +several hundred times the true slope, so the test fails by orders of magnitude. This file gets the +corner from the Euler inequality instead, and the only thing it asks is `βR < 1`. + +## The argument + +Two steps, both at the fixed point. + +**Nobody at the borrowing limit consumes less than the worst income.** Take the income state at +which consumption at the limit is smallest. If that household were saving, its Euler inequality +(`euler_ge`) would read `u'(c) ≤ βR E[u'(c')]`; but tomorrow's consumption is at least today's +minimum (consumption rises with assets, and assets can only have risen), so `u'(c') ≤ u'(c)` and +`u'(c) ≤ βR u'(c)`, impossible with `βR < 1`. So the household at the minimum is at the corner, +consumes its whole income, and the minimum is at least `minIncome`. + +**The corner, state by state.** At any state where `βR · u'(minIncome) < u'(cash on hand)`, +saving is not optimal: if it were, the Euler inequality and step one would give +`u'(cash on hand) ≤ u'(c) ≤ βR u'(minIncome)`. This is the contrapositive of `euler_ge`. + +For log utility the test at the worst income state is `a < minIncome (1 - βR) / (βR²)`: a small +but positive corner region, at every `β < 1/R`. Compare Clarida (1987), who has the corner on +`[ε - rφ, w_bar]` under `u'(0) < ∞`; here nothing is assumed about `u'(0)`. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **At the borrowing limit, nobody consumes less than the worst income.** The household at the +minimum is at the corner — otherwise its own Euler inequality would push it up by `1/βR`. -/ +theorem minIncome_le_consumptionFn_floor + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + (z : Z) : P.minIncome + P.interest * assetFloor ≤ P.consumptionFn z assetFloor := by + have hfl : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + -- the state at which consumption at the limit is smallest + obtain ⟨z₀, -, hz₀⟩ := Finset.exists_min_image Finset.univ + (fun z' => P.consumptionFn z' assetFloor) Finset.univ_nonempty + have hmin : ∀ z', P.consumptionFn z₀ assetFloor ≤ P.consumptionFn z' assetFloor := + fun z' => hz₀ z' (Finset.mem_univ _) + -- it is at the corner + have hcorner : P.policy (assetFloor, z₀) = assetFloor := by + by_contra hne + set A : ℝ := P.policy (assetFloor, z₀) with hA + have hAmem : A ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policy_mem _) + have hA0 : assetFloor < A := lt_of_le_of_ne hAmem.1 (Ne.symm hne) + have hbv := P.toExtended.bellman_valueFunction + have hc : 0 < P.consumptionFn z₀ assetFloor := hpos z₀ _ hfl + have hE := P.euler_ge (v := P.toExtended.valueFunction) (z := z₀) (a := assetFloor) (A := A) + (du := du (P.consumptionFn z₀ assetFloor)) (du' := fun z' => du (P.consumptionFn z' A)) + hfl (by rw [hbv, hA]; exact congrFun P.policyOf_valueFunction (assetFloor, z₀)) hA0 + (fun z' => hslack z' A hAmem) (by rw [hbv]; exact hc) + (by rw [hbv]; exact hderiv _ hc) (fun z' => hpos z' A hAmem) + (fun z' => hderiv _ (hpos z' A hAmem)) + -- tomorrow's consumption is at least today's minimum, so marginal utility is at most today's + have hbound : ∀ z', du (P.consumptionFn z' A) ≤ du (P.consumptionFn z₀ assetFloor) := + fun z' => hanti (mem_Ioi.mpr hc) (mem_Ioi.mpr (hpos z' A hAmem)) + (le_trans (hmin z') (P.consumptionFn_mono hfl hAmem hAmem.1)) + have hsum : ∑ z' : Z, P.transitionMatrix z₀ z' * du (P.consumptionFn z' A) + ≤ du (P.consumptionFn z₀ assetFloor) := by + refine le_trans (Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (hbound z') (P.transitionMatrix_nonneg z₀ z')) ?_ + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + have hdu : 0 < du (P.consumptionFn z₀ assetFloor) := hdupos _ hc + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * (1 + P.interest) := mul_nonneg hβ hR.le + have h2 := mul_le_mul_of_nonneg_left hsum hcoef + nlinarith [hE, h2, hdu, hβR] + -- so it consumes its whole income + have hres : P.resources (assetFloor, z₀) = P.income z₀ + (1 + P.interest) * assetFloor := by + simp only [resources, max_self] + have hc₀ : P.consumptionFn z₀ assetFloor = P.income z₀ + P.interest * assetFloor := by + simp only [consumptionFn, consumption, hcorner, hres]; ring + have := P.minIncome_le z₀ + linarith [hmin z, hc₀] + +/-- **The corner from the Euler inequality.** Wherever `βR · u'(minIncome + r·floor)` is below the +marginal utility of cash on hand, the household saves nothing. The contrapositive of `euler_ge`, +with `minIncome_le_consumptionFn_floor` bounding tomorrow's marginal utility. + +Nothing here depends on how large `β` is beyond `βR < 1`; that is what makes it usable at +`β = 0.96`, where the Lipschitz corner `policy_eq_zero_of_corner_at` is hopeless. -/ +theorem policy_eq_floor_of_euler + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) + (hcond : (P.discount : ℝ) * (1 + P.interest) * du (P.minIncome + P.interest * assetFloor) + < du (P.resources (a, z) - assetFloor)) : + P.policy (a, z) = assetFloor := by + by_contra hne + set A : ℝ := P.policy (a, z) with hA + have hAmem : A ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policy_mem _) + have hA0 : assetFloor < A := lt_of_le_of_ne hAmem.1 (Ne.symm hne) + have hbv := P.toExtended.bellman_valueFunction + have hc : 0 < P.consumptionFn z a := hpos z a ha + have hE := P.euler_ge (v := P.toExtended.valueFunction) (z := z) (a := a) (A := A) + (du := du (P.consumptionFn z a)) (du' := fun z' => du (P.consumptionFn z' A)) + ha (by rw [hbv, hA]; exact congrFun P.policyOf_valueFunction (a, z)) hA0 + (fun z' => hslack z' A hAmem) (by rw [hbv]; exact hc) + (by rw [hbv]; exact hderiv _ hc) (fun z' => hpos z' A hAmem) + (fun z' => hderiv _ (hpos z' A hAmem)) + -- the worst income bounds tomorrow's marginal utility + have hy : 0 < P.minIncome + P.interest * assetFloor := by + have h1 := P.minConsumption_le_floor + have h2 := P.minConsumption_pos + linarith + have hfl : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + have hbound : ∀ z', du (P.consumptionFn z' A) ≤ du (P.minIncome + P.interest * assetFloor) := + fun z' => hanti (mem_Ioi.mpr hy) (mem_Ioi.mpr (hpos z' A hAmem)) + (le_trans (P.minIncome_le_consumptionFn_floor hβR hderiv hanti hdupos hpos hslack z') + (P.consumptionFn_mono hfl hAmem hAmem.1)) + have hsum : ∑ z' : Z, P.transitionMatrix z z' * du (P.consumptionFn z' A) + ≤ du (P.minIncome + P.interest * assetFloor) := by + refine le_trans (Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (hbound z') (P.transitionMatrix_nonneg z z')) ?_ + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + -- and cash on hand bounds today's from below + have hcle : P.consumptionFn z a ≤ P.resources (a, z) - assetFloor := by + simp only [consumptionFn, consumption]; linarith [hAmem.1] + have hres0 : 0 < P.resources (a, z) - assetFloor := lt_of_lt_of_le hc hcle + have htoday : du (P.resources (a, z) - assetFloor) ≤ du (P.consumptionFn z a) := + hanti (mem_Ioi.mpr hc) (mem_Ioi.mpr hres0) hcle + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * (1 + P.interest) := mul_nonneg hβ hR.le + have h2 := mul_le_mul_of_nonneg_left hsum hcoef + linarith [hE, h2, htoday, hcond] + +/-! ### CRRA and log -/ + +/-- **The Euler corner for CRRA**, `u' c = c ^ (-γ)`, at a zero borrowing limit. The test at +state `z` is `βR · minIncome^(-γ) < resources^(-γ)`, i.e. +`resources (a, z) < minIncome · (βR)^(-1/γ)`. -/ +theorem crra_policy_eq_zero_of_euler {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ (z : Z), ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc (0 : ℝ) assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) + (hcond : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < P.resources (a, z) ^ (-γ)) : + P.policy (a, z) = 0 := by + subst hfl + refine P.policy_eq_floor_of_euler hβR (du := fun c => c ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_crraUtility γ hc) + (fun _ hx _ _ hxy => Real.rpow_le_rpow_of_nonpos hx hxy (by linarith)) + (fun c hc => Real.rpow_pos_of_pos hc _) hpos hslack ha z ?_ + simpa using hcond + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean new file mode 100644 index 000000000..d67747e98 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean @@ -0,0 +1,243 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate +public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness + + +/-! +# Açıkgöz (2018) Proposition 4: the natural asset bound from impatience alone + +`NaturalAssetBound` gets assets to decline from a LINEAR lower bound on consumption, which it +can supply only for log utility, and with the very lossy constant `exp (-2 β ‖V‖)`. Its own +docstring says what the sharp argument would need: "Getting it needs the Euler equation, hence +the envelope condition, hence the machinery that `IncomeFluctuationEnvelope` supplies only under +side conditions." That machinery now exists (`hasDerivAt_bellman`, `euler_ge`), and with it the +decline condition is not a calibration at all — it is exactly impatience. + +## The argument, in one line + +Let `z` be the income state at which consumption is LOWEST, and suppose the household does not +decumulate there: `A = g(a, z) ≥ a`. The Euler inequality from saving less is + + `u'(c(a, z)) ≤ β R · Σ_{z'} π_{z z'} u'(c(A, z'))`. + +Consumption rises with assets and `A ≥ a`, so `c(A, z') ≥ c(a, z') ≥ c(a, z)`, and marginal +utility falls, so every term on the right is at most `u'(c(a, z))`. The weights sum to one. +Hence `u'(c(a,z)) ≤ β R · u'(c(a,z))`, and since marginal utility is positive, `1 ≤ β R`. + +So `β R < 1` forces decline. No limits, no asymptotic marginal propensity to consume, no +calibration — and nothing is asked of the utility beyond a positive, decreasing derivative. + +## What it costs + +Only the Euler inequality's own side conditions: consumption positive at every optimum and the +saving strictly below its cap (`hslack`), which is `policyOf_lt_maxSaving` once the asset cap is +slack. Note the direction — `euler_ge` comes from the deviation "save LESS", so it needs room +BELOW, which is exactly what `A ≥ a > assetFloor` provides. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **Impatience forces decumulation at a state where consumption is lowest.** -/ +theorem policy_lt_self_of_impatient + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha0 : assetFloor < a) {z : Z} + (hmin : ∀ z' : Z, P.consumptionFn z a ≤ P.consumptionFn z' a) : + P.policy (a, z) < a := by + by_contra hcon + rw [not_lt] at hcon + set A : ℝ := P.policy (a, z) with hA + have hAmem : A ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policy_mem _) + have hA0 : assetFloor < A := lt_of_lt_of_le ha0 hcon + have hbv := P.toExtended.bellman_valueFunction + have hc : 0 < P.consumptionFn z a := hpos z a ha + -- the Euler inequality from saving less + have hE := P.euler_ge (v := P.toExtended.valueFunction) (z := z) (a := a) (A := A) + (du := du (P.consumptionFn z a)) (du' := fun z' => du (P.consumptionFn z' A)) + ha (by rw [hbv, hA]; exact congrFun P.policyOf_valueFunction (a, z)) hA0 + (fun z' => hslack z' A hAmem) (by rw [hbv]; exact hc) + (by rw [hbv]; exact hderiv _ hc) (fun z' => hpos z' A hAmem) + (fun z' => hderiv _ (hpos z' A hAmem)) + -- every next-period marginal utility is at most today's + have hbound : ∀ z' : Z, du (P.consumptionFn z' A) ≤ du (P.consumptionFn z a) := fun z' => + hanti (mem_Ioi.mpr hc) (mem_Ioi.mpr (hpos z' A hAmem)) + (le_trans (hmin z') (P.consumptionFn_mono ha hAmem hcon)) + have hsum : ∑ z' : Z, P.transitionMatrix z z' * du (P.consumptionFn z' A) + ≤ du (P.consumptionFn z a) := by + refine le_trans (Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (hbound z') (P.transitionMatrix_nonneg z z')) ?_ + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + have hdu : 0 < du (P.consumptionFn z a) := hdupos _ hc + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * (1 + P.interest) := mul_nonneg hβ hR.le + have h2 : ((P.discount : ℝ) * (1 + P.interest)) + * (∑ z' : Z, P.transitionMatrix z z' * du (P.consumptionFn z' A)) + ≤ ((P.discount : ℝ) * (1 + P.interest)) * du (P.consumptionFn z a) := + mul_le_mul_of_nonneg_left hsum hcoef + nlinarith [hE, h2, hdu, hβR] + +/-! ### Which income state consumes least + +The Euler argument above runs at the state where consumption is lowest, and the Doeblin +construction wants a FIXED state to repeat. The two agree when income is iid: the continuation +is then the same function of saving whatever today's state, so the problem depends on the state +only through cash on hand, and `consumptionFn_mono` generalises from two asset levels at one +income state to two states with ordered resources. -/ + +/-- **Consumption rises with resources**, across income states as well as asset levels, whenever +the two states share a continuation. This is `consumptionFn_mono` with the state allowed to +move: its proof never used `z` for anything but `cont z`. -/ +theorem consumptionFn_mono_of_cont_eq {a a' : ℝ} {z₁ z₂ : Z} (hcont : P.cont z₁ = P.cont z₂) + (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) + (hres : P.resources (a, z₁) ≤ P.resources (a', z₂)) : + P.consumptionFn z₁ a ≤ P.consumptionFn z₂ a' := by + set Δ : ℝ := P.resources (a', z₂) - P.resources (a, z₁) with hΔdef + have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith + set b : ℝ := P.policy (a, z₁) with hbdef + set b' : ℝ := P.policy (a', z₂) with hb'def + by_contra hcon + rw [not_le] at hcon + have hgap : b + Δ < b' := by + simp only [consumptionFn, consumption] at hcon + rw [hΔdef]; linarith + have hbmem : b ∈ P.toExtended.feasible (a, z₁) := P.policy_mem _ + have hb'mem : b' ∈ P.toExtended.feasible (a', z₂) := P.policy_mem _ + rw [P.feasible_eq] at hbmem hb'mem + -- the saving cap cannot grow by more than resources do, across states as within one + have hms : P.maxSaving (a', z₂) ≤ P.maxSaving (a, z₁) + Δ := by + have hmin : min assetCap (P.resources (a', z₂)) + ≤ min assetCap (P.resources (a, z₁)) + Δ := by + rcases le_total assetCap (P.resources (a, z₁)) with h | h + · rw [min_eq_left h] + exact le_trans (min_le_left _ _) (by linarith) + · rw [min_eq_right h] + exact le_trans (min_le_right _ _) (by rw [hΔdef]; linarith) + rw [P.maxSaving_eq, P.maxSaving_eq] + linarith + have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z₂) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z₁) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hc1 : P.consumption (a, z₁) b ∈ P.dom := P.consumption_policy_mem_dom (s := (a, z₁)) ha + have hc2 : P.consumption (a', z₂) b' ∈ P.dom := P.consumption_policy_mem_dom (s := (a', z₂)) ha' + have he1 : P.consumption (a', z₂) (b + Δ) = P.consumption (a, z₁) b := by + simp only [consumption, hΔdef]; ring + have he2 : P.consumption (a, z₁) (b' - Δ) = P.consumption (a', z₂) b' := by + simp only [consumption, hΔdef]; ring + have hI := P.objR_le_of_mem ha hshiftdown (by rw [he2]; exact hc2) + have hII := P.objR_le_of_mem ha' hshiftup (by rw [he1]; exact hc1) + have hbreg : b ∈ Icc assetFloor assetCap := P.policy_mem_region _ + have hb'reg : b' ∈ Icc assetFloor assetCap := P.policy_mem_region _ + have hshift := (P.concaveOn_cont z₁).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) + hbreg (by simpa using hb'reg) (by linarith) hΔ + rw [sub_add_cancel] at hshift + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hshift hβ + simp only [objR, he1, he2, ← hcont] at hI hII + have hIIeq : P.objR (a', z₂) (b + Δ) = P.objR (a', z₂) b' := by + simp only [objR, he1, ← hcont] + nlinarith [hI, hII, hscaled] + have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z₂) (b + Δ) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z₂) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe ha' hshiftup (by rw [he1]; exact hc1), hIIeq, + ← P.objectiveE_eq_coe ha' (P.policy_mem _) hc2] + exact P.policy_optimal _ + exact absurd (P.eq_policy_of_optimal ha' hshiftup hbell) (by rw [← hb'def]; linarith) + +/-- Income is iid: the transition probabilities do not depend on today's state. -/ +def IidIncome : Prop := ∀ z₁ z₂ z' : Z, P.transitionMatrix z₁ z' = P.transitionMatrix z₂ z' + +theorem cont_eq_of_iid (hiid : P.IidIncome) (z₁ z₂ : Z) : P.cont z₁ = P.cont z₂ := by + funext x + exact Finset.sum_congr rfl fun z' _ => by rw [hiid z₁ z₂ z'] + +/-- **Consumption is lowest where income is lowest**, for an iid income process. -/ +theorem consumptionFn_le_of_income_le (hiid : P.IidIncome) {z₁ z₂ : Z} + (hinc : P.income z₁ ≤ P.income z₂) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + P.consumptionFn z₁ a ≤ P.consumptionFn z₂ a := + P.consumptionFn_mono_of_cont_eq (P.cont_eq_of_iid hiid z₁ z₂) ha ha + (by simp only [resources]; linarith) + +/-! ### The decline condition, from impatience + +Putting the two together: with iid income the lowest-income state is the lowest-consumption +state, so the Euler argument runs there, and assets decline at it from EVERY asset level above +the borrowing constraint. That is the hypothesis `hdecl` of `exists_exhaust_of_decline`, which +every witness has been discharging by a numerical calibration. It is impatience. + +What is NOT replaced is the corner condition `hzero`: decline alone gives a strictly falling +sequence, and the Doeblin argument needs an ATOM — the constraint has to be reached exactly, not +approached. That is a statement about the bottom of the state space, where the Euler equation +does not hold, and it stays a calibration. -/ + +/-- **Açıkgöz (2018) Proposition 4.** With iid income and `β(1+r) < 1`, assets strictly decline +at the lowest income state, at every asset level above the borrowing constraint. -/ +theorem policy_lt_self_of_impatient_iid + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha0 : assetFloor < a) : + P.policy (a, z₀) < a := + P.policy_lt_self_of_impatient hβR hderiv hanti hdupos hpos hslack ha ha0 + (fun z' => P.consumptionFn_le_of_income_le hiid (hz₀ z') ha) + +/-- **The Doeblin exhaustion data, with the decline half free.** Only the corner condition is +left as a calibration. -/ +theorem exists_exhaust_of_impatient + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a₀ : ℝ} (ha₀ : assetFloor < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc assetFloor a₀, P.policy (a, z₀) = assetFloor) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := + P.exists_exhaust_of_decline ha₀ hle hzero fun _a ha => + P.policy_lt_self_of_impatient_iid hβR hiid hderiv hanti hdupos hpos hslack hz₀ + ⟨le_trans ha₀.le ha.1, ha.2⟩ (lt_of_lt_of_le ha₀ ha.1) + +/-! ### CRRA, with the marginal-utility hypotheses discharged -/ + +/-- **Açıkgöz Proposition 4 for CRRA.** Marginal utility `c ^ (-γ)` is positive and decreasing +for every `γ > 0`, so impatience is the whole hypothesis. -/ +theorem crra_exists_exhaust_of_impatient {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a₀ : ℝ} (ha₀ : assetFloor < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc assetFloor a₀, P.policy (a, z₀) = assetFloor) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := + P.exists_exhaust_of_impatient hβR hiid (du := fun c => c ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_crraUtility γ hc) + (fun _ hx _ _ hxy => Real.rpow_le_rpow_of_nonpos hx hxy (by linarith)) + (fun c hc => Real.rpow_pos_of_pos hc _) hpos hslack hz₀ ha₀ hle hzero + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean new file mode 100644 index 000000000..354657807 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean @@ -0,0 +1,741 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.DynamicProgramming.ExtendedStochastic +public import LeanPool.AiyagariEquilibrium.Topology.IccCorrespondence +public import LeanPool.AiyagariEquilibrium.Models.ConsumptionSavingsUnbounded + + +/-! +# The income fluctuation problem + +The canonical incomplete-markets household: income follows a finite Markov chain, assets +earn interest, borrowing is ruled out, and the household chooses savings. The state is the +pair `(a, z)` of assets and income state, and + + `V (a, z) = max { u c + β * ∑ z', P z z' * V (a', z') }`. + +Period utility may fall to `-∞` at zero consumption, so this covers log and CRRA with +`σ ≥ 1` -- and, since the utility DOMAIN is a field, CES with `γ < 1` as well. + +## The domain field + +`u` is required to behave on a set `dom` with `Ioi 0 ⊆ dom ⊆ Ici 0`, so there are exactly two +choices: `Ioi 0`, where utility is unbounded below, and `Ici 0`, where it is bounded there. The +old `tendsto_atBot_u` is replaced by `continuousOn_extendDom`, which says that `u` extended by +`⊥` off `dom` is continuous where consumption lives. That is what Berge actually consumes, and +it is exactly `tendsto_atBot_u` when `dom = Ioi 0` (see `continuousOn_extendDom_Ioi`) and merely +continuity of `u` on `Ici 0` in the other case, where the `⊥` branch is unreachable. + +Nothing is lost by the swap: `tendsto_atBot_u` comes BACK as a theorem from `Unbounded`. + +The one real consequence is that positive consumption at the optimum is no longer free. With +`dom = Ioi 0` the reward is `⊥` there and the value being real settles it; with `dom = Ici 0` +it has to be earned from a MARGINAL Inada condition instead — see `ConsumptionFloor`. Results +that need it take `PositiveConsumption` as a hypothesis, which both routes supply. Results that +only need `u` to behave take membership in `dom`, and carry over untouched. + +## Retrofitted onto extended-real rewards + +This file used a floor, with the same apparatus as the deterministic models: `floor`, +`cFloor`, and a chain of results establishing that the floor never binds. All of it is +gone. The reward is `-∞` where consumption vanishes, and positive consumption at the +optimum follows from the value function being real rather than from an argument. + +The stochastic case needed `LeanEconomics.DynamicProgramming.ExtendedStochastic`, since the +continuation here is an expectation over shocks rather than `v` at a single point. That +turned out to be routine: the expectation is a sum of reals, so the objective is again an +extended real plus a finite coercion, and the reward's `-∞` never meets the expectation's +arithmetic. + +## Unchanged + +Discreteness of the income state is still what makes every function of `z` continuous, so +the budget correspondence is continuous in the pair. Consumption is still clamped above and +assets still capped -- the ordinary boundedness requirement, unrelated to the floor. Cash on +hand is computed from `max 0 a`, as in the deterministic retrofits, so that the value stays +finite at every state. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +/-- Utility extended by `⊥` outside the domain on which it is required to behave. With +`D = Ioi 0` this is `extendBot`; with `D = Ici 0` the `⊥` branch is unreachable on `Ici 0`. -/ +noncomputable def extendDom (D : Set ℝ) (u : ℝ → ℝ) (c : ℝ) : EReal := + open Classical in if c ∈ D then ((u c : ℝ) : EReal) else ⊥ + +theorem extendDom_of_mem {D : Set ℝ} {u : ℝ → ℝ} {c : ℝ} (h : c ∈ D) : + extendDom D u c = ((u c : ℝ) : EReal) := by + simp only [extendDom, h, reduceIte] + +theorem extendDom_of_not_mem {D : Set ℝ} {u : ℝ → ℝ} {c : ℝ} (h : c ∉ D) : + extendDom D u c = ⊥ := by + simp only [extendDom, h, reduceIte] + +theorem extendDom_ne_bot_iff {D : Set ℝ} {u : ℝ → ℝ} {c : ℝ} : + extendDom D u c ≠ ⊥ ↔ c ∈ D := by + by_cases h : c ∈ D + · simp [extendDom_of_mem h, h] + · simp [extendDom_of_not_mem h, h] + +/-- With `D = Ioi 0` the extension is `extendBot`, so the divergence condition supplies the +continuity requirement. -/ +theorem extendDom_Ioi (u : ℝ → ℝ) : extendDom (Ioi 0) u = extendBot u := by + funext c + rcases le_or_gt c 0 with h | h + · rw [extendDom_of_not_mem (by simpa using h), extendBot_of_nonpos h] + · rw [extendDom_of_mem (mem_Ioi.mpr h), extendBot_of_pos h] + +/-- The unbounded case: divergence at zero supplies the continuity requirement. -/ +theorem continuousOn_extendDom_Ioi {u : ℝ → ℝ} (hc : ContinuousOn u (Ioi 0)) + (ht : Tendsto u (𝓝[>] 0) atBot) : ContinuousOn (extendDom (Ioi 0) u) (Ici 0) := by + rw [extendDom_Ioi] + exact (continuous_extendBot hc ht).continuousOn + +/-- The income fluctuation problem, with period utility unbounded below. -/ +structure IncomeFluctuation (Z : Type*) [Fintype Z] [Nonempty Z] [TopologicalSpace Z] + [DiscreteTopology Z] (assetFloor assetCap : ℝ) where + /-- Income in each state. -/ + income : Z → ℝ + /-- The Markov transition matrix on income states. -/ + transitionMatrix : Z → Z → ℝ + /-- The interest rate on assets. -/ + interest : ℝ + /-- The discount factor. -/ + discount : ℝ≥0 + /-- Period utility, required to behave only on positive consumption. -/ + u : ℝ → ℝ + /-- A lower bound on income. -/ + minIncome : ℝ + /-- An upper bound on income. -/ + maxIncome : ℝ + minIncome_pos : 0 < minIncome + minIncome_le : ∀ z, minIncome ≤ income z + le_maxIncome : ∀ z, income z ≤ maxIncome + transitionMatrix_nonneg : ∀ z z', 0 ≤ transitionMatrix z z' + transitionMatrix_sum : ∀ z, ∑ z', transitionMatrix z z' = 1 + interest_gt_neg_one : 0 < 1 + interest + /-- The borrowing limit is below the asset cap, so the household always has somewhere to be. -/ + assetFloor_le_assetCap : assetFloor ≤ assetCap + /-- The asset cap is a large positive number; nothing in the development needs it to be small, + but several bounds need it to have a sign. -/ + assetCap_nonneg : 0 ≤ assetCap + /-- **A declared floor on consumption**, at most what a household pinned at the borrowing limit + can afford. Carrying it as data rather than computing it from the rate is what lets the same + household problem be re-run at a different interest rate without moving the reward's lower + bound: `minConsumption` is the rate-free part of the natural-borrowing-limit condition. -/ + minConsumption : ℝ := minIncome + interest * assetFloor + /-- **The borrowing limit is sustainable**: at the limit, income covers the interest on the debt + with something left to eat. With `assetFloor = 0` this is `0 < minIncome`; with a negative limit + it is Aiyagari's natural-borrowing-limit condition, and it is what replaces positivity of cash + on hand -- resources can be negative when the household is in debt, while consumption cannot. -/ + minConsumption_pos : 0 < minConsumption + minConsumption_le_floor : minConsumption ≤ minIncome + interest * assetFloor + discount_lt_one : discount < 1 + /-- The consumption levels at which utility is required to behave. `Ioi 0` when utility is + unbounded below, `Ici 0` when it is bounded there. -/ + dom : Set ℝ + Ioi_subset_dom : Ioi 0 ⊆ dom + dom_subset_Ici : dom ⊆ Ici 0 + continuousOn_u_dom : ContinuousOn u dom + monotoneOn_u_dom : MonotoneOn u dom + /-- Diminishing marginal utility, which makes the optimal policy unique. -/ + strictConcaveOn_u_dom : StrictConcaveOn ℝ dom u + /-- **What replaces the Inada condition.** Utility, extended by `⊥` off its domain, must be + continuous where consumption lives. For `dom = Ioi 0` this is exactly `Tendsto u (𝓝[>] 0) atBot` + via `continuous_extendBot`; for `dom = Ici 0` the `⊥` branch is never taken and it is just + continuity of `u` on `Ici 0`. -/ + continuousOn_extendDom : ContinuousOn (extendDom dom u) (Ici 0) + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- Cash on hand, with assets read as nonnegative. -/ +noncomputable def resources (s : ℝ × Z) : ℝ := + P.income s.2 + (1 + P.interest) * max assetFloor s.1 + +/-- The largest consumption the capped problem allows. -/ +noncomputable def maxConsumption : ℝ := + P.maxIncome + (1 + P.interest) * assetCap - assetFloor + +/-- The largest asset holding that can be carried forward. -/ +noncomputable def maxSaving (s : ℝ × Z) : ℝ := max assetFloor (min assetCap (P.resources s)) + +/-- Consumption on the budget line. -/ +noncomputable def consumption (s : ℝ × Z) (a' : ℝ) : ℝ := P.resources s - a' + +/-- Consumption clamped into `[0, maxConsumption]`. On the feasible set the lower clamp is +inactive, so this is consumption itself; off it, it keeps the reward's argument in `Ici 0`, which +is where `continuousOn_extendDom` applies. -/ +noncomputable def clampedConsumption (p : (ℝ × Z) × ℝ) : ℝ := + min P.maxConsumption (max 0 (P.consumption p.1 p.2)) + +/-- The reward: utility of consumption clamped above, and `⊥` where consumption falls outside +the domain on which utility behaves. No floor. -/ +noncomputable def rewardFn (p : (ℝ × Z) × ℝ) : EReal := + extendDom P.dom P.u (P.clampedConsumption p) + +/-- **The rates at which the borrowing limit is sustainable.** Raising the interest rate raises +the debt service on a negative floor, so a floor that supports positive consumption at one rate +need not support it at another: sustainability is part of what it means for a rate to be +admissible, not a consequence of `-1 < r`. With `assetFloor = 0` the second conjunct is free. -/ +def RateOK (P : IncomeFluctuation Z assetFloor assetCap) (r : ℝ) : Prop := + 0 < 1 + r ∧ P.minConsumption ≤ P.minIncome + r * assetFloor + +theorem RateOK.one_add_pos {P : IncomeFluctuation Z assetFloor assetCap} {r : ℝ} + (h : P.RateOK r) : 0 < 1 + r := h.1 + +theorem RateOK.sustainable {P : IncomeFluctuation Z assetFloor assetCap} {r : ℝ} + (h : P.RateOK r) : P.minConsumption ≤ P.minIncome + r * assetFloor := h.2 + +/-- At a zero borrowing limit every admissible rate is sustainable. -/ +theorem rateOK_of_floor_zero {P : IncomeFluctuation Z 0 assetCap} {r : ℝ} (hr : 0 < 1 + r) : + P.RateOK r := ⟨hr, by simpa using P.minConsumption_le_floor⟩ + +/-- Sustainability is linear in the rate, so it is inherited from the endpoints of an interval. -/ +theorem rateOK_of_mem_Icc {rlo rhi r : ℝ} (hlo : P.RateOK rlo) (hhi : P.RateOK rhi) + (hr : r ∈ Set.Icc rlo rhi) : P.RateOK r := by + refine ⟨by linarith [hr.1, hlo.1], ?_⟩ + rcases le_total (0 : ℝ) assetFloor with hf | hf + · nlinarith [hlo.2, hr.1, hr.2] + · nlinarith [hhi.2, hr.1, hr.2] + +theorem rateOK_self : P.RateOK P.interest := ⟨P.interest_gt_neg_one, P.minConsumption_le_floor⟩ + +theorem minConsumption_le_consumption_floor (s : ℝ × Z) : + P.minConsumption ≤ P.resources s - assetFloor := by + have h1 : assetFloor ≤ max assetFloor s.1 := le_max_left _ _ + have h2 : (1 + P.interest) * assetFloor ≤ (1 + P.interest) * max assetFloor s.1 := + mul_le_mul_of_nonneg_left h1 P.interest_gt_neg_one.le + have := P.minIncome_le s.2 + have := P.minConsumption_le_floor + simp only [resources]; linarith + +/-- **Cash on hand always exceeds the borrowing limit**, so something can always be eaten. This +replaces positivity of cash on hand: in debt, resources can be negative while consumption cannot. +-/ +theorem assetFloor_lt_resources (s : ℝ × Z) : assetFloor < P.resources s := by + have h1 := P.minConsumption_le_consumption_floor s + have h2 := P.minConsumption_pos + linarith + +theorem minIncome_le_maxIncome : P.minIncome ≤ P.maxIncome := + (P.minIncome_le Classical.ofNonempty).trans (P.le_maxIncome _) + +theorem minConsumption_le_maxConsumption : P.minConsumption ≤ P.maxConsumption := by + have h : (1 + P.interest) * assetFloor ≤ (1 + P.interest) * assetCap := + mul_le_mul_of_nonneg_left P.assetFloor_le_assetCap P.interest_gt_neg_one.le + simp only [maxConsumption] + linarith [P.minIncome_le_maxIncome, P.minConsumption_le_floor] + +theorem maxConsumption_pos : 0 < P.maxConsumption := + lt_of_lt_of_le P.minConsumption_pos P.minConsumption_le_maxConsumption + +theorem mem_dom_of_pos {c : ℝ} (hc : 0 < c) : c ∈ P.dom := P.Ioi_subset_dom hc + +theorem nonneg_of_mem_dom {c : ℝ} (hc : c ∈ P.dom) : 0 ≤ c := P.dom_subset_Ici hc + +theorem maxConsumption_mem_dom : P.maxConsumption ∈ P.dom := P.mem_dom_of_pos P.maxConsumption_pos + +theorem convex_dom : Convex ℝ P.dom := P.strictConcaveOn_u_dom.1 + +/-- The domain is an up-set: if utility behaves at `c` it behaves at anything larger. Both +`Ioi 0` and `Ici 0` are, and it follows from the two bracketing inclusions alone. -/ +theorem dom_upward {c c' : ℝ} (hc : c ∈ P.dom) (h : c ≤ c') : c' ∈ P.dom := by + rcases lt_or_ge 0 c' with h' | h' + · exact P.mem_dom_of_pos h' + · rwa [le_antisymm h (h'.trans (P.nonneg_of_mem_dom hc))] at hc + +/-- Utility behaves on the positives whatever the domain is, so every existing argument that +supplies `0 < c` keeps working. -/ +theorem continuousOn_u : ContinuousOn P.u (Ioi 0) := P.continuousOn_u_dom.mono P.Ioi_subset_dom + +theorem monotoneOn_u : MonotoneOn P.u (Ioi 0) := P.monotoneOn_u_dom.mono P.Ioi_subset_dom + +theorem strictConcaveOn_u : StrictConcaveOn ℝ (Ioi 0) P.u := + P.strictConcaveOn_u_dom.subset P.Ioi_subset_dom (convex_Ioi 0) + +theorem clampedConsumption_nonneg (p : (ℝ × Z) × ℝ) : 0 ≤ P.clampedConsumption p := + le_min P.maxConsumption_pos.le (le_max_left _ _) + +theorem clampedConsumption_le (p : (ℝ × Z) × ℝ) : + P.clampedConsumption p ≤ P.maxConsumption := min_le_left _ _ + +/-- Discreteness of `Z` is what makes this continuous. -/ +theorem continuous_resources : Continuous P.resources := + (continuous_of_discreteTopology.comp continuous_snd).add + (continuous_const.mul (continuous_const.max continuous_fst)) + +theorem continuous_maxSaving : Continuous P.maxSaving := + continuous_const.max (continuous_const.min P.continuous_resources) + +theorem continuous_clampedConsumption : Continuous P.clampedConsumption := by + refine continuous_const.min (continuous_const.max ?_) + exact (P.continuous_resources.comp continuous_fst).sub continuous_snd + +theorem continuous_rewardFn : Continuous P.rewardFn := + P.continuousOn_extendDom.comp_continuous P.continuous_clampedConsumption + fun p => mem_Ici.mpr (P.clampedConsumption_nonneg p) + +/-- The problem as a stochastic dynamic program with an extended-real reward. -/ +noncomputable def toExtended : ExtendedStochasticProgram (ℝ × Z) ℝ Z where + feasible s := Icc assetFloor (P.maxSaving s) + isCompact_feasible _ := isCompact_Icc + feasible_nonempty _ := nonempty_Icc.mpr (le_max_left _ _) + upperHemicontinuous_feasible := upperHemicontinuous_Icc continuous_const P.continuous_maxSaving + lowerHemicontinuous_feasible := + lowerHemicontinuous_Icc continuous_const P.continuous_maxSaving fun _ => le_max_left _ _ + reward := ⟨P.rewardFn, P.continuous_rewardFn⟩ + rewardMax := P.u P.maxConsumption + reward_le := by + intro s a _ + simp only [ContinuousMap.coe_mk, rewardFn] + by_cases h : P.clampedConsumption (s, a) ∈ P.dom + · rw [extendDom_of_mem h, EReal.coe_le_coe_iff] + exact P.monotoneOn_u_dom h P.maxConsumption_mem_dom (P.clampedConsumption_le _) + · rw [extendDom_of_not_mem h] + exact bot_le + select := ⟨fun _ => assetFloor, continuous_const⟩ + select_mem := fun _ => ⟨le_rfl, le_max_left _ _⟩ + rewardMin := P.u P.minConsumption + le_reward_select := by + intro s + simp only [ContinuousMap.coe_mk, rewardFn] + have h : P.minConsumption ≤ P.clampedConsumption (s, assetFloor) := by + refine le_min P.minConsumption_le_maxConsumption (le_max_of_le_right ?_) + simp only [consumption] + exact P.minConsumption_le_consumption_floor s + have hpos : 0 < P.clampedConsumption (s, assetFloor) := + lt_of_lt_of_le P.minConsumption_pos h + rw [extendDom_of_mem (P.mem_dom_of_pos hpos), EReal.coe_le_coe_iff] + exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) + (P.mem_dom_of_pos hpos) h + transition z' := ⟨fun p => (p.2, z'), continuous_snd.prodMk continuous_const⟩ + prob z' := ⟨fun p => P.transitionMatrix p.1.2 z', + (continuous_of_discreteTopology (f := fun z => P.transitionMatrix z z')).comp + (continuous_snd.comp continuous_fst)⟩ + prob_nonneg _ _ := P.transitionMatrix_nonneg _ _ + prob_sum p := P.transitionMatrix_sum _ + discount := P.discount + discount_lt_one := P.discount_lt_one + +@[simp] +theorem feasible_eq (s : ℝ × Z) : P.toExtended.feasible s = Icc assetFloor (P.maxSaving s) := rfl + +theorem maxSaving_le_resources (s : ℝ × Z) : P.maxSaving s ≤ P.resources s := + max_le (P.assetFloor_lt_resources s).le (min_le_right _ _) + +theorem consumption_nonneg {s : ℝ × Z} {a : ℝ} (ha : a ∈ P.toExtended.feasible s) : + 0 ≤ P.consumption s a := by + simp only [consumption] + linarith [le_trans ha.2 (P.maxSaving_le_resources s)] + +theorem consumption_le_maxConsumption {s : ℝ × Z} {a' : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (ha' : assetFloor ≤ a') : P.consumption s a' ≤ P.maxConsumption := by + have hmax : max assetFloor s.1 = s.1 := max_eq_right hs.1 + have : (1 + P.interest) * s.1 ≤ (1 + P.interest) * assetCap := + mul_le_mul_of_nonneg_left hs.2 P.interest_gt_neg_one.le + simp only [consumption, resources, maxConsumption, hmax] + linarith [P.le_maxIncome s.2] + +/-- **Utility is unbounded below**: the domain is the open half-line, so the reward is `⊥` at +zero consumption. This is the hypothesis under which positive consumption comes free. -/ +def Unbounded : Prop := P.dom = Ioi 0 + +theorem clampedConsumption_mem_dom_of_ne_bot {s : ℝ × Z} {a : ℝ} + (h : P.toExtended.reward (s, a) ≠ ⊥) : P.clampedConsumption (s, a) ∈ P.dom := + extendDom_ne_bot_iff.mp h + +theorem consumption_pos_of_ne_bot (hd : P.Unbounded) {s : ℝ × Z} {a : ℝ} + (h : P.toExtended.reward (s, a) ≠ ⊥) : 0 < P.consumption s a := by + have hmem := P.clampedConsumption_mem_dom_of_ne_bot h + rw [hd] at hmem + have : 0 < max 0 (P.consumption s a) := lt_of_lt_of_le hmem (min_le_right _ _) + rcases max_cases 0 (P.consumption s a) with ⟨he, _⟩ | ⟨he, _⟩ + · rw [he] at this; exact absurd this (lt_irrefl 0) + · rwa [he] at this + +/-- On the feasible set the clamps are both inactive, so the reward's argument is consumption +itself. -/ +theorem clampedConsumption_eq {s : ℝ × Z} {a : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (ha : a ∈ P.toExtended.feasible s) : P.clampedConsumption (s, a) = P.consumption s a := by + have h0 : 0 ≤ P.consumption s a := P.consumption_nonneg ha + simp only [clampedConsumption, max_eq_right h0, + min_eq_right (P.consumption_le_maxConsumption hs ha.1)] + +theorem consumption_mem_dom_of_ne_bot {s : ℝ × Z} {a : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (ha : a ∈ P.toExtended.feasible s) (h : P.toExtended.reward (s, a) ≠ ⊥) : + P.consumption s a ∈ P.dom := by + have := P.clampedConsumption_mem_dom_of_ne_bot h + rwa [P.clampedConsumption_eq hs ha] at this + +/-- **The reward is the utility of consumption**, wherever consumption lies in the domain on +which utility is required to behave. -/ +theorem reward_eq_coe_dom {s : ℝ × Z} {a : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (ha : a ∈ P.toExtended.feasible s) (hc : P.consumption s a ∈ P.dom) : + P.toExtended.reward (s, a) = ((P.u (P.consumption s a) : ℝ) : EReal) := + calc P.toExtended.reward (s, a) + = extendDom P.dom P.u (P.clampedConsumption (s, a)) := rfl + _ = extendDom P.dom P.u (P.consumption s a) := by rw [P.clampedConsumption_eq hs ha] + _ = _ := extendDom_of_mem hc + +theorem reward_eq_coe {s : ℝ × Z} {a : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (ha : a ∈ P.toExtended.feasible s) (hc : 0 < P.consumption s a) : + P.toExtended.reward (s, a) = ((P.u (P.consumption s a) : ℝ) : EReal) := + P.reward_eq_coe_dom hs ha (P.mem_dom_of_pos hc) + +/-- **The stochastic Bellman equation, with honest utility and positive consumption.** That +consumption is positive at the optimum is a consequence of the value being real, not a +separate development. -/ +theorem exists_optimal_saving {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + ∃ a' ∈ Icc assetFloor (P.maxSaving s), P.consumption s a' ∈ P.dom ∧ + P.toExtended.valueFunction s + = P.u (P.consumption s a') + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * P.toExtended.valueFunction (a', z') := by + obtain ⟨a', ha', heq⟩ := P.toExtended.exists_optimal_policy s + have hne : P.rewardFn (s, a') ≠ ⊥ := by + intro hb + rw [show P.toExtended.reward (s, a') = P.rewardFn (s, a') from rfl, hb, + EReal.bot_add _] at heq + exact EReal.coe_ne_bot _ heq + have hc : P.consumption s a' ∈ P.dom := P.consumption_mem_dom_of_ne_bot hs ha' hne + refine ⟨a', ha', hc, ?_⟩ + rw [P.reward_eq_coe_dom hs ha' hc, ← EReal.coe_add, EReal.coe_eq_coe_iff] at heq + exact heq + +/-! ### Concavity and the optimal policy + +The state is a pair `(assets, income state)`, which is not a module over `ℝ`, so concavity +is stated along the asset slice for each income state -- see +`LeanEconomics.Blackwell.forall_concaveOn_valueFunction`. Everything else follows the +deterministic argument, with the continuation value now an expectation: it is concave in +next period's assets because each `V (·, z')` is, and the transition probabilities are +nonnegative. -/ + +/-- A convex combination stays above a common lower bound. Used wherever the borrowing limit +has to survive an averaging argument, which with a floor of zero was `add_nonneg`. -/ +theorem le_convex_comb {c x y θ φ : ℝ} (hx : c ≤ x) (hy : c ≤ y) (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) + (hθφ : θ + φ = 1) : c ≤ θ * x + φ * y := by + have key : θ * x + φ * y - c = θ * (x - c) + φ * (y - c) := by linear_combination c * hθφ + linarith [key, mul_nonneg hθ (sub_nonneg.mpr hx), mul_nonneg hφ (sub_nonneg.mpr hy)] + +theorem convex_comb_le {c x y θ φ : ℝ} (hx : x ≤ c) (hy : y ≤ c) (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) + (hθφ : θ + φ = 1) : θ * x + φ * y ≤ c := by + have key : c - (θ * x + φ * y) = θ * (c - x) + φ * (c - y) := by + linear_combination (-c) * hθφ + linarith [key, mul_nonneg hθ (sub_nonneg.mpr hx), mul_nonneg hφ (sub_nonneg.mpr hy)] + +theorem resources_eq_affine {s : ℝ × Z} (hs : assetFloor ≤ s.1) : + P.resources s = P.income s.2 + (1 + P.interest) * s.1 := by + simp only [resources, max_eq_right hs] + +theorem resources_affine_comb {x y θ φ : ℝ} {z : Z} (hx : assetFloor ≤ x) + (hy : assetFloor ≤ y) + (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) (hθφ : θ + φ = 1) : + P.resources (θ * x + φ * y, z) = θ * P.resources (x, z) + φ * P.resources (y, z) := by + have hmix : assetFloor ≤ θ * x + φ * y := le_convex_comb hx hy hθ hφ hθφ + rw [P.resources_eq_affine hmix, P.resources_eq_affine hx, P.resources_eq_affine hy] + linear_combination (-(P.income z)) * hθφ + +theorem maxSaving_le_assetCap (s : ℝ × Z) : P.maxSaving s ≤ assetCap := + max_le P.assetFloor_le_assetCap (min_le_left _ _) + +theorem feasible_subset_region {s : ℝ × Z} {a : ℝ} (ha : a ∈ P.toExtended.feasible s) : + a ∈ Icc assetFloor assetCap := + ⟨ha.1, ha.2.trans (P.maxSaving_le_assetCap s)⟩ + +theorem maxSaving_eq (s : ℝ × Z) : P.maxSaving s = min assetCap (P.resources s) := + max_eq_right (le_min P.assetFloor_le_assetCap (P.assetFloor_lt_resources s).le) + +theorem feasible_convex {x y : ℝ} {z : Z} (hx : x ∈ Icc assetFloor assetCap) + (hy : y ∈ Icc assetFloor assetCap) {ax ay θ φ : ℝ} + (hax : ax ∈ P.toExtended.feasible (x, z)) (hay : ay ∈ P.toExtended.feasible (y, z)) + (hθ : 0 ≤ θ) (hφ : 0 ≤ φ) (hθφ : θ + φ = 1) : + θ * ax + φ * ay ∈ P.toExtended.feasible (θ * x + φ * y, z) := by + obtain ⟨hax0, haxm⟩ := hax + obtain ⟨hay0, haym⟩ := hay + rw [P.maxSaving_eq (x, z)] at haxm + rw [P.maxSaving_eq (y, z)] at haym + refine ⟨le_convex_comb hax0 hay0 hθ hφ hθφ, ?_⟩ + rw [P.maxSaving_eq (θ * x + φ * y, z)] + refine le_min ?_ ?_ + · have h1 : ax ≤ assetCap := haxm.trans (min_le_left _ _) + have h2 : ay ≤ assetCap := haym.trans (min_le_left _ _) + exact convex_comb_le h1 h2 hθ hφ hθφ + · have h1 : ax ≤ P.resources (x, z) := haxm.trans (min_le_right _ _) + have h2 : ay ≤ P.resources (y, z) := haym.trans (min_le_right _ _) + rw [P.resources_affine_comb hx.1 hy.1 hθ hφ hθφ] + exact add_le_add (mul_le_mul_of_nonneg_left h1 hθ) (mul_le_mul_of_nonneg_left h2 hφ) + +theorem bellmanFn_eq_of_optimal {v : (ℝ × Z) →ᵇ ℝ} {s : ℝ × Z} {a : ℝ} + (hs : s.1 ∈ Icc assetFloor assetCap) (ha : a ∈ P.toExtended.feasible s) + (heq : P.toExtended.objectiveE v s a + = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) : + P.consumption s a ∈ P.dom ∧ + P.toExtended.bellmanFn v s + = P.u (P.consumption s a) + P.discount * ∑ z', P.transitionMatrix s.2 z' * v (a, z') := by + have hne : P.toExtended.reward (s, a) ≠ ⊥ := by + intro hb + rw [ExtendedStochasticProgram.objectiveE, hb, EReal.bot_add _] at heq + exact EReal.coe_ne_bot _ heq.symm + have hc := P.consumption_mem_dom_of_ne_bot hs ha hne + refine ⟨hc, ?_⟩ + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs ha hc, ← EReal.coe_add, + EReal.coe_eq_coe_iff] at heq + exact heq.symm + +/-- **The Bellman operator preserves concavity along each asset slice.** -/ +theorem concaveOn_bellman (v : (ℝ × Z) →ᵇ ℝ) + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) fun a => (P.toExtended.bellman v) (a, z) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + obtain ⟨ax, hax, heqx⟩ := P.toExtended.exists_optimal_action v (x, z) + obtain ⟨ay, hay, heqy⟩ := P.toExtended.exists_optimal_action v (y, z) + obtain ⟨hcx, hbx⟩ := P.bellmanFn_eq_of_optimal hx hax heqx + obtain ⟨hcy, hby⟩ := P.bellmanFn_eq_of_optimal hy hay heqy + have hxy : θ * x + φ * y ∈ Icc assetFloor assetCap := by + simpa using convex_Icc assetFloor assetCap hx hy hθ hφ hθφ + have hmix := P.feasible_convex hx hy hax hay hθ hφ hθφ + have hcmix : P.consumption (θ * x + φ * y, z) (θ * ax + φ * ay) + = θ * P.consumption (x, z) ax + φ * P.consumption (y, z) ay := by + simp only [consumption] + rw [P.resources_affine_comb hx.1 hy.1 hθ hφ hθφ] + ring + have hcm : P.consumption (θ * x + φ * y, z) (θ * ax + φ * ay) ∈ P.dom := by + rw [hcmix] + simpa only [smul_eq_mul] using P.convex_dom hcx hcy hθ hφ hθφ + have hle := P.toExtended.le_bellmanFn v hmix + have hobj : P.toExtended.objectiveE v (θ * x + φ * y, z) (θ * ax + φ * ay) + = ((P.u (P.consumption (θ * x + φ * y, z) (θ * ax + φ * ay)) + + P.discount * ∑ z', P.transitionMatrix z z' * v (θ * ax + φ * ay, z') : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hxy hmix hcm, ← EReal.coe_add] + rfl + rw [hobj] at hle + have hle' : P.u (P.consumption (θ * x + φ * y, z) (θ * ax + φ * ay)) + + P.discount * ∑ z', P.transitionMatrix z z' * v (θ * ax + φ * ay, z') + ≤ P.toExtended.bellmanFn v (θ * x + φ * y, z) := by exact_mod_cast hle + -- utility is concave, and so is the expectation, termwise + have hu := P.strictConcaveOn_u_dom.concaveOn.2 hcx hcy hθ hφ hθφ + have hexp : θ * (∑ z', P.transitionMatrix z z' * v (ax, z')) + + φ * (∑ z', P.transitionMatrix z z' * v (ay, z')) + ≤ ∑ z', P.transitionMatrix z z' * v (θ * ax + φ * ay, z') := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hvz := (hv z').2 (P.feasible_subset_region hax) (P.feasible_subset_region hay) hθ hφ hθφ + simp only [smul_eq_mul] at hvz + nlinarith [P.transitionMatrix_nonneg z z'] + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hexp hβ + simp only [smul_eq_mul] at hu + simp only [smul_eq_mul, ExtendedStochasticProgram.bellman_apply] + rw [hbx, hby] + rw [hcmix] at hle' + linarith + +/-- **The value function is concave in assets, for each income state.** -/ +theorem concaveOn_valueFunction (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) fun a => P.toExtended.valueFunction (a, z) := + Blackwell.forall_concaveOn_valueFunction (convex_Icc _ _) (fun (z : Z) (a : ℝ) => (a, z)) + P.toExtended.blackwell P.toExtended.discount_lt_one + (fun v hv => P.concaveOn_bellman v hv) z + +/-- **The optimal action is unique.** -/ +theorem optimal_action_unique {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a₀ a₁ : ℝ} + (h₀ : a₀ ∈ P.toExtended.feasible s) (h₁ : a₁ ∈ P.toExtended.feasible s) + (hm₀ : P.toExtended.objectiveE P.toExtended.valueFunction s a₀ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) + (hm₁ : P.toExtended.objectiveE P.toExtended.valueFunction s a₁ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) : + a₀ = a₁ := by + by_contra hne + obtain ⟨hc₀, hb₀⟩ := P.bellmanFn_eq_of_optimal hs h₀ hm₀ + obtain ⟨hc₁, hb₁⟩ := P.bellmanFn_eq_of_optimal hs h₁ hm₁ + have hhalf : (0 : ℝ) < 1 / 2 := by norm_num + have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num + have hmem : (1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁ ∈ P.toExtended.feasible s := by + simpa using convex_Icc assetFloor (P.maxSaving s) h₀ h₁ hhalf.le hhalf.le hsum + have hcmid : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = (1 / 2 : ℝ) * P.consumption s a₀ + (1 / 2 : ℝ) * P.consumption s a₁ := by + simp only [consumption]; ring + have hcm : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) ∈ P.dom := by + rw [hcmid] + simpa only [smul_eq_mul] using P.convex_dom hc₀ hc₁ hhalf.le hhalf.le hsum + have hcne : P.consumption s a₀ ≠ P.consumption s a₁ := by + simp only [consumption] + intro h + exact hne (by linarith) + have hu := P.strictConcaveOn_u_dom.2 hc₀ hc₁ hcne hhalf hhalf hsum + have hexp : (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * P.toExtended.valueFunction (a₀, z')) + + (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * P.toExtended.valueFunction (a₁, z')) + ≤ ∑ z', P.transitionMatrix s.2 z' + * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hvz := (P.concaveOn_valueFunction z').2 (P.feasible_subset_region h₀) + (P.feasible_subset_region h₁) hhalf.le hhalf.le hsum + simp only [smul_eq_mul] at hvz + nlinarith [P.transitionMatrix_nonneg s.2 z'] + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hexp hβ + simp only [smul_eq_mul] at hu + have hle := P.toExtended.le_bellmanFn P.toExtended.valueFunction hmem + have hobj : P.toExtended.objectiveE P.toExtended.valueFunction s + ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = ((P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') + : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs hmem hcm, ← EReal.coe_add] + rfl + rw [hobj] at hle + have hle' : P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') + ≤ P.toExtended.bellmanFn P.toExtended.valueFunction s := by exact_mod_cast hle + rw [hcmid] at hle' + linarith + +/-- The optimal saving choice. -/ +noncomputable def policy (s : ℝ × Z) : ℝ := + Classical.choose (P.toExtended.exists_optimal_action P.toExtended.valueFunction s) + +theorem policy_mem (s : ℝ × Z) : P.policy s ∈ P.toExtended.feasible s := + (Classical.choose_spec + (P.toExtended.exists_optimal_action P.toExtended.valueFunction s)).1 + +theorem policy_optimal (s : ℝ × Z) : + P.toExtended.objectiveE P.toExtended.valueFunction s (P.policy s) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal) := + (Classical.choose_spec + (P.toExtended.exists_optimal_action P.toExtended.valueFunction s)).2 + +theorem policy_mem_region (s : ℝ × Z) : P.policy s ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policy_mem s) + +theorem consumption_policy_mem_dom {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + P.consumption s (P.policy s) ∈ P.dom := + (P.bellmanFn_eq_of_optimal hs (P.policy_mem s) (P.policy_optimal s)).1 + +/-- **Consumption never vanishes at the optimum.** Automatic when utility is unbounded below +(`positiveConsumption_of_unbounded`); when it is bounded, it has to be earned from a marginal +Inada condition, which is what `ConsumptionFloor` does. -/ +def PositiveConsumption : Prop := + ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → 0 < P.consumption s (P.policy s) + +/-- **Utility really does diverge when the domain is the open half-line.** The structure no +longer carries `tendsto_atBot_u`, but it has not lost it: `continuousOn_extendDom` says the +extension is continuous at zero consumption, and with `dom = Ioi 0` the value there is `⊥`. -/ +theorem tendsto_atBot_u (hd : P.Unbounded) : Tendsto P.u (𝓝[>] 0) atBot := by + have hcont : ContinuousWithinAt (extendDom P.dom P.u) (Ici 0) 0 := + P.continuousOn_extendDom 0 (mem_Ici.mpr le_rfl) + have hbot : extendDom P.dom P.u 0 = ⊥ := extendDom_of_not_mem (by rw [hd]; simp) + rw [ContinuousWithinAt, hbot] at hcont + have h2 : Tendsto (extendDom P.dom P.u) (𝓝[>] (0 : ℝ)) (𝓝 ⊥) := + hcont.mono_left (nhdsWithin_mono _ Ioi_subset_Ici_self) + refine tendsto_atBot.2 fun R => ?_ + filter_upwards [h2 (Iio_mem_nhds (EReal.bot_lt_coe R)), self_mem_nhdsWithin] with c hc hcpos + simp only [mem_preimage, mem_Iio, extendDom_of_mem (P.mem_dom_of_pos hcpos), + EReal.coe_lt_coe_iff] at hc + exact hc.le + +theorem positiveConsumption_of_unbounded (hd : P.Unbounded) : P.PositiveConsumption := by + intro s hs + have := P.consumption_policy_mem_dom hs + rwa [hd] at this + +theorem consumption_policy_pos (hpc : P.PositiveConsumption) {s : ℝ × Z} + (hs : s.1 ∈ Icc assetFloor assetCap) : 0 < P.consumption s (P.policy s) := hpc s hs + +theorem eq_policy_of_optimal {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a : ℝ} + (ha : a ∈ P.toExtended.feasible s) + (hopt : P.toExtended.objectiveE P.toExtended.valueFunction s a + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) : + a = P.policy s := + P.optimal_action_unique hs ha (P.policy_mem s) hopt (P.policy_optimal s) + +theorem argmax_eq_singleton {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + argmax (P.toExtended.objectiveE P.toExtended.valueFunction) P.toExtended.feasible s + = {P.policy s} := by + ext a + simp only [argmax, Set.mem_ofPred_eq, Set.mem_singleton_iff] + constructor + · rintro ⟨ha, hmax⟩ + refine P.eq_policy_of_optimal hs ha (le_antisymm (P.toExtended.le_bellmanFn _ ha) ?_) + rw [← P.policy_optimal s] + exact isMaxOn_iff.mp hmax _ (P.policy_mem s) + · rintro rfl + refine ⟨P.policy_mem s, isMaxOn_iff.mpr fun b hb => ?_⟩ + rw [P.policy_optimal s] + exact P.toExtended.le_bellmanFn _ hb + +/-- **The optimal policy is continuous** on the region of states with assets in +`[0, assetCap]`. -/ +theorem continuousOn_policy : + ContinuousOn P.policy {s : ℝ × Z | s.1 ∈ Icc assetFloor assetCap} := + continuousOn_of_upperHemicontinuous_singleton + (P.toExtended.upperHemicontinuous_argmax P.toExtended.valueFunction) + fun _ hs => P.argmax_eq_singleton hs + +/-- **The Bellman equation at the policy.** -/ +theorem valueFunction_eq_policy {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + P.toExtended.valueFunction s + = P.u (P.consumption s (P.policy s)) + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * P.toExtended.valueFunction (P.policy s, z') := by + have h := (P.bellmanFn_eq_of_optimal hs (P.policy_mem s) (P.policy_optimal s)).2 + have hfix : P.toExtended.bellmanFn P.toExtended.valueFunction s + = P.toExtended.valueFunction s := by + conv_rhs => rw [← P.toExtended.bellman_valueFunction] + rfl + rw [← hfix, h] + +/-- A two-state calibration with `σ = 2`, where CES utility is `-1 / c`. -/ +noncomputable def calibrated : IncomeFluctuation (Fin 2) 0 10 where + income z := if z = 0 then 1 / 2 else 3 / 2 + transitionMatrix _ _ := 1 / 2 + interest := 1 / 20 + discount := 24 / 25 + u := fun c => -c⁻¹ + minIncome := 1 / 2 + maxIncome := 3 / 2 + minIncome_pos := by norm_num + minIncome_le z := by fin_cases z <;> norm_num + le_maxIncome z := by fin_cases z <;> norm_num + transitionMatrix_nonneg _ _ := by norm_num + transitionMatrix_sum _ := by simp + interest_gt_neg_one := by norm_num + assetFloor_le_assetCap := by norm_num + assetCap_nonneg := by norm_num + minConsumption_pos := by norm_num + minConsumption_le_floor := le_rfl + discount_lt_one := by norm_num + dom := Ioi 0 + Ioi_subset_dom := subset_rfl + dom_subset_Ici := Ioi_subset_Ici_self + continuousOn_u_dom := (continuousOn_id.inv₀ fun x hx => ne_of_gt hx).neg + monotoneOn_u_dom := by + intro x hx y _ hxy + have : (0 : ℝ) < x := hx + simp only [neg_le_neg_iff] + gcongr + strictConcaveOn_u_dom := strictConcaveOn_neg_inv + continuousOn_extendDom := + continuousOn_extendDom_Ioi ((continuousOn_id.inv₀ fun x hx => ne_of_gt hx).neg) + (tendsto_neg_atTop_atBot.comp tendsto_inv_nhdsGT_zero) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean new file mode 100644 index 000000000..cc784b518 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean @@ -0,0 +1,597 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate +public import LeanPool.AiyagariEquilibrium.Distribution.Stationary + + +/-! +# The interest rate as a state variable + +Comparative statics in the interest rate can be done by a parametric argument -- prove the +value function continuous in the parameter, then the policy, then the distribution -- or by +putting the rate INTO the state and letting the existing machinery do the work. This file +takes the second route. + +The rate never changes, so the operator is block diagonal in it: the transition carries the +rate forward untouched. The `r`-slice of the augmented value function is therefore a fixed +point of the `r`-programme's own Bellman operator, hence equal to its value function; and +Berge, already proved in this library, gives continuity of the augmented policy in the whole +state, which includes the rate. + +Two design points. + +The rate coordinate is CLAMPED to `[rlo, rhi]`. Without it the reward has no bound above, +since maximum consumption grows with the rate. This is the same clamp-and-prove-inactive +device the model already uses for consumption, and `clampRate_eq` is the "inactive" half. + +The clamp inside the reward is STATE-DEPENDENT rather than a constant, even though a constant +would also bound it. That is what makes the slice at a rate in range *equal* to the +`r`-programme rather than merely close to it: every piece of data matches on the nose, so the +two Bellman operators are literally the same and uniqueness of the fixed point finishes. With +a constant clamp the two would differ off the region and the slice lemma would need the +region bridge instead of an equality. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction MeasureTheory + +namespace LeanEconomics + +/-- The rate, clamped to the interval under consideration. -/ +noncomputable def clampRate (rlo rhi r : ℝ) : ℝ := min rhi (max rlo r) + +theorem continuous_clampRate (rlo rhi : ℝ) : Continuous (clampRate rlo rhi) := + continuous_const.min (continuous_const.max continuous_id) + +theorem clampRate_mem {rlo rhi : ℝ} (hle : rlo ≤ rhi) (r : ℝ) : + clampRate rlo rhi r ∈ Icc rlo rhi := + ⟨le_min hle (le_max_left _ _), min_le_left _ _⟩ + +/-- On the interval, the clamp is inactive. -/ +theorem clampRate_eq {rlo rhi r : ℝ} (hr : r ∈ Icc rlo rhi) : clampRate rlo rhi r = r := by + rw [clampRate, max_eq_right hr.1, min_eq_right hr.2] + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +-- The augmented state space is built for a household whose borrowing limit is at zero: the +-- clamped rate moves the debt service, so a negative limit would have to be re-checked at every +-- rate in the interval. The general-floor machinery is upstream. +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) (rlo rhi : ℝ) + +/-- The augmented state: assets, the interest rate, and the income state. -/ +abbrev AugState (_P : IncomeFluctuation Z (0 : ℝ) assetCap) : Type _ := (ℝ × ℝ) × Z + +/-- Cash on hand when the interest rate is carried in the state and clamped to the interval. -/ +noncomputable def augResources (s : P.AugState) : ℝ := + P.income s.2 + (1 + clampRate rlo rhi s.1.2) * max 0 s.1.1 + +/-- The consumption ceiling for the rate carried by an augmented state. -/ +noncomputable def augMaxConsumption (s : P.AugState) : ℝ := + P.maxIncome + (1 + clampRate rlo rhi s.1.2) * assetCap + +/-- The largest feasible saving, respecting the zero borrowing limit and asset cap. -/ +noncomputable def augMaxSaving (s : P.AugState) : ℝ := + max 0 (min assetCap (P.augResources rlo rhi s)) + +/-- Consumption restricted to the domain on which the extended reward is defined. -/ +noncomputable def augClampedConsumption (p : P.AugState × ℝ) : ℝ := + min (P.augMaxConsumption rlo rhi p.1) (max 0 (P.augResources rlo rhi p.1 - p.2)) + +/-- The extended period utility used by the augmented Bellman operator. -/ +noncomputable def augRewardFn (p : P.AugState × ℝ) : EReal := + extendDom P.dom P.u (P.augClampedConsumption rlo rhi p) + +variable {rlo rhi} + +theorem one_add_clampRate_pos (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (r : ℝ) : + 0 < 1 + clampRate rlo rhi r := by + have := (clampRate_mem hle r).1 + linarith + +theorem minIncome_le_augResources (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (s : P.AugState) : + P.minIncome ≤ P.augResources rlo rhi s := by + have h1 : 0 ≤ (1 + clampRate rlo rhi s.1.2) * max 0 s.1.1 := + mul_nonneg (one_add_clampRate_pos hrlo hle _).le (le_max_left _ _) + have := P.minIncome_le s.2 + simp only [augResources]; linarith + +theorem augResources_pos (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (s : P.AugState) : + 0 < P.augResources rlo rhi s := + lt_of_lt_of_le P.minIncome_pos (P.minIncome_le_augResources hrlo hle s) + +theorem minIncome_le_augMaxConsumption (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (s : P.AugState) : + P.minIncome ≤ P.augMaxConsumption rlo rhi s := by + have h1 : 0 ≤ (1 + clampRate rlo rhi s.1.2) * assetCap := + mul_nonneg (one_add_clampRate_pos hrlo hle _).le P.assetCap_nonneg + simp only [augMaxConsumption]; linarith [P.minIncome_le_maxIncome] + +theorem augMaxConsumption_pos (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (s : P.AugState) : + 0 < P.augMaxConsumption rlo rhi s := + lt_of_lt_of_le P.minIncome_pos (P.minIncome_le_augMaxConsumption hrlo hle s) + +/-- The rate clamp is what bounds the reward above. -/ +theorem augMaxConsumption_le (hle : rlo ≤ rhi) (s : P.AugState) : + P.augMaxConsumption rlo rhi s ≤ P.maxIncome + (1 + rhi) * assetCap := by + have h1 : clampRate rlo rhi s.1.2 ≤ rhi := (clampRate_mem hle _).2 + have h2 := P.assetCap_nonneg + simp only [augMaxConsumption] + nlinarith + +variable (rlo rhi) + +theorem continuous_augResources : Continuous (P.augResources rlo rhi) := + (continuous_of_discreteTopology.comp continuous_snd).add + (((continuous_const.add + ((continuous_clampRate rlo rhi).comp (continuous_snd.comp continuous_fst)))).mul + (continuous_const.max (continuous_fst.comp continuous_fst))) + +theorem continuous_augMaxConsumption : Continuous (P.augMaxConsumption rlo rhi) := + continuous_const.add + (((continuous_const.add + ((continuous_clampRate rlo rhi).comp (continuous_snd.comp continuous_fst)))).mul + continuous_const) + +theorem continuous_augMaxSaving : Continuous (P.augMaxSaving rlo rhi) := + continuous_const.max (continuous_const.min (P.continuous_augResources rlo rhi)) + +theorem augClampedConsumption_nonneg (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) + (p : P.AugState × ℝ) : 0 ≤ P.augClampedConsumption rlo rhi p := + le_min (P.augMaxConsumption_pos hrlo hle p.1).le (le_max_left _ _) + +theorem augClampedConsumption_le (p : P.AugState × ℝ) : + P.augClampedConsumption rlo rhi p ≤ P.augMaxConsumption rlo rhi p.1 := min_le_left _ _ + +theorem continuous_augClampedConsumption : Continuous (P.augClampedConsumption rlo rhi) := + ((P.continuous_augMaxConsumption rlo rhi).comp continuous_fst).min + (continuous_const.max + (((P.continuous_augResources rlo rhi).comp continuous_fst).sub continuous_snd)) + +theorem continuous_augRewardFn (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) : + Continuous (P.augRewardFn rlo rhi) := + P.continuousOn_extendDom.comp_continuous (P.continuous_augClampedConsumption rlo rhi) + fun p => mem_Ici.mpr (P.augClampedConsumption_nonneg rlo rhi hrlo hle p) + +variable {rlo rhi} + +/-- **The augmented programme**: the interest rate carried along as a state that never +changes. -/ +noncomputable def toAugmented (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) : + ExtendedStochasticProgram P.AugState ℝ Z where + feasible s := Icc 0 (P.augMaxSaving rlo rhi s) + isCompact_feasible _ := isCompact_Icc + feasible_nonempty _ := nonempty_Icc.mpr (le_max_left _ _) + upperHemicontinuous_feasible := + upperHemicontinuous_Icc continuous_const (P.continuous_augMaxSaving rlo rhi) + lowerHemicontinuous_feasible := + lowerHemicontinuous_Icc continuous_const (P.continuous_augMaxSaving rlo rhi) + fun _ => le_max_left _ _ + reward := ⟨P.augRewardFn rlo rhi, P.continuous_augRewardFn rlo rhi hrlo hle⟩ + rewardMax := P.u (P.maxIncome + (1 + rhi) * assetCap) + reward_le := by + intro s a _ + simp only [ContinuousMap.coe_mk, augRewardFn] + have hcap : 0 < P.maxIncome + (1 + rhi) * assetCap := + lt_of_lt_of_le (P.augMaxConsumption_pos hrlo hle s) (P.augMaxConsumption_le hle s) + by_cases h : P.augClampedConsumption rlo rhi (s, a) ∈ P.dom + · rw [extendDom_of_mem h, EReal.coe_le_coe_iff] + exact P.monotoneOn_u_dom h (P.mem_dom_of_pos hcap) + (le_trans (P.augClampedConsumption_le rlo rhi (s, a)) (P.augMaxConsumption_le hle s)) + · rw [extendDom_of_not_mem h]; exact bot_le + select := ⟨fun _ => 0, continuous_const⟩ + select_mem := fun _ => ⟨le_rfl, le_max_left _ _⟩ + rewardMin := P.u P.minIncome + le_reward_select := by + intro s + simp only [ContinuousMap.coe_mk, augRewardFn] + have h : P.minIncome ≤ P.augClampedConsumption rlo rhi (s, 0) := by + refine le_min (P.minIncome_le_augMaxConsumption hrlo hle s) (le_max_of_le_right ?_) + rw [sub_zero]; exact P.minIncome_le_augResources hrlo hle s + have hpos : 0 < P.augClampedConsumption rlo rhi (s, 0) := + lt_of_lt_of_le P.minIncome_pos h + rw [extendDom_of_mem (P.mem_dom_of_pos hpos), EReal.coe_le_coe_iff] + exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minIncome_pos) (P.mem_dom_of_pos hpos) h + transition z' := ⟨fun p => ((p.2, p.1.1.2), z'), + (continuous_snd.prodMk ((continuous_snd.comp continuous_fst).comp continuous_fst)).prodMk + continuous_const⟩ + prob z' := ⟨fun p => P.transitionMatrix p.1.2 z', + (continuous_of_discreteTopology (f := fun z => P.transitionMatrix z z')).comp + (continuous_snd.comp continuous_fst)⟩ + prob_nonneg _ _ := P.transitionMatrix_nonneg _ _ + prob_sum p := P.transitionMatrix_sum _ + discount := P.discount + discount_lt_one := P.discount_lt_one + +/-! ### The slice + +At a rate inside the interval the clamp is inactive, so every piece of the augmented +programme's data agrees on the nose with the `r`-programme's. The two Bellman operators are +then literally equal on the slice, and uniqueness of the fixed point does the rest. -/ + +variable {r : ℝ} + +theorem augResources_slice (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) (t : ℝ × Z) : + P.augResources rlo rhi ((t.1, r), t.2) = (P.withRate r hrr).resources t := by + simp only [augResources, resources, clampRate_eq hr] + rfl + +theorem augMaxConsumption_slice (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) (t : ℝ × Z) : + P.augMaxConsumption rlo rhi ((t.1, r), t.2) = (P.withRate r hrr).maxConsumption := by + simp only [augMaxConsumption, maxConsumption, clampRate_eq hr] + norm_num + +theorem augMaxSaving_slice (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) (t : ℝ × Z) : + P.augMaxSaving rlo rhi ((t.1, r), t.2) = (P.withRate r hrr).maxSaving t := by + simp only [augMaxSaving, maxSaving, P.augResources_slice hr hrr t] + +/-- The slice of a bounded continuous function on the augmented state. -/ +noncomputable def sliceAt (w : P.AugState →ᵇ ℝ) (r : ℝ) : (ℝ × Z) →ᵇ ℝ := + w.compContinuous ⟨fun t => ((t.1, r), t.2), + (continuous_fst.prodMk continuous_const).prodMk continuous_snd⟩ + +@[simp] +theorem sliceAt_apply (w : P.AugState →ᵇ ℝ) (r : ℝ) (t : ℝ × Z) : + P.sliceAt w r t = w ((t.1, r), t.2) := rfl + +/-- The feasible sets agree on the slice. -/ +theorem feasible_aug_eq (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) (t : ℝ × Z) : + (P.withRate r hrr).toExtended.feasible t + = (P.toAugmented hrlo hle).feasible ((t.1, r), t.2) := by + change Icc 0 ((P.withRate r hrr).maxSaving t) = Icc 0 (P.augMaxSaving rlo rhi ((t.1, r), t.2)) + rw [P.augMaxSaving_slice hr hrr t] + +/-- The objectives agree on the slice, for ANY continuation value. -/ +theorem objectiveE_aug_eq (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) (W : P.AugState →ᵇ ℝ) (t : ℝ × Z) (a : ℝ) : + (P.withRate r hrr).toExtended.objectiveE (P.sliceAt W r) t a + = (P.toAugmented hrlo hle).objectiveE W ((t.1, r), t.2) a := by + have hrew : (P.withRate r hrr).toExtended.reward (t, a) + = (P.toAugmented hrlo hle).reward (((t.1, r), t.2), a) := by + change extendDom (P.withRate r hrr).dom P.u + (min ((P.withRate r hrr).maxConsumption) + (max 0 ((P.withRate r hrr).resources t - a))) + = extendDom P.dom P.u (min (P.augMaxConsumption rlo rhi ((t.1, r), t.2)) + (max 0 (P.augResources rlo rhi ((t.1, r), t.2) - a))) + rw [P.augMaxConsumption_slice hr hrr t, P.augResources_slice hr hrr t] + rfl + have hexp : (P.withRate r hrr).toExtended.expect (P.sliceAt W r) (t, a) + = (P.toAugmented hrlo hle).expect W (((t.1, r), t.2), a) := by + simp only [ExtendedStochasticProgram.expect] + rfl + simp only [ExtendedStochasticProgram.objectiveE, hrew, hexp] + rfl + +/-- **The slice of the augmented value function is the value function at that rate.** -/ +theorem sliceAt_valueFunction (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) : + P.sliceAt (P.toAugmented hrlo hle).valueFunction r + = (P.withRate r hrr).toExtended.valueFunction := by + set W := (P.toAugmented hrlo hle).valueFunction with hW + set w := P.sliceAt W r with hw + -- the two programmes have the same feasible set and the same objective on the slice + have hfeas := P.feasible_aug_eq hrlo hle hr hrr + have hobj := fun (t : ℝ × Z) (a : ℝ) => P.objectiveE_aug_eq hrlo hle hr hrr W t a + -- so `w` is a fixed point of the `r`-operator + refine ((P.withRate r hrr).toExtended.eq_valueFunction ?_).symm ▸ rfl + refine BoundedContinuousFunction.ext fun t => ?_ + have hfix : W ((t.1, r), t.2) + = (P.toAugmented hrlo hle).bellmanFn W ((t.1, r), t.2) := by + rw [hW, ← (P.toAugmented hrlo hle).bellman_apply, + (P.toAugmented hrlo hle).bellman_valueFunction] + change (P.withRate r hrr).toExtended.bellmanFn w t = w t + rw [hw, sliceAt_apply, hfix] + simp only [ExtendedStochasticProgram.bellmanFn, ExtendedStochasticProgram.maxE, maxValueE] + rw [hfeas t] + exact congrArg EReal.toReal (congrArg sSup (Set.image_congr fun a _ => hobj t a)) + +/-! ### The transfer + +Uniqueness of the maximiser is IMPORTED from the slice rather than reproved: since the two +objectives and the two feasible sets coincide, the augmented argmax at `((a, r), z)` is the +`r`-programme's argmax at `(a, z)`, which is already known to be a singleton. So the whole +concavity and strict-concavity chain is reused rather than redone for the augmented state, +which is the reason this route was worth taking. -/ + +/-- An optimal saving choice for the augmented value function. -/ +noncomputable def augPolicy (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (x : P.AugState) : ℝ := + Classical.choose ((P.toAugmented hrlo hle).exists_optimal_action + (P.toAugmented hrlo hle).valueFunction x) + +theorem augPolicy_mem (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (x : P.AugState) : + P.augPolicy hrlo hle x ∈ (P.toAugmented hrlo hle).feasible x := + (Classical.choose_spec ((P.toAugmented hrlo hle).exists_optimal_action + (P.toAugmented hrlo hle).valueFunction x)).1 + +theorem augPolicy_optimal (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (x : P.AugState) : + (P.toAugmented hrlo hle).objectiveE (P.toAugmented hrlo hle).valueFunction x + (P.augPolicy hrlo hle x) + = (((P.toAugmented hrlo hle).bellmanFn (P.toAugmented hrlo hle).valueFunction x : ℝ) : + EReal) := + (Classical.choose_spec ((P.toAugmented hrlo hle).exists_optimal_action + (P.toAugmented hrlo hle).valueFunction x)).2 + +theorem augPolicy_mem_argmax (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (x : P.AugState) : + P.augPolicy hrlo hle x ∈ argmax ((P.toAugmented hrlo hle).objectiveE + (P.toAugmented hrlo hle).valueFunction) (P.toAugmented hrlo hle).feasible x := by + refine ⟨P.augPolicy_mem hrlo hle x, isMaxOn_iff.mpr fun b hb => ?_⟩ + rw [P.augPolicy_optimal hrlo hle x] + exact (P.toAugmented hrlo hle).le_bellmanFn _ hb + +/-- **The augmented argmax is the sliced programme's policy.** -/ +theorem argmax_aug_eq_singleton (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) {t : ℝ × Z} (ht : t.1 ∈ Icc (0 : ℝ) assetCap) : + argmax ((P.toAugmented hrlo hle).objectiveE (P.toAugmented hrlo hle).valueFunction) + (P.toAugmented hrlo hle).feasible ((t.1, r), t.2) + = {(P.withRate r hrr).policy t} := by + rw [← (P.withRate r hrr).argmax_eq_singleton ht] + have hobj : ∀ a, (P.withRate r hrr).toExtended.objectiveE + (P.withRate r hrr).toExtended.valueFunction t a + = (P.toAugmented hrlo hle).objectiveE (P.toAugmented hrlo hle).valueFunction + ((t.1, r), t.2) a := by + intro a + rw [← P.sliceAt_valueFunction hrlo hle hr hrr] + exact P.objectiveE_aug_eq hrlo hle hr hrr _ t a + have heq : (P.toAugmented hrlo hle).objectiveE (P.toAugmented hrlo hle).valueFunction + ((t.1, r), t.2) + = (P.withRate r hrr).toExtended.objectiveE + (P.withRate r hrr).toExtended.valueFunction t := (funext hobj).symm + have hfe := P.feasible_aug_eq hrlo hle hr hrr t + simp only [argmax, heq, ← hfe] + +theorem augPolicy_eq (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (hr : r ∈ Icc rlo rhi) + (hrr : P.RateOK r) {t : ℝ × Z} (ht : t.1 ∈ Icc (0 : ℝ) assetCap) : + P.augPolicy hrlo hle ((t.1, r), t.2) = (P.withRate r hrr).policy t := by + have hmem := P.augPolicy_mem_argmax hrlo hle ((t.1, r), t.2) + rw [P.argmax_aug_eq_singleton hrlo hle hr hrr ht] at hmem + exact hmem + +/-- **The policy is continuous in the state AND the interest rate, jointly.** -/ +theorem continuousOn_augPolicy (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) : + ContinuousOn (P.augPolicy hrlo hle) + {x : P.AugState | x.1.1 ∈ Icc (0 : ℝ) assetCap ∧ x.1.2 ∈ Icc rlo rhi} := by + refine continuousOn_of_upperHemicontinuous_singleton + ((P.toAugmented hrlo hle).upperHemicontinuous_argmax + (P.toAugmented hrlo hle).valueFunction) fun x hx => ?_ + have hrr : P.RateOK x.1.2 := rateOK_of_floor_zero (by have := hx.2.1; linarith) + have h := P.argmax_aug_eq_singleton hrlo hle hx.2 hrr (t := (x.1.1, x.2)) hx.1 + rw [h, P.augPolicy_eq hrlo hle hx.2 hrr (t := (x.1.1, x.2)) hx.1] + +/-! ### Uniform convergence of the policy + +Joint continuity plus compactness gives uniform continuity, and that is what resolves the +pointwise-versus-uniform difficulty recorded with `abs_markovFn_sub_le`: a parametric +argument would have produced continuity in `r` for each fixed state and then owed an +equicontinuity argument to make it uniform. Here uniformity is free, because a continuous +function on a compact set is uniformly continuous. + +As with the continuation value, `Z` carries no metric, so `(ℝ × ℝ) × Z` is not a metric space +and the modulus is taken in `(assets, rate)` for each income state, then combined over the +finitely many states. -/ + +/-- **The policy converges uniformly as the interest rate moves.** -/ +theorem exists_policy_modulus (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (z : Z), ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ r ∈ Icc rlo rhi, ∀ r' ∈ Icc rlo rhi, + |r - r'| < η → + |P.augPolicy hrlo hle ((a, r), z) - P.augPolicy hrlo hle ((a, r'), z)| < ε := by + classical + have hslice : ∀ z : Z, ∃ η > 0, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ r ∈ Icc rlo rhi, + ∀ r' ∈ Icc rlo rhi, |r - r'| < η → + |P.augPolicy hrlo hle ((a, r), z) - P.augPolicy hrlo hle ((a, r'), z)| < ε := by + intro z + set K : Set (ℝ × ℝ) := Icc (0 : ℝ) assetCap ×ˢ Icc rlo rhi with hK + have hKc : IsCompact K := isCompact_Icc.prod isCompact_Icc + have hmaps : MapsTo (fun p : ℝ × ℝ => ((p, z) : P.AugState)) K + {x : P.AugState | x.1.1 ∈ Icc (0 : ℝ) assetCap ∧ x.1.2 ∈ Icc rlo rhi} := + fun p hp => ⟨hp.1, hp.2⟩ + have hcont : ContinuousOn (fun p : ℝ × ℝ => P.augPolicy hrlo hle (p, z)) K := + (P.continuousOn_augPolicy hrlo hle).comp + (continuous_id.prodMk continuous_const).continuousOn hmaps + have huc := hKc.uniformContinuousOn_of_continuous hcont + obtain ⟨η, hη, hspec⟩ := Metric.uniformContinuousOn_iff.mp huc ε hε + refine ⟨η, hη, fun a ha r hr r' hr' hd => ?_⟩ + have hmem : ((a, r) : ℝ × ℝ) ∈ K := ⟨ha, hr⟩ + have hmem' : ((a, r') : ℝ × ℝ) ∈ K := ⟨ha, hr'⟩ + have hdist : dist ((a, r) : ℝ × ℝ) ((a, r') : ℝ × ℝ) < η := by + rw [Prod.dist_eq, dist_self, Real.dist_eq] + simpa using hd + have := hspec _ hmem _ hmem' hdist + rwa [Real.dist_eq] at this + choose η hη hspec using hslice + have hne : (Finset.univ : Finset Z).Nonempty := ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + refine ⟨Finset.univ.inf' hne η, (Finset.lt_inf'_iff _).2 fun z _ => hη z, ?_⟩ + exact fun z a ha r hr r' hr' hd => + hspec z a ha r hr r' hr' (lt_of_lt_of_le hd (Finset.inf'_le _ (Finset.mem_univ z))) + +/-- The same, stated for the policies of the sliced programmes. -/ +theorem exists_policy_modulus_withRate (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) {ε : ℝ} + (hε : 0 < ε) : + ∃ η > 0, ∀ (z : Z), ∀ a ∈ Icc (0 : ℝ) assetCap, + ∀ (r : ℝ) (_hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r), + ∀ (r' : ℝ) (_hr' : r' ∈ Icc rlo rhi) (hrr' : P.RateOK r'), + |r - r'| < η → + |(P.withRate r hrr).policy (a, z) - (P.withRate r' hrr').policy (a, z)| < ε := by + obtain ⟨η, hη, hspec⟩ := P.exists_policy_modulus hrlo hle hε + refine ⟨η, hη, fun z a ha r _hr hrr r' _hr' hrr' hd => ?_⟩ + rw [← P.augPolicy_eq hrlo hle _hr hrr (t := (a, z)) ha, + ← P.augPolicy_eq hrlo hle _hr' hrr' (t := (a, z)) ha] + exact hspec z a ha r _hr r' _hr' hd + +/-! ### The Markov operator converges uniformly + +Feeding the policy modulus through `abs_markovFn_sub_le`. The two successor states differ +only in their asset coordinate -- the income state is the same draw -- so a modulus for the +test function in that coordinate is all that is needed. -/ + +/-- A modulus for a test function in the asset coordinate, uniform over income states. -/ +theorem exists_modulus_state (h : P.State →ᵇ ℝ) {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (z' : Z) (x y : ↥(Icc (0 : ℝ) assetCap)), |(x : ℝ) - (y : ℝ)| < η → + |h (x, z') - h (y, z')| ≤ ε := by + classical + have : CompactSpace ↥(Icc (0 : ℝ) assetCap) := isCompact_iff_compactSpace.mp isCompact_Icc + have hslice : ∀ z' : Z, ∃ η > 0, ∀ x y : ↥(Icc (0 : ℝ) assetCap), + |(x : ℝ) - (y : ℝ)| < η → |h (x, z') - h (y, z')| ≤ ε := by + intro z' + have hcont : Continuous fun x : ↥(Icc (0 : ℝ) assetCap) => h (x, z') := + h.continuous.comp (continuous_id.prodMk continuous_const) + have huc : UniformContinuous fun x : ↥(Icc (0 : ℝ) assetCap) => h (x, z') := + CompactSpace.uniformContinuous_of_continuous hcont + obtain ⟨η, hη, hspec⟩ := Metric.uniformContinuous_iff.mp huc ε hε + refine ⟨η, hη, fun x y hxy => ?_⟩ + have hd : dist x y < η := by rw [Subtype.dist_eq, Real.dist_eq]; exact hxy + have := hspec hd + rw [Real.dist_eq] at this + exact this.le + choose η hη hspec using hslice + have hne : (Finset.univ : Finset Z).Nonempty := ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + refine ⟨Finset.univ.inf' hne η, (Finset.lt_inf'_iff _).2 fun z' _ => hη z', ?_⟩ + exact fun z' x y hxy => + hspec z' x y (lt_of_lt_of_le hxy (Finset.inf'_le _ (Finset.mem_univ z'))) + +/-- **The Markov operator converges uniformly as the interest rate moves.** This is the +statement the distribution's closed-graph argument consumes. -/ +theorem exists_markovFn_modulus (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) (h : P.State →ᵇ ℝ) + {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (r : ℝ) (_hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) + (r' : ℝ) (_hr' : r' ∈ Icc rlo rhi) (hrr' : P.RateOK r'), + |r - r'| < η → ∀ s : P.State, + |(P.withRate r hrr).markovFn h s - (P.withRate r' hrr').markovFn h s| ≤ ε := by + obtain ⟨ηh, hηh, hh⟩ := P.exists_modulus_state h hε + obtain ⟨η, hη, hpol⟩ := P.exists_policy_modulus_withRate hrlo hle hηh + refine ⟨η, hη, fun r hr hrr r' hr' hrr' hd s => ?_⟩ + refine abs_markovFn_sub_le (A := P.withRate r hrr) (B := P.withRate r' hrr') rfl h + (fun s z' => ?_) s + -- the successor states differ only in the asset coordinate + have hmem : ((s.1 : ℝ)) ∈ Icc (0 : ℝ) assetCap := s.1.2 + have hgap := hpol s.2 (s.1 : ℝ) hmem r hr hrr r' hr' hrr' hd + exact hh z' _ _ hgap + +/-! ### The closed-graph estimate + +A distribution stationary for a NEARBY rate is almost stationary for the reference rate, with +an error controlled uniformly. This is the estimate the closed-graph argument runs on: the +error term below involves only the two fixed bounded continuous functions `markovOp h` and +`h`, so it survives passage to a weak limit, and uniqueness of the stationary distribution +then pins the limit down. -/ + +variable [MeasurableSpace Z] [BorelSpace Z] + +theorem exists_almost_stationary_modulus (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) + {r₀ : ℝ} (hr₀ : r₀ ∈ Icc rlo rhi) (hrr₀ : P.RateOK r₀) (h : P.State →ᵇ ℝ) + {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (r : ℝ) (_hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r), |r - r₀| < η → + ∀ ν : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary ν → + |∫ s, (P.withRate r₀ hrr₀).markovOp h s ∂(ν : Measure P.State) + - ∫ s, h s ∂(ν : Measure P.State)| ≤ ε := by + obtain ⟨η, hη, hmod⟩ := P.exists_markovFn_modulus hrlo hle h hε + refine ⟨η, hη, fun r hr hrr hd ν hstat => ?_⟩ + set A := P.withRate r₀ hrr₀ with hA + set B := P.withRate r hrr with hB + -- stationarity at the nearby rate + have hpush : B.push (ν : Measure P.State) = (ν : Measure P.State) := + congrArg (fun x : ProbabilityMeasure P.State => (x : Measure P.State)) hstat + have hstat' : ∫ s, h s ∂(ν : Measure P.State) + = ∫ s, B.markovOp h s ∂(ν : Measure P.State) := by + have hp := B.integral_push (ν : Measure P.State) h + rwa [hpush] at hp + rw [hstat'] + -- the two operators are uniformly close + have hptwise : ∀ s : P.State, |A.markovOp h s - B.markovOp h s| ≤ ε := fun s => by + have hthis := hmod r₀ hr₀ hrr₀ r hr hrr (by rw [abs_sub_comm]; exact hd) s + rw [IncomeFluctuation.markovOp_apply, IncomeFluctuation.markovOp_apply] + exact hthis + have hnorm : ‖A.markovOp h - B.markovOp h‖ ≤ ε := + (BoundedContinuousFunction.norm_le hε.le).mpr fun s => by + rw [Real.norm_eq_abs] + simpa only [BoundedContinuousFunction.coe_sub, Pi.sub_apply] using hptwise s + -- so the integrals are close + have hsub : ∫ s, (A.markovOp h - B.markovOp h) s ∂(ν : Measure P.State) + = ∫ s, A.markovOp h s ∂(ν : Measure P.State) + - ∫ s, B.markovOp h s ∂(ν : Measure P.State) := by + simpa only [BoundedContinuousFunction.coe_sub, Pi.sub_apply] using + integral_sub ((A.markovOp h).integrable _) ((B.markovOp h).integrable _) + rw [← hsub] + exact le_trans (A.abs_integral_le _ _) hnorm + +/-! ### The stationary distribution moves continuously with the rate + +The cluster point step. Compactness of the space of probability measures supplies cluster +points; the estimate above makes any cluster point exactly stationary at the reference rate; +uniqueness there collapses it to a single point, and a filter with a unique cluster point in +a compact space converges. + +Uniqueness is needed only AT the reference rate. Existence at nearby rates is all that is +asked of them, and it is supplied by the selection `ν` rather than assumed of the model, so +the Doeblin hypotheses need not be carried across the whole interval. -/ + +theorem tendsto_stationary (hrlo : 0 < 1 + rlo) (hle : rlo ≤ rhi) {r₀ : ℝ} + (hr₀ : r₀ ∈ Icc rlo rhi) (hrr₀ : P.RateOK r₀) (ν : ℝ → ProbabilityMeasure P.State) + (hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r)) + (huniq : ∀ μ : ProbabilityMeasure P.State, + (P.withRate r₀ hrr₀).IsStationary μ → μ = ν r₀) : + Tendsto ν (𝓝[Icc rlo rhi] r₀) (𝓝 (ν r₀)) := by + refine tendsto_nhds_of_unique_mapClusterPt fun μ hcl => ?_ + refine huniq μ ?_ + -- every cluster point is stationary at the reference rate + have key : ∀ h : P.State →ᵇ ℝ, + ∫ s, h s ∂(((P.withRate r₀ hrr₀).pushProb μ : ProbabilityMeasure P.State) : + Measure P.State) + = ∫ s, h s ∂(μ : Measure P.State) := by + intro h + set G : ProbabilityMeasure P.State → ℝ := fun ρ => + ∫ s, (P.withRate r₀ hrr₀).markovOp h s ∂(ρ : Measure P.State) + - ∫ s, h s ∂(ρ : Measure P.State) with hG + have hGc : Continuous G := + ((P.withRate r₀ hrr₀).continuous_integral_bcf _).sub + ((P.withRate r₀ hrr₀).continuous_integral_bcf h) + -- the estimate holds eventually along the filter + have hzero : G μ = 0 := by + have hbound : ∀ ε : ℝ, 0 < ε → |G μ| ≤ ε := by + intro ε hε + obtain ⟨η, hη, hest⟩ := + P.exists_almost_stationary_modulus hrlo hle hr₀ hrr₀ h hε + have hev : ∀ᶠ r in 𝓝[Icc rlo rhi] r₀, |G (ν r)| ≤ ε := by + have h1 : ∀ᶠ r in 𝓝[Icc rlo rhi] r₀, r ∈ Icc rlo rhi := self_mem_nhdsWithin + have h2 : ∀ᶠ r in 𝓝[Icc rlo rhi] r₀, |r - r₀| < η := by + refine nhdsWithin_le_nhds ?_ + filter_upwards [Metric.ball_mem_nhds r₀ hη] with x hx + rwa [Metric.mem_ball, Real.dist_eq] at hx + filter_upwards [h1, h2] with r hr1 hr2 + have hrr : P.RateOK r := rateOK_of_floor_zero (by have := hr1.1; linarith) + exact hest r hr1 hrr hr2 (ν r) (hν r hr1 hrr) + -- a cluster point inherits a closed condition + have hmap : ClusterPt (G μ) (Filter.map (fun r => G (ν r)) (𝓝[Icc rlo rhi] r₀)) := by + have hm := hcl.map hGc.continuousAt + (le_refl (Filter.map G (Filter.map ν (𝓝[Icc rlo rhi] r₀)))) + rwa [Filter.map_map] at hm + have hle' : Filter.map (fun r => G (ν r)) (𝓝[Icc rlo rhi] r₀) + ≤ Filter.principal {x : ℝ | |x| ≤ ε} := by + rw [Filter.le_principal_iff] + exact hev + have : G μ ∈ closure {x : ℝ | |x| ≤ ε} := + mem_closure_iff_clusterPt.mpr (hmap.mono hle') + rwa [IsClosed.closure_eq (isClosed_le (continuous_abs) continuous_const)] at this + have habs : |G μ| ≤ 0 := by + by_contra hcon + rw [not_le] at hcon + have := hbound (|G μ| / 2) (by linarith) + linarith + exact abs_eq_zero.mp (le_antisymm habs (abs_nonneg _)) + have hdual := (P.withRate r₀ hrr₀).integral_push (μ : Measure P.State) h + rw [(P.withRate r₀ hrr₀).coe_pushProb, hdual] + rw [hG] at hzero + linarith [hzero] + have hfin : ((P.withRate r₀ hrr₀).pushProb μ).toFiniteMeasure = μ.toFiniteMeasure := + FiniteMeasure.ext_of_forall_integral_eq key + exact ProbabilityMeasure.toMeasure_injective + (congrArg (fun x : FiniteMeasure P.State => (x : Measure P.State)) hfin) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean new file mode 100644 index 000000000..e260c1269 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean @@ -0,0 +1,184 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import Mathlib.Algebra.Order.Group.Pointwise.Interval + + +/-! +# Consumption at the optimum is bounded away from zero + +Positivity of optimal consumption is already known pointwise: a zero would make the reward +`⊥` and the value could not be real. What is needed for comparative statics is a UNIFORM +bound, a single `δ > 0` working at every state. + +The bound proved here is explicit rather than compactness-based, and that is the whole point. +A compactness argument would say that optimal consumption is continuous and positive on a +compact state space, hence has a positive minimum — true, and recorded below as a sanity +check, but useless for the parametric problem. There the bound has to be uniform over an +interval of interest rates as well, and the corresponding compactness argument would need +joint continuity in `(r, s)`, which is precisely what the bound is wanted to prove. It would +be circular. + +The explicit route avoids that. At the optimum the Bellman equation reads + + V s = u c* + β · E[V] + +so `u c* ≥ -(1 + β)‖V‖`, a lower bound on the UTILITY of optimal consumption depending only +on the sup norm of the value function. Since `u` falls to `-∞` at zero, that pins consumption +away from zero. Nothing in the argument refers to the state, so the bound is automatically +uniform in the state; and when `r` varies, `‖V‖` is controlled by the reward bounds, which are +themselves uniform over a compact interval of rates. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The expected continuation value is bounded by the sup norm of the value function. -/ +theorem abs_expectation_le (a : ℝ) (z : Z) : + |∑ z', P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| + ≤ ‖P.toExtended.valueFunction‖ := by + calc |∑ z', P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| + ≤ ∑ z', |P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z', P.transitionMatrix z z' * ‖P.toExtended.valueFunction‖ := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] + exact mul_le_mul_of_nonneg_left + (P.toExtended.valueFunction.norm_coe_le_norm _) (P.transitionMatrix_nonneg z z') + _ = ‖P.toExtended.valueFunction‖ := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + +/-- **The utility of optimal consumption is bounded below**, by a quantity depending only on +the sup norm of the value function — not on the state. -/ +theorem le_utility_consumption_policy {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + -(1 + P.discount) * ‖P.toExtended.valueFunction‖ + ≤ P.u (P.consumption s (P.policy s)) := by + have hbell := P.valueFunction_eq_policy hs + have hexp := P.abs_expectation_le (P.policy s) s.2 + have hV : |P.toExtended.valueFunction s| ≤ ‖P.toExtended.valueFunction‖ := + P.toExtended.valueFunction.norm_coe_le_norm _ + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + rw [abs_le] at hexp hV + nlinarith [hexp.1, hexp.2, hV.1, hV.2, hbell] + +/-- **The utility bound in terms of the reward bounds alone** — no fixed point on the right. + +This is the form that survives into a parametric statement. A family of programs sharing a +utility function and a common bound on `max |u minConsumption| |u maxConsumption| / (1 - β)` has +a common `L`, and then `exists_lower_bound_of_utility_ge` hands it a single `δ`. -/ +theorem le_utility_consumption_policy_of_bounds {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + -(1 + P.discount) * (max |P.u P.minConsumption| |P.u P.maxConsumption| / (1 - P.discount)) + ≤ P.u (P.consumption s (P.policy s)) := by + refine le_trans ?_ (P.le_utility_consumption_policy hs) + have hnorm := P.toExtended.norm_valueFunction_le + have hmin : P.toExtended.rewardMin = P.u P.minConsumption := rfl + have hmax : P.toExtended.rewardMax = P.u P.maxConsumption := rfl + have hdisc : P.toExtended.discount = P.discount := rfl + rw [hmin, hmax, hdisc] at hnorm + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + nlinarith [hnorm] + +/-- **From a lower bound on utility to a lower bound on consumption.** The resulting `δ` +depends only on `u` and on `L` — NOT on the state, and not on anything that moves with the +interest rate. That is the property the parametric argument needs. -/ +theorem exists_lower_bound_of_utility_ge (hd : P.Unbounded) (L : ℝ) + (hL : ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → L ≤ P.u (P.consumption s (P.policy s))) : + ∃ δ > 0, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → δ ≤ P.consumption s (P.policy s) := by + have hev : ∀ᶠ c in 𝓝[>] (0 : ℝ), P.u c < L - 1 := + P.tendsto_atBot_u hd (eventually_lt_atBot (L - 1)) + obtain ⟨ε, hε, hsub⟩ := (nhdsGT_basis (0 : ℝ)).eventually_iff.mp hev + refine ⟨ε, hε, fun s hs => ?_⟩ + by_contra hlt + rw [not_le] at hlt + have hpos : 0 < P.consumption s (P.policy s) := + P.consumption_policy_pos (P.positiveConsumption_of_unbounded hd) hs + have hbad : P.u (P.consumption s (P.policy s)) < L - 1 := hsub ⟨hpos, hlt⟩ + linarith [hL s hs] + +/-- **Consumption at the optimum is bounded away from zero, uniformly in the state.** + +No compactness and no continuity of the policy are used, which is exactly what lets the same +argument run uniformly in the interest rate: a compactness proof would need joint continuity +in `(r, s)`, which is what such a bound is wanted to prove in the first place. -/ +theorem exists_consumption_policy_lower_bound (hd : P.Unbounded) : + ∃ δ > 0, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → δ ≤ P.consumption s (P.policy s) := + P.exists_lower_bound_of_utility_ge hd _ fun _ hs => + P.le_utility_consumption_policy_of_bounds hs + +/-! ### A choice set that does not move + +The Bellman operator maximises over `Icc 0 (maxSaving s)`, an interval whose RIGHT ENDPOINT +moves with the interest rate. Comparing the operator at two rates therefore means comparing +suprema over two different sets, which is the awkward part of any such estimate. + +Writing the choice as a fraction `θ ∈ [0,1]` of the maximum feasible saving removes the +problem: the choice set becomes `[0,1]` for every rate and every state, and all the rate +dependence moves into the objective, where it can be estimated pointwise. `|sSup f - sSup g|` +over a COMMON set is bounded by the pointwise gap; over two different sets it is not. -/ + +theorem assetFloor_le_maxSaving (s : ℝ × Z) : assetFloor ≤ P.maxSaving s := le_max_left _ _ + +/-- How much saving room the household has above the borrowing limit. -/ +noncomputable def savingRoom (s : ℝ × Z) : ℝ := P.maxSaving s - assetFloor + +theorem savingRoom_nonneg (s : ℝ × Z) : 0 ≤ P.savingRoom s := by + simp only [savingRoom]; linarith [P.assetFloor_le_maxSaving s] + +/-- The feasible set is the image of the FIXED interval `[0,1]` under the affine map that takes +`θ` to the borrowing limit plus a fraction `θ` of the saving room. With `assetFloor = 0` this is +just scaling by the maximum feasible saving. -/ +theorem feasible_eq_image (s : ℝ × Z) : + P.toExtended.feasible s = (fun θ => assetFloor + θ * P.savingRoom s) '' Icc 0 1 := by + have hcomp : (fun θ : ℝ => assetFloor + θ * P.savingRoom s) + = (fun y : ℝ => assetFloor + y) ∘ (fun θ : ℝ => θ * P.savingRoom s) := rfl + rw [P.feasible_eq, hcomp, Set.image_comp, + image_mul_right_Icc (by norm_num) (P.savingRoom_nonneg s), image_const_add_Icc] + simp only [zero_mul, one_mul, add_zero] + congr 1 + simp only [savingRoom]; ring + +/-- **The one-period value as a supremum over a fixed interval.** -/ +theorem maxE_eq_sSup_unit (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : + P.toExtended.maxE v s + = sSup ((fun θ => P.toExtended.objectiveE v s (assetFloor + θ * P.savingRoom s)) + '' Icc 0 1) := by + rw [ExtendedStochasticProgram.maxE, maxValueE, P.feasible_eq_image, Set.image_image] + +/-- **Consumption is bounded below by a quantity free of the state and the interest rate.** + +Along the reparametrised choice, consumption is at least `minConsumption * (1 - θ)`. The bound +holds because the saving room never exceeds what is left after servicing the debt, so taking a +fraction `θ` of it leaves at least a fraction `1 - θ`. + +This is the uniformity that makes the parametric estimate possible: it does not mention the +state, and the only model data it mentions -- `minConsumption` -- does not move with the interest +rate. Away from `θ = 1` the reward is therefore real and bounded, so the `-∞` cannot +interfere. -/ +theorem minConsumption_mul_le_consumption {θ : ℝ} (hθ : θ ∈ Icc (0 : ℝ) 1) (s : ℝ × Z) : + P.minConsumption * (1 - θ) ≤ P.consumption s (assetFloor + θ * P.savingRoom s) := by + have hms := P.maxSaving_le_resources s + have hroom := P.savingRoom_nonneg s + have hR := P.minConsumption_le_consumption_floor s + have hle : P.savingRoom s ≤ P.resources s - assetFloor := by + simp only [savingRoom]; linarith + have h1 : θ * P.savingRoom s ≤ θ * (P.resources s - assetFloor) := + mul_le_mul_of_nonneg_left hle hθ.1 + simp only [consumption] + nlinarith [hθ.1, hθ.2, hR, h1] + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean new file mode 100644 index 000000000..2b5d32253 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -0,0 +1,571 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption +public import LeanPool.AiyagariEquilibrium.Analysis.PowerMean + + +/-! +# Concavity of the consumption function as a closed class + +`IncomeFluctuationConsumption` showed that concavity of the consumption function cannot follow +from concavity of `u` and of the value function, and that it is a statement about the FIXED +POINT. This file supplies the machinery that statements about the fixed point need, and reduces +Carroll and Kimball (1996) to its single inductive step. + +The pattern is the one used four times already — monotonicity, concavity, Lipschitz bounds and +increasing differences — but with one new difficulty. Those four are properties OF the value +function, closed in sup norm by `isClosed_concaveOn` and its relatives. Concavity of the +consumption function is a property of the value function's ARGMAX, and nothing says offhand +that an argmax survives a uniform limit. So the closure step has to be proved rather than +quoted, and that is what `tendsto_policyOf` does: along a uniformly convergent sequence of +continuation values the optimal actions converge, because a cluster point of the maximisers is +a maximiser of the limit and the maximiser of the limit is unique. + +That gives `concaveOn_consumptionFn_of_preserves`: if the Bellman operator carries a concave +consumption function to a concave consumption function, the fixed point's consumption function +is concave. The iteration starts from `0`, whose consumption function is `resources`, affine on +the asset region — `policyOf_zero`, an agent with no future eats everything. + +## What is proved, and what is left + +The hypothesis `hT` is the whole of Carroll and Kimball. Two thirds of it are now discharged. + +**The analysis.** On paper the argument runs through second derivatives: risk tolerances ADD +across the optimisation, `T_V(m) = T_u(c) + T_W(m - c)`, and Kimball's risk-tolerance +aggregation theorem propagates `T_V' ≥ T_u'` from the continuation to the value. `PowerMean` +supplies the integrated form of exactly that, with no derivative: for CRRA marginal utility the +consumption paired with a given stock of end-of-period assets is a positive multiple of the +weighted power mean, exponent `-γ`, of next period's consumptions, and a power mean with a +NEGATIVE exponent is concave and nondecreasing. Homogeneity turns that into the convexity of a +sublevel set, which is elementary. + +**The transfer.** Turning concavity of that map into concavity of the consumption function is +usually done by differentiating an inverse function. `concaveOn_of_egm` does it from the budget +identity alone: write the choice against end-of-period assets, and the two concavities are the +same statement. + +**The Euler equation** `hEuler` is proved in `IncomeFluctuationEuler`, and so is the step with +the BORROWING LIMIT allowed to bind. Saving more is feasible unless the household already saves +the maximum, so the inequality half of the first-order condition survives the corner; and at a +constrained state the saving is `assetFloor`, which lies below any average of savings, which is +the only thing `concaveOn_of_egm` ever needed. `concaveOn_of_egm_corner` and +`concaveOn_consumptionFnOf_bellman` are the results. + +**The induction's bookkeeping** is done in `IncomeFluctuationIterate`: positivity and +monotonicity of the consumption function, proved at the fixed point in this development, restated +for an arbitrary continuation, and a uniform bound on saving that covers every iterate at once. + +**So Carroll and Kimball is proved** -- `concaveOn_consumptionFn_of_marginal` for CRRA, +`concaveOn_consumptionFn_of_bounded_crra` for the bounded CES family `0 < γ < 1`, where positive +consumption comes from the marginal Inada condition run against an arbitrary continuation. + +One input is left explicit: a calibration inequality putting the asset cap above the bound on +saving. `crra_policyOf_le_mul` makes that bound the multiplicative one -- deviate to a FRACTION +of the saving and price the extra consumption where it lands -- so it involves the continuation's +OSCILLATION rather than its level, and the hypothesis is character for character the one +`crra_policy_lt_assetCap` carries. The witnesses that discharge that one discharge this one: +`nearLog_concaveOn_consumptionFn` is Carroll and Kimball for a concrete CES economy, at every +rate the equilibrium argument uses. + +**Toda (2021) still bounds what can be hoped for.** Under regularity conditions HARA is +NECESSARY for the consumption function to be concave, and the structure assumes of `u` only +strict concavity, so `hT` at that generality is FALSE and no proof of it can exist. That is why +the step above is stated for CRRA, which is what Light's published version (2020) assumes. + +## OWED: this is deferred, not abandoned + +The obstruction has moved four times. + +* HARA was the first answer: Toda (2021) makes it NECESSARY, and `Analysis/HARA.lean` supplies + it. The utility class is no longer the blocker. +* The asset cap was the second, and worse, because it made the conclusion outright FALSE -- + `not_concaveOn_consumptionFn_of_cap_binds` below. `crra_policy_lt_assetCap` removes it by + calibration, and `nearLog` is recalibrated so that saving provably never reaches the cap. +* Differentiability was the third, and it is now GONE: `hasDerivAt_bellman` is the Clausen and + Strub sandwich run along the iteration, and `PowerMean` removes second derivatives altogether. +* The Euler equation was the fourth, and it is proved -- `euler_eq`, and without the envelope + theorem at that. +* The borrowing-constraint kink was the fifth, and `concaveOn_of_egm_corner` absorbs it. +* The induction's bookkeeping was the sixth, and `IncomeFluctuationIterate` does it. + +* Positive consumption for the bounded CES family was the seventh, and + `positiveConsumptionAll_of_marginalInada` does it. +* The cap calibration was the eighth, and `crra_policyOf_le_mul` makes it the one the witnesses + already prove. + +Nothing remains: `nearLog_concaveOn_consumptionFn` is the theorem at a concrete calibration. + +This file holds the analysis and the closure step; `IncomeFluctuationEuler` the first-order +condition and the corner; `IncomeFluctuationIterate` the bookkeeping and the theorem. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- Continuation values that are concave along each asset slice. This is what makes the optimal +action unique, and it is preserved by the Bellman operator. -/ +def ConcaveSlices (assetFloor assetCap : ℝ) (v : (ℝ × Z) →ᵇ ℝ) : Prop := + ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z) + +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] in +theorem concaveSlices_zero : ConcaveSlices assetFloor assetCap (0 : (ℝ × Z) →ᵇ ℝ) := + fun _ => ⟨convex_Icc _ _, fun _ _ _ _ _ _ _ _ _ => by simp⟩ + +theorem concaveSlices_bellman {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) : + ConcaveSlices assetFloor assetCap (P.toExtended.bellman v) := + fun z => P.concaveOn_bellman v hv z + +theorem concaveSlices_valueFunction : + ConcaveSlices assetFloor assetCap P.toExtended.valueFunction := + P.concaveOn_valueFunction + +/-! ### The optimal action of an arbitrary continuation + +`IncomeFluctuation` gives uniqueness of the optimal action at the value function. The closure +argument needs it along the whole approximating sequence, so the same proof is run with any +continuation whose slices are concave. -/ + +/-- **The optimal action is unique** for any continuation with concave slices. -/ +theorem optimal_action_unique_of_concaveSlices {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) + {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a₀ a₁ : ℝ} + (h₀ : a₀ ∈ P.toExtended.feasible s) (h₁ : a₁ ∈ P.toExtended.feasible s) + (hm₀ : P.toExtended.objectiveE v s a₀ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) + (hm₁ : P.toExtended.objectiveE v s a₁ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) : + a₀ = a₁ := by + by_contra hne + obtain ⟨hc₀, hb₀⟩ := P.bellmanFn_eq_of_optimal hs h₀ hm₀ + obtain ⟨hc₁, hb₁⟩ := P.bellmanFn_eq_of_optimal hs h₁ hm₁ + have hhalf : (0 : ℝ) < 1 / 2 := by norm_num + have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num + have hmem : (1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁ ∈ P.toExtended.feasible s := by + rw [P.feasible_eq] at h₀ h₁ ⊢ + exact convex_Icc assetFloor (P.maxSaving s) h₀ h₁ hhalf.le hhalf.le hsum + have hcmid : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = (1 / 2 : ℝ) * P.consumption s a₀ + (1 / 2 : ℝ) * P.consumption s a₁ := by + simp only [consumption]; ring + have hcm : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) ∈ P.dom := by + rw [hcmid] + simpa only [smul_eq_mul] using P.convex_dom hc₀ hc₁ hhalf.le hhalf.le hsum + have hcne : P.consumption s a₀ ≠ P.consumption s a₁ := by + simp only [consumption] + intro h + exact hne (by linarith) + have hu := P.strictConcaveOn_u_dom.2 hc₀ hc₁ hcne hhalf hhalf hsum + have hexp : (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₀, z')) + + (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₁, z')) + ≤ ∑ z', P.transitionMatrix s.2 z' * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') := by + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hvz := (hv z').2 (P.feasible_subset_region h₀) (P.feasible_subset_region h₁) + hhalf.le hhalf.le hsum + simp only [smul_eq_mul] at hvz + nlinarith [P.transitionMatrix_nonneg s.2 z'] + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hexp hβ + simp only [smul_eq_mul] at hu + have hle := P.toExtended.le_bellmanFn v hmem + have hobj : P.toExtended.objectiveE v s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + = ((P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs hmem hcm, ← EReal.coe_add] + rfl + rw [hobj] at hle + have hle' : P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + + P.discount * ∑ z', P.transitionMatrix s.2 z' + * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') + ≤ P.toExtended.bellmanFn v s := by exact_mod_cast hle + rw [hcmid] at hle' + linarith + +/-- The optimal saving choice under an arbitrary continuation. At the value function this is +definitionally `policy`. -/ +noncomputable def policyOf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : ℝ := + Classical.choose (P.toExtended.exists_optimal_action v s) + +theorem policyOf_mem (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : + P.policyOf v s ∈ P.toExtended.feasible s := + (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).1 + +theorem policyOf_optimal (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : + P.toExtended.objectiveE v s (P.policyOf v s) + = ((P.toExtended.bellmanFn v s : ℝ) : EReal) := + (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).2 + +theorem policyOf_valueFunction : P.policyOf P.toExtended.valueFunction = P.policy := rfl + +/-- The consumption function implied by an arbitrary continuation. -/ +noncomputable def consumptionFnOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a : ℝ) : ℝ := + P.consumption (a, z) (P.policyOf v (a, z)) + +theorem consumptionFnOf_valueFunction : + P.consumptionFnOf P.toExtended.valueFunction = P.consumptionFn := rfl + +/-! ### The agent with no future eats everything + +The iteration starts at the zero continuation, where saving buys nothing and the optimal plan +is to consume all cash on hand. Its consumption function is `resources`, affine on the asset +region, so the induction has a base. -/ + +/-- Saving more cannot raise the reward: consumption falls, and utility rises with it. -/ +theorem reward_antitone {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {x y : ℝ} + (hx : x ∈ P.toExtended.feasible s) (hy : y ∈ P.toExtended.feasible s) (hxy : x ≤ y) : + P.toExtended.reward (s, y) ≤ P.toExtended.reward (s, x) := by + by_cases h : P.consumption s y ∈ P.dom + · have hcx : P.consumption s x ∈ P.dom := + P.dom_upward h (by simp only [consumption]; linarith) + rw [P.reward_eq_coe_dom hs hx hcx, P.reward_eq_coe_dom hs hy h, EReal.coe_le_coe_iff] + exact P.monotoneOn_u_dom h hcx (by simp only [consumption]; linarith) + · have hb : P.toExtended.reward (s, y) = ⊥ := by + by_contra hne + exact h (P.consumption_mem_dom_of_ne_bot hs hy hne) + rw [hb]; exact bot_le + +/-- **With nothing to gain from saving, the household saves nothing.** -/ +theorem policyOf_zero {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + P.policyOf 0 s = assetFloor := by + have hmem0 : assetFloor ∈ P.toExtended.feasible s := ⟨le_rfl, le_max_left _ _⟩ + have hc0 : 0 < P.consumption s assetFloor := by + have h1 := P.minConsumption_le_consumption_floor s + have h2 := P.minConsumption_pos + simp only [consumption]; linarith + have hobj : ∀ a : ℝ, P.toExtended.objectiveE (0 : (ℝ × Z) →ᵇ ℝ) s a + = P.toExtended.reward (s, a) := by + intro a + simp [ExtendedStochasticProgram.objectiveE, ExtendedStochasticProgram.expect] + have hle : ∀ a ∈ P.toExtended.feasible s, + P.toExtended.objectiveE (0 : (ℝ × Z) →ᵇ ℝ) s a + ≤ ((P.u (P.consumption s assetFloor) : ℝ) : EReal) := by + intro a ha + rw [hobj a, ← P.reward_eq_coe hs hmem0 hc0] + exact P.reward_antitone hs hmem0 ha ha.1 + have hb := P.toExtended.bellmanFn_le (0 : (ℝ × Z) →ᵇ ℝ) hle + have hopt : P.toExtended.objectiveE (0 : (ℝ × Z) →ᵇ ℝ) s assetFloor + = ((P.toExtended.bellmanFn (0 : (ℝ × Z) →ᵇ ℝ) s : ℝ) : EReal) := by + refine le_antisymm (P.toExtended.le_bellmanFn _ hmem0) ?_ + rw [hobj assetFloor, P.reward_eq_coe hs hmem0 hc0, EReal.coe_le_coe_iff] + exact hb + exact optimal_action_unique_of_concaveSlices P concaveSlices_zero hs (P.policyOf_mem _ _) hmem0 + (P.policyOf_optimal _ _) hopt + +theorem consumptionFnOf_zero {z : Z} {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + P.consumptionFnOf 0 z a = P.income z + (1 + P.interest) * a - assetFloor := by + rw [consumptionFnOf, P.policyOf_zero (s := (a, z)) ha] + simp only [consumption, resources, max_eq_right ha.1] + +theorem concaveOn_consumptionFnOf_zero (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf 0 z) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hm ⊢ + rw [P.consumptionFnOf_zero hx, P.consumptionFnOf_zero hy, P.consumptionFnOf_zero hm] + have hφ' : φ = 1 - θ := by linarith + subst hφ' + exact le_of_eq (by ring) + +/-! ### The closure step: optimal actions survive a uniform limit + +This is what the four earlier closed-class arguments got for free from `isClosed_concaveOn`. +Concavity of the consumption function is a property of the ARGMAX, so closure has to be proved: +a cluster point of maximisers of `vₙ` is a maximiser of the limit, and the maximiser of the +limit is unique. -/ + +theorem abs_expect_sub_le_norm (v w : (ℝ × Z) →ᵇ ℝ) (p : (ℝ × Z) × ℝ) : + |P.toExtended.expect v p - P.toExtended.expect w p| ≤ ‖v - w‖ := by + have h : P.toExtended.expect v p - P.toExtended.expect w p + = P.toExtended.expect (v - w) p := by + simp only [ExtendedStochasticProgram.expect, ← Finset.sum_sub_distrib, + BoundedContinuousFunction.coe_sub, Pi.sub_apply, mul_sub] + rw [h] + exact P.toExtended.abs_expect_le _ _ + +theorem tendsto_expect {v : ℕ → (ℝ × Z) →ᵇ ℝ} {w : (ℝ × Z) →ᵇ ℝ} (hv : Tendsto v atTop (𝓝 w)) + {x : ℕ → ℝ} {y : ℝ} (hx : Tendsto x atTop (𝓝 y)) (s : ℝ × Z) : + Tendsto (fun n => P.toExtended.expect (v n) (s, x n)) atTop + (𝓝 (P.toExtended.expect w (s, y))) := by + have hnorm : Tendsto (fun n => ‖v n - w‖) atTop (𝓝 0) := + tendsto_iff_norm_sub_tendsto_zero.mp hv + have hcont : Tendsto (fun n => P.toExtended.expect w (s, x n)) atTop + (𝓝 (P.toExtended.expect w (s, y))) := + ((P.toExtended.continuous_expect w).tendsto _).comp (tendsto_const_nhds.prodMk_nhds hx) + have hdiff : Tendsto (fun n => P.toExtended.expect (v n) (s, x n) + - P.toExtended.expect w (s, x n)) atTop (𝓝 0) := by + refine squeeze_zero_norm (fun n => ?_) hnorm + simpa only [Real.norm_eq_abs] using P.abs_expect_sub_le_norm (v n) w (s, x n) + simpa using hdiff.add hcont + +theorem tendsto_objectiveE {v : ℕ → (ℝ × Z) →ᵇ ℝ} {w : (ℝ × Z) →ᵇ ℝ} (hv : Tendsto v atTop (𝓝 w)) + {x : ℕ → ℝ} {y : ℝ} (hx : Tendsto x atTop (𝓝 y)) (s : ℝ × Z) : + Tendsto (fun n => P.toExtended.objectiveE (v n) s (x n)) atTop + (𝓝 (P.toExtended.objectiveE w s y)) := by + have hr : Tendsto (fun n => P.toExtended.reward (s, x n)) atTop + (𝓝 (P.toExtended.reward (s, y))) := + (P.toExtended.reward.continuous.tendsto _).comp (tendsto_const_nhds.prodMk_nhds hx) + have he : Tendsto (fun n => + ((P.toExtended.discount * P.toExtended.expect (v n) (s, x n) : ℝ) : EReal)) atTop + (𝓝 ((P.toExtended.discount * P.toExtended.expect w (s, y) : ℝ) : EReal)) := + (continuous_coe_real_ereal.tendsto _).comp + (tendsto_const_nhds.mul (P.tendsto_expect hv hx s)) + exact Filter.Tendsto.comp + (EReal.continuousAt_add (Or.inr (EReal.coe_ne_bot _)) (Or.inr (EReal.coe_ne_top _))) + (hr.prodMk_nhds he) + +/-- **Optimal actions survive a uniform limit.** -/ +theorem tendsto_policyOf {v : ℕ → (ℝ × Z) →ᵇ ℝ} {w : (ℝ × Z) →ᵇ ℝ} + (hv : Tendsto v atTop (𝓝 w)) (hw : ConcaveSlices assetFloor assetCap w) + {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + Tendsto (fun n => P.policyOf (v n) s) atTop (𝓝 (P.policyOf w s)) := by + refine tendsto_of_subseq_tendsto fun ns hns => ?_ + obtain ⟨L, hLK, φ, hφ, hφlim⟩ := (P.toExtended.isCompact_feasible s).tendsto_subseq + (x := fun i => P.policyOf (v (ns i)) s) (fun i => P.policyOf_mem _ _) + have hv' : Tendsto (fun i => v (ns (φ i))) atTop (𝓝 w) := + hv.comp (hns.comp hφ.tendsto_atTop) + have hlim' : Tendsto (fun i => P.policyOf (v (ns (φ i))) s) atTop (𝓝 L) := hφlim + -- every feasible action is beaten by the limit of the maximisers + have key : ∀ x ∈ P.toExtended.feasible s, + P.toExtended.objectiveE w s x ≤ P.toExtended.objectiveE w s L := by + intro x hx + refine le_of_tendsto_of_tendsto (P.tendsto_objectiveE hv' tendsto_const_nhds s) + (P.tendsto_objectiveE hv' hlim' s) (Eventually.of_forall fun i => ?_) + have hle := P.toExtended.le_bellmanFn (v (ns (φ i))) hx + rwa [← P.policyOf_optimal (v (ns (φ i))) s] at hle + have hLopt : P.toExtended.objectiveE w s L = ((P.toExtended.bellmanFn w s : ℝ) : EReal) := by + refine le_antisymm (P.toExtended.le_bellmanFn w hLK) ?_ + rw [← P.policyOf_optimal w s] + exact key _ (P.policyOf_mem w s) + have hLeq : L = P.policyOf w s := + optimal_action_unique_of_concaveSlices P hw hs hLK (P.policyOf_mem w s) hLopt + (P.policyOf_optimal w s) + exact ⟨φ, by rw [← hLeq]; exact hlim'⟩ + +/-! ### Carroll and Kimball, reduced to one step -/ + +/-- **The consumption function of the fixed point is concave whenever the Bellman operator +preserves concavity of the consumption function.** Everything except the hypothesis `hT` is +discharged: the base case, the induction, the limit. + +`hT` is Carroll and Kimball (1996) — see the module docstring for what proving it would take. -/ +theorem concaveOn_consumptionFn_of_preserves + (hT : ∀ v : (ℝ × Z) →ᵇ ℝ, ConcaveSlices assetFloor assetCap v → + (∀ z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z)) → + ∀ z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf (P.toExtended.bellman v) z)) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + set T := P.toExtended.bellman with hTdef + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + have hcons : ∀ n : ℕ, ∀ z, ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z) := by + intro n + induction n with + | zero => exact P.concaveOn_consumptionFnOf_zero + | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ (hslices k) ih + have hlim : Tendsto (fun n => T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 P.toExtended.valueFunction) := + P.toExtended.tendsto_iterate_valueFunction 0 + have hpt : ∀ a ∈ Icc assetFloor assetCap, + Tendsto (fun n => P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (P.consumptionFn z a)) := by + intro a ha + have hp := P.tendsto_policyOf hlim P.concaveSlices_valueFunction (s := (a, z)) ha + have he : P.consumptionFn z a + = P.resources (a, z) - P.policyOf P.toExtended.valueFunction (a, z) := rfl + rw [he] + simpa only [consumptionFnOf, consumption] using tendsto_const_nhds.sub hp + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ + refine le_of_tendsto_of_tendsto + (((hpt x hx).const_smul θ).add ((hpt y hy).const_smul φ)) (hpt _ hm) + (Eventually.of_forall fun n => (hcons n z).2 hx hy hθ hφ hθφ) + +/-! ### The endogenous-gridpoint transfer + +The step that carries concavity forward is not about the Bellman operator at all. Write the +household's choice against END-OF-PERIOD assets rather than against cash on hand: let `C A` be +the consumption it pairs with saving `A`, so that the budget reads `y + R a = A + C A`. The +graph of the consumption function is then the curve `A ↦ (A + C A, C A)`, and concavity of the +consumption function is exactly concavity of `C`. + +That equivalence is elementary — it is proved below with no derivative and no inverse function, +from the budget identity and the fact that `A + C A` is strictly increasing. What it buys is +that the hard step moves to `C`, where the first-order condition makes it a POWER MEAN of next +period's consumptions, and `Analysis/PowerMean` settles those. -/ + +/-- If `C` is nondecreasing then cash on hand is strictly increasing in end-of-period assets. -/ +theorem strictMonoOn_add_egm {S : Set ℝ} {C : ℝ → ℝ} (hC : MonotoneOn C S) : + StrictMonoOn (fun A => A + C A) S := fun _ ha _ hb hab => by + have := hC ha hb hab.le + simp only + linarith + +/-- **The endogenous-gridpoint transfer.** With the budget written against end-of-period assets, +concavity of the consumption function is concavity of `C`. Derivative-free. -/ +theorem concaveOn_of_egm {z : Z} {g C h : ℝ → ℝ} + (hC : ConcaveOn ℝ (Icc assetFloor assetCap) C) + (hM : StrictMonoOn (fun A => A + C A) (Icc assetFloor assetCap)) + (hg : ∀ a ∈ Icc assetFloor assetCap, g a ∈ Icc assetFloor assetCap) + (hbud : ∀ a ∈ Icc assetFloor assetCap, + P.income z + (1 + P.interest) * a = g a + C (g a)) + (hh : ∀ a ∈ Icc assetFloor assetCap, h a = C (g a)) : + ConcaveOn ℝ (Icc assetFloor assetCap) h := by + refine ⟨convex_Icc _ _, fun a ha b hb θ φ hθ hφ hθφ => ?_⟩ + simp only [smul_eq_mul] + have hm : θ * a + φ * b ∈ Icc assetFloor assetCap := by + simpa only [smul_eq_mul] using convex_Icc assetFloor assetCap ha hb hθ hφ hθφ + have hA₁ := hg a ha + have hA₂ := hg b hb + have hAθ := hg _ hm + have hAm : θ * g a + φ * g b ∈ Icc assetFloor assetCap := by + simpa only [smul_eq_mul] using convex_Icc assetFloor assetCap hA₁ hA₂ hθ hφ hθφ + have h1 := hbud a ha + have h2 := hbud b hb + have h3 := hbud _ hm + -- the budget at the midpoint, split two ways + have hcomb : P.income z + (1 + P.interest) * (θ * a + φ * b) + = (θ * g a + φ * g b) + (θ * C (g a) + φ * C (g b)) := by + linear_combination θ * h1 + φ * h2 - P.income z * hθφ + -- concavity of `C` puts the midpoint's saving below the average + have hCc : θ * C (g a) + φ * C (g b) ≤ C (θ * g a + φ * g b) := by + simpa only [smul_eq_mul] using hC.2 hA₁ hA₂ hθ hφ hθφ + have hle : g (θ * a + φ * b) + C (g (θ * a + φ * b)) + ≤ (θ * g a + φ * g b) + C (θ * g a + φ * g b) := by + rw [← h3, hcomb]; linarith + have hAle : g (θ * a + φ * b) ≤ θ * g a + φ * g b := by + by_contra hcon + exact absurd (hM hAm hAθ (not_le.mp hcon)) (not_lt.mpr hle) + -- and the budget converts that into the concavity of the consumption function + rw [hh a ha, hh b hb, hh _ hm] + have hval : C (g (θ * a + φ * b)) + = (θ * g a + φ * g b) + (θ * C (g a) + φ * C (g b)) - g (θ * a + φ * b) := by + rw [← hcomb, h3]; ring + rw [hval] + linarith + +/-! ### Carroll and Kimball for CRRA, from the first-order condition + +With CRRA marginal utility `u' c = c ^ (-γ)` the first-order condition says exactly that the +consumption paired with saving `A` is a positive multiple of the weighted power mean, exponent +`-γ`, of next period's consumptions at `A`. `Analysis/PowerMean` makes that map concave and +nondecreasing, and the transfer above does the rest. -/ + +/-- **The endogenous-gridpoint map for CRRA.** The consumption a household with continuation `v` +pairs with end-of-period assets `A`, read off the Euler equation. -/ +noncomputable def egmMap (v : (ℝ × Z) →ᵇ ℝ) (γ : ℝ) (z : Z) (A : ℝ) : ℝ := + ((P.discount : ℝ) * (1 + P.interest)) ^ (-(1 / γ)) + * powerMean (P.transitionMatrix z) (-γ) fun z' => P.consumptionFnOf v z' A + +theorem concaveOn_egmMap {γ : ℝ} (hγ : 0 < γ) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.egmMap v γ z) := by + have hmean := concaveOn_powerMean_comp (convex_Icc assetFloor assetCap) + (neg_neg_of_pos hγ) (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hpos hconc + have hk : (0 : ℝ) ≤ ((P.discount : ℝ) * (1 + P.interest)) ^ (-(1 / γ)) := + Real.rpow_nonneg (mul_nonneg P.discount.coe_nonneg P.interest_gt_neg_one.le) _ + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hstep := hmean.2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hstep ⊢ + simp only [egmMap] + linarith [mul_le_mul_of_nonneg_left hstep hk] + +theorem monotoneOn_egmMap {γ : ℝ} (hγ : 0 < γ) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) : + MonotoneOn (P.egmMap v γ z) (Icc assetFloor assetCap) := by + have hmean := monotoneOn_powerMean_comp (D := Icc assetFloor assetCap) + (neg_neg_of_pos hγ) (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hpos hmono + intro x hx y hy hxy + exact mul_le_mul_of_nonneg_left (hmean hx hy hxy) + (Real.rpow_nonneg (mul_nonneg P.discount.coe_nonneg P.interest_gt_neg_one.le) _) + +/-- **Carroll and Kimball (1996), the induction step, for CRRA.** Given the Euler equation, the +Bellman operator carries a concave consumption function to a concave consumption function. + +Everything except `hEuler` is now proved: the power-mean concavity that Carroll and Kimball +obtain from Kimball's risk-tolerance aggregation, and the endogenous-gridpoint transfer that +turns it into concavity of the consumption function. `hEuler` is the first-order condition of +the maximisation, which holds wherever the choice is interior — see the module docstring. -/ +theorem concaveOn_consumptionFnOf_bellman_of_euler {γ : ℝ} (hγ : 0 < γ) {v : (ℝ × Z) →ᵇ ℝ} + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) (z : Z) + (hEuler : ∀ a ∈ Icc assetFloor assetCap, + P.consumptionFnOf (P.toExtended.bellman v) z a + = P.egmMap v γ z (P.policyOf (P.toExtended.bellman v) (a, z))) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_of_egm (z := z) (g := fun a => P.policyOf (P.toExtended.bellman v) (a, z)) + (C := P.egmMap v γ z) (P.concaveOn_egmMap hγ z hpos hconc) + (strictMonoOn_add_egm (P.monotoneOn_egmMap hγ z hpos hmono)) + (fun a _ => P.feasible_subset_region (P.policyOf_mem _ (a, z))) (fun a ha => ?_) + (fun a ha => hEuler a ha) + rw [← hEuler a ha] + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + simp only [consumptionFnOf, consumption, hres] + ring + +/-! ### The asset cap obstructs concavity, and `hT` is stated too strongly + +Attempting `hT` for log utility — the one CRRA specification this structure admits with Light's +condition, by `crra_pinch` — turns up an obstruction that has nothing to do with Carroll and +Kimball and everything to do with the cap. + +Where the cap binds the household cannot save more, so every extra unit of resources is consumed +and the consumption function has slope exactly `1 + r`. Below, wherever it saves strictly more, +the slope is strictly less. Increments therefore RISE across the kink, which concavity forbids. +`not_concaveOn_consumptionFn_of_cap_binds` proves it. + +So `hT` as stated is not merely hard, it is FALSE for any calibration whose cap binds, quite +apart from the HARA question. Concavity of the consumption function on the asset region needs the +cap to be SLACK — above the natural asset bound, so that the household never wants to save that +much. That is exactly what `exists_decline_of_consumption_lower_bound` is about, and by +`one_sub_mpc_mul_of_asymptotic` the condition behind it is `β (1 + r) < 1`. + +Which ties the two halves of the development together: the imposed `assetCap`, so far a +bookkeeping device for compactness, has to be justified economically before the Carroll–Kimball +hypothesis can even be true. -/ + +/-- Once the cap binds it binds for ever after, since the policy is monotone and capped. -/ +theorem policy_eq_assetCap_of_le {y w : ℝ} {z : Z} (hy : y ∈ Icc assetFloor assetCap) + (hw : w ∈ Icc assetFloor assetCap) (hyw : y ≤ w) (h : P.policy (y, z) = assetCap) : + P.policy (w, z) = assetCap := by + exact le_antisymm (P.policy_mem_region _).2 + (le_trans (le_of_eq h.symm) (P.policy_mono hy hw hyw)) + +/-- **A binding asset cap rules out a concave consumption function.** -/ +theorem not_concaveOn_consumptionFn_of_cap_binds {x y w : ℝ} {z : Z} + (hx : x ∈ Icc assetFloor assetCap) (hw : w ∈ Icc assetFloor assetCap) + (hxy : x < y) (hyw : y < w) + (hrise : P.policy (x, z) < P.policy (y, z)) + (hflat : P.policy (y, z) = P.policy (w, z)) : + ¬ ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + intro hconc + have hy : y ∈ Icc assetFloor assetCap := ⟨le_trans hx.1 hxy.le, le_trans hyw.le hw.2⟩ + have h := hconc.slope_anti_adjacent hx hw hxy hyw + have hres : ∀ a ∈ Icc assetFloor assetCap, + P.resources (a, z) = P.income z + (1 + P.interest) * a := + fun a ha => by simp only [resources, max_eq_right ha.1] + simp only [consumptionFn, consumption, hres x hx, hres y hy, hres w hw] at h + rw [div_le_div_iff₀ (by linarith) (by linarith)] at h + nlinarith [h, hrise, hflat, sub_pos.mpr hxy, sub_pos.mpr hyw] + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean new file mode 100644 index 000000000..20a184153 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean @@ -0,0 +1,252 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone +public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion + + +/-! +# The consumption function + +`consumptionFn z a` is what the household actually eats at assets `a`: cash on hand less the +optimal saving. Light (2018) Theorem 1 needs three things from it — positivity, monotonicity, +and CONCAVITY in assets (his Lemma 4). The first two are proved here. The third is not, and +this file says exactly why. + +## Consumption rises with assets + +`consumptionFn_mono` is the theorem of this file, and it is the mirror image of `policy_mono`. +That one swapped the two households' savings and used increasing differences of `u`; this one +swaps them by the SAME resource gap, `b ↦ b + Δ` and `b' ↦ b' - Δ`, which leaves every +consumption level untouched and moves only the continuation. So the roles are exchanged: the +argument runs on increasing differences of the CONTINUATION, and it is again +`ConcaveOn.sub_le_sub_of_shift` — applied to `cont` rather than to `u`. + +Together the two say the marginal propensities are both non-negative: + + `0 ≤ c(a') - c(a) ≤ resources(a') - resources(a)` for `a ≤ a'`, + +which is `sub_le_sub_of_le_resources`. Neither half needs a derivative. + +## Concavity is NOT free, and this file does not prove it + +It would be pleasant if concavity of `c` followed from concavity of `u` and of the value +function, the way monotonicity does. It does not. With CRRA `u` and a continuation of constant +absolute risk tolerance, the marginal propensity to consume is `T_u(c) / (T_u(c) + T_W(b))` +with `T_u(c) = c/γ` and `T_W` constant, which RISES with consumption — so the consumption +function is CONVEX. Concavity therefore cannot follow from concavity of the two primitives; it +needs the continuation to inherit the curvature of `u`, which is what makes it a statement +about the FIXED POINT rather than about one application of the operator. + +That is the Carroll and Kimball (1996) theorem — the Bellman operator preserves concavity of +the consumption function when utility is HARA — and it is the shape of the closed-class +arguments already used here for monotonicity, concavity, Lipschitz bounds and increasing +differences. Light states it as his Lemma 4 and cites Jensen (2017) rather than proving it. +It is left as a hypothesis: `mul_marginal_le_of_scale` takes it as an argument, and everything +else that theorem needs about the consumption function is discharged here. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **The continuation value is concave in savings**, being a non-negative combination of the +value function's concave slices. -/ +theorem concaveOn_cont (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.cont z) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + simp only [cont, smul_eq_mul] + rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hvz := (P.concaveOn_valueFunction z').2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hvz + nlinarith [P.transitionMatrix_nonneg z z'] + +/-- Consumption at the optimum, as a function of assets. -/ +noncomputable def consumptionFn (z : Z) (a : ℝ) : ℝ := + P.consumption (a, z) (P.policy (a, z)) + +theorem consumptionFn_pos (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) + (z : Z) : + 0 < P.consumptionFn z a := + P.consumption_policy_pos hpc (s := (a, z)) ha + +/-- The saving cap cannot grow by more than resources do. -/ +theorem maxSaving_le_add_sub {a a' : ℝ} {z : Z} (hle : a ≤ a') : + P.maxSaving (a', z) + ≤ P.maxSaving (a, z) + (P.resources (a', z) - P.resources (a, z)) := by + have hΔ : 0 ≤ P.resources (a', z) - P.resources (a, z) := by + linarith [P.resources_mono (z := z) hle] + have hmin : min assetCap (P.resources (a', z)) + ≤ min assetCap (P.resources (a, z)) + (P.resources (a', z) - P.resources (a, z)) := by + rcases le_total assetCap (P.resources (a, z)) with h | h + · rw [min_eq_left h] + exact le_trans (min_le_left _ _) (by linarith) + · rw [min_eq_right h] + exact le_trans (min_le_right _ _) (by linarith) + rw [P.maxSaving_eq, P.maxSaving_eq] + linarith + +/-- **Consumption rises with assets.** The two households swap savings plans shifted by their +resource gap, which leaves consumption alone and moves only the continuation — so the +comparison is increasing differences of the continuation, exactly as `policy_mono` is +increasing differences of `u`. -/ +theorem consumptionFn_mono {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) + (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.consumptionFn z a ≤ P.consumptionFn z a' := by + set Δ : ℝ := P.resources (a', z) - P.resources (a, z) with hΔdef + have hΔ : 0 ≤ Δ := by + rw [hΔdef]; linarith [P.resources_mono (z := z) hle] + set b : ℝ := P.policy (a, z) with hbdef + set b' : ℝ := P.policy (a', z) with hb'def + -- the goal is `b' ≤ b + Δ` + by_contra hcon + rw [not_le] at hcon + have hgap : b + Δ < b' := by + simp only [consumptionFn, consumption] at hcon + rw [hΔdef]; linarith + have hbmem : b ∈ P.toExtended.feasible (a, z) := P.policy_mem _ + have hb'mem : b' ∈ P.toExtended.feasible (a', z) := P.policy_mem _ + rw [P.feasible_eq] at hbmem hb'mem + -- both shifted plans are feasible + have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + refine ⟨by linarith [hbmem.1], ?_⟩ + have := P.maxSaving_le_add_sub (z := z) hle + rw [← hΔdef] at this + linarith [hb'mem.2] + -- the shift is consumption-neutral, so only two consumption levels appear + have hc1 : P.consumption (a, z) b ∈ P.dom := P.consumption_policy_mem_dom (s := (a, z)) ha + have hc2 : P.consumption (a', z) b' ∈ P.dom := + P.consumption_policy_mem_dom (s := (a', z)) ha' + have he1 : P.consumption (a', z) (b + Δ) = P.consumption (a, z) b := by + simp only [consumption, hΔdef]; ring + have he2 : P.consumption (a, z) (b' - Δ) = P.consumption (a', z) b' := by + simp only [consumption, hΔdef]; ring + -- optimality at each state, against the other's shifted plan + have hI := P.objR_le_of_mem ha hshiftdown (by rw [he2]; exact hc2) + have hII := P.objR_le_of_mem ha' hshiftup (by rw [he1]; exact hc1) + -- increasing differences of the continuation + have hbreg : b ∈ Icc assetFloor assetCap := P.policy_mem_region _ + have hb'reg : b' ∈ Icc assetFloor assetCap := P.policy_mem_region _ + have hshift := (P.concaveOn_cont z).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) + hbreg (by simpa using hb'reg) (by linarith) hΔ + rw [sub_add_cancel] at hshift + -- the two optimality inequalities therefore hold with equality + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hshift hβ + simp only [objR, he1, he2] at hI hII + have hIIeq : P.objR (a', z) (b + Δ) = P.objR (a', z) b' := by + simp only [objR, he1] + nlinarith [hI, hII, hscaled] + -- so the shifted plan attains the optimum at the richer state, and uniqueness closes it + have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z) (b + Δ) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe ha' hshiftup (by rw [he1]; exact hc1), hIIeq, + ← P.objectiveE_eq_coe ha' (P.policy_mem _) hc2] + exact P.policy_optimal _ + exact absurd (P.eq_policy_of_optimal ha' hshiftup hbell) (by rw [← hb'def]; linarith) + +/-- **The marginal propensities are both non-negative.** Consumption rises with assets, and by +no more than resources do — the second half is `policy_mono`. -/ +theorem sub_le_sub_of_le_resources {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) + (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + 0 ≤ P.consumptionFn z a' - P.consumptionFn z a ∧ + P.consumptionFn z a' - P.consumptionFn z a + ≤ P.resources (a', z) - P.resources (a, z) := by + refine ⟨by linarith [P.consumptionFn_mono (z := z) ha ha' hle], ?_⟩ + have := P.policy_mono (z := z) ha ha' hle + simp only [consumptionFn, consumption] + linarith + +theorem monotoneOn_consumptionFn (z : Z) : + MonotoneOn (P.consumptionFn z) (Icc assetFloor assetCap) := + fun _ ha _ ha' h => P.consumptionFn_mono ha ha' h + +/-! ### A linear lower bound on consumption makes assets decline + +Ma and Toda (2022) prove that the consumption function is asymptotically LINEAR whenever +marginal utility is regularly varying, and characterise the slope. Their Corollary 2.10 covers +this model — constant discount factor and return, bounded relative risk aversion, `R ≥ 1` — and +gives the asymptotic marginal propensity to consume in closed form, + + `c_bar = 1 - (β R^(1-γ))^(1/γ)`. + +That result is about the UNCAPPED problem and is not proved here. What is proved here is the +consequence, which is what this development actually wants: a linear lower bound on consumption +forces assets to fall, above an explicit threshold. That is precisely the hypothesis `hdecl` of +`exists_exhaust_of_decline`, on which the whole Doeblin uniqueness and convergence argument +rests, and which has been a raw assumption until now. + +The two fit together exactly. With Ma and Toda's slope the asymptotic growth factor of assets is +`(1 - c_bar) R = (β R)^(1/γ)` — `one_sub_mpc_mul_of_asymptotic`, below — so the condition +`(1 - ε) (1 + r) < 1` demanded by `policy_lt_self_of_consumption_lower_bound` is exactly `β R < 1`. +Impatience, and nothing else, is what bounds assets. -/ + +/-- **The asymptotic growth factor of assets is `(β R)^(1/γ)`.** With Ma and Toda's asymptotic +MPC `c_bar = 1 - (β R^(1-γ))^(1/γ)`, the factor `(1 - c_bar) R` by which assets grow collapses to +`(β R)^(1/γ)`, so assets are eventually contracted exactly when `β R < 1`. -/ +theorem one_sub_mpc_mul_of_asymptotic {β R γ : ℝ} (hβ : 0 ≤ β) (hR : 0 < R) (hγ : 0 < γ) : + (β * R ^ (1 - γ)) ^ (1 / γ) * R = (β * R) ^ (1 / γ) := by + have h1 : (R ^ (1 - γ)) ^ (1 / γ) * R = R ^ (1 / γ) := by + have hadd : R ^ ((1 - γ) * (1 / γ)) * R ^ (1 : ℝ) = R ^ ((1 - γ) * (1 / γ) + 1) := + (Real.rpow_add hR _ _).symm + rw [Real.rpow_one] at hadd + rw [← Real.rpow_mul hR.le, hadd, show (1 - γ) * (1 / γ) + 1 = 1 / γ by field_simp; ring] + rw [Real.mul_rpow hβ (Real.rpow_nonneg hR.le _), mul_assoc, h1, ← Real.mul_rpow hβ hR.le] + +/-- **A household that consumes a fixed share of cash on hand runs its assets down**, once +assets pass an explicit threshold. The share `ε` has to be large enough that `(1 - ε)(1 + r) < 1` +— with Ma and Toda's asymptotic MPC that is exactly `β(1 + r) < 1`. -/ +theorem policy_lt_self_of_consumption_lower_bound {z : Z} {ε a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hlb : ε * (P.resources (a, z) - assetFloor) ≤ P.consumptionFn z a) + (hgt : (1 - ε) * P.income z + ε * assetFloor + < (1 - (1 - ε) * (1 + P.interest)) * a) : + P.policy (a, z) < a := by + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + have hpol : P.policy (a, z) + = P.income z + (1 + P.interest) * a - P.consumptionFn z a := by + simp only [consumptionFn, consumption, hres]; ring + rw [hres] at hlb + rw [hpol] + have e1 : P.income z + (1 + P.interest) * a - P.consumptionFn z a + ≤ (1 - ε) * P.income z + (1 - ε) * ((1 + P.interest) * a) + ε * assetFloor := by + nlinarith [hlb] + rw [show (1 - (1 - ε) * (1 + P.interest)) * a + = a - (1 - ε) * ((1 + P.interest) * a) by ring] at hgt + linarith + +/-- **Every asset level above an explicit threshold declines**, given a linear lower bound on +consumption. This is the hypothesis `hdecl` of `exists_exhaust_of_decline`. -/ +theorem exists_decline_of_consumption_lower_bound {z : Z} {ε : ℝ} + (hε : (1 - ε) * (1 + P.interest) < 1) + (hlb : ∀ a ∈ Icc assetFloor assetCap, + ε * (P.resources (a, z) - assetFloor) ≤ P.consumptionFn z a) : + ∃ ā : ℝ, ∀ a ∈ Icc assetFloor assetCap, ā < a → P.policy (a, z) < a := by + refine ⟨((1 - ε) * P.income z + ε * assetFloor) + / (1 - (1 - ε) * (1 + P.interest)), fun a ha hā => ?_⟩ + refine P.policy_lt_self_of_consumption_lower_bound ha (hlb a ha) ?_ + rw [div_lt_iff₀ (by linarith)] at hā + linarith + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean new file mode 100644 index 000000000..19d897558 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean @@ -0,0 +1,323 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds + + +/-! +# Comparing two interest rates + +Estimates relating two household problems that share an income process and an asset cap but +differ in the interest rate. These are the cross-rate ingredients of the sup-norm estimate on +the Bellman operator; they are stated for two programs rather than for a family, which is +more flexible and needs no indexing. + +They are only expressible at all because `assetCap` is a structure PARAMETER: `P` and `Q` +below have the same type, so their state spaces and feasible sets are directly comparable. + +## The trap + +The obvious estimate is false as stated. Resources differ by + + resources_P s - resources_Q s = (P.interest - Q.interest) * max 0 s.1 + +which is UNBOUNDED in the state: a household with enormous assets sees an enormous change in +income from a small change in the rate. A sup-norm estimate over all states cannot come from +this quantity directly. + +What rescues it is that the difference is only visible through a clamp. Maximum feasible +saving is `min assetCap resources`, so once resources exceed the cap at BOTH rates the +difference is exactly zero. The difference can be nonzero only where some rate leaves +resources below the cap, and that confines the state: `(1 + r) * max 0 s.1 < assetCap`. So +wherever the estimate is not trivially zero, the offending factor is bounded — by +`assetCap / (1 + rlo)`, with `rlo` any lower bound on the rates considered. + +That is the shape every cross-rate estimate here takes: a case split on whether the clamp +binds, trivial on one side, and bounded on the other precisely because the clamp failing to +bind confines the state. +-/ + +@[expose] public section + +open Set BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} + +/-- `min c ·` is 1-Lipschitz. A direct corollary of Mathlib's `lipschitzWith_min`. -/ +theorem abs_min_sub_min_le (c x y : ℝ) : |min c x - min c y| ≤ |x - y| := by + have h : LipschitzWith 1 fun t : ℝ => min c t := LipschitzWith.const_min LipschitzWith.id c + simpa [Real.dist_eq] using h.dist_le_mul x y + +/-- Resources differ by the rate gap times assets — a quantity unbounded in the state. -/ +theorem resources_sub {P Q : IncomeFluctuation Z assetFloor assetCap} (hinc : P.income = Q.income) + (s : ℝ × Z) : + P.resources s - Q.resources s = (P.interest - Q.interest) * max assetFloor s.1 := by + simp only [resources, hinc] + ring + +/-- The uniform bound on the state coordinate that the confinement lemmas deliver. With a +borrowing limit the coordinate can be negative, so what is bounded is its ABSOLUTE value, and +the limit contributes its own modulus to the constant. -/ +noncomputable def stateBoundCross (P : IncomeFluctuation Z assetFloor assetCap) (rlo : ℝ) : ℝ := + |assetFloor| + |assetCap| + (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) + +theorem maxIncome_pos (P : IncomeFluctuation Z assetFloor assetCap) : 0 < P.maxIncome := + lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome + +theorem stateBoundCross_nonneg (P : IncomeFluctuation Z assetFloor assetCap) {rlo : ℝ} + (hr : 0 < 1 + rlo) : 0 ≤ P.stateBoundCross rlo := by + have h1 : 0 ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := by + have := P.maxIncome_pos + positivity + have := abs_nonneg assetFloor + have := abs_nonneg assetCap + simp only [stateBoundCross]; linarith + +theorem abs_assetCap_le_stateBoundCross (P : IncomeFluctuation Z assetFloor assetCap) {rlo : ℝ} + (hr : 0 < 1 + rlo) : |assetCap| ≤ P.stateBoundCross rlo := by + have h1 : 0 ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := by + have := P.maxIncome_pos + positivity + have := abs_nonneg assetFloor + simp only [stateBoundCross]; linarith + +/-- The elementary division step the two confinement lemmas share. -/ +private theorem div_le_div_of_le_denom {a b c : ℝ} (ha : 0 ≤ a) (hb : 0 < b) (hc : 0 < c) + (hbc : b ≤ c) : a / c ≤ a / b := by + rw [div_le_div_iff₀ hc hb] + nlinarith + +/-- **Where the cap fails to bind, the state is confined.** This is what tames the unbounded +factor above. -/ +theorem abs_max_le_of_resources_lt {P : IncomeFluctuation Z assetFloor assetCap} {rlo : ℝ} + (hr : 0 < 1 + rlo) (hP : rlo ≤ P.interest) {s : ℝ × Z} (hlt : P.resources s < assetCap) : + |max assetFloor s.1| ≤ P.stateBoundCross rlo := by + have hp1 : 0 < 1 + P.interest := P.interest_gt_neg_one + have hy : 0 < P.income s.2 := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le s.2) + have hres : P.resources s = P.income s.2 + (1 + P.interest) * max assetFloor s.1 := rfl + have hcapabs := le_abs_self assetCap + have hfl := abs_nonneg assetFloor + have hca := abs_nonneg assetCap + have hmi := P.maxIncome_pos + have hup : max assetFloor s.1 ≤ |assetCap| / (1 + P.interest) := + (le_div_iff₀ hp1).mpr (by linarith) + have hmono : |assetCap| / (1 + P.interest) ≤ |assetCap| / (1 + rlo) := + div_le_div_of_le_denom hca hr hp1 (by linarith) + have hnum : |assetCap| / (1 + rlo) + ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := by + rw [div_le_div_iff₀ hr hr] + nlinarith + have hlow : -|assetFloor| ≤ max assetFloor s.1 := + le_trans (neg_abs_le _) (le_max_left _ _) + have hdiv : 0 ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := by positivity + rw [abs_le] + simp only [stateBoundCross] + constructor <;> linarith + +/-- **The maximum feasible saving moves little when the interest rate does.** + +The bound is uniform over the whole state space, which the raw resources gap is not. -/ +theorem abs_maxSaving_sub_le {P Q : IncomeFluctuation Z assetFloor assetCap} + (hinc : P.income = Q.income) (hmi : P.maxIncome = Q.maxIncome) + {rlo : ℝ} (hr : 0 < 1 + rlo) (hP : rlo ≤ P.interest) (hQ : rlo ≤ Q.interest) (s : ℝ × Z) : + |P.maxSaving s - Q.maxSaving s| + ≤ |P.interest - Q.interest| * P.stateBoundCross rlo := by + rw [P.maxSaving_eq, Q.maxSaving_eq] + rcases lt_or_ge (P.resources s) assetCap with hPlt | hPge + · -- the cap fails to bind at `P`, so the state is confined + refine le_trans (abs_min_sub_min_le _ _ _) ?_ + rw [resources_sub hinc, abs_mul] + exact mul_le_mul_of_nonneg_left (abs_max_le_of_resources_lt hr hP hPlt) (abs_nonneg _) + · rcases lt_or_ge (Q.resources s) assetCap with hQlt | hQge + · -- symmetrically at `Q` + refine le_trans (abs_min_sub_min_le _ _ _) ?_ + rw [resources_sub hinc, abs_mul] + refine mul_le_mul_of_nonneg_left ?_ (abs_nonneg _) + have := abs_max_le_of_resources_lt hr hQ hQlt + simpa only [stateBoundCross, hmi] using this + · -- the cap binds at both rates: the difference is exactly zero + rw [min_eq_left hPge, min_eq_left hQge, sub_self, abs_zero] + exact mul_nonneg (abs_nonneg _) (P.stateBoundCross_nonneg hr) + +/-! ### The clamped consumption gap + +Harder than the saving bound in two ways. The clamp LEVEL `maxConsumption` now moves with the +rate too, since it is `maxIncome + (1 + r) * assetCap - assetFloor`; and the chosen saving has +to be carried along, which is where the `θ` reparametrisation earns its place — with the choice +written as the borrowing limit plus a fraction of the saving room, the same `θ` can be fed to +both programs. -/ + +/-- `min` is 1-Lipschitz in both arguments at once: `lipschitzWith_min` read through the +product's supremum metric. -/ +theorem abs_min_sub_min_le_max (A B x y : ℝ) : + |min A x - min B y| ≤ max |A - B| |x - y| := by + simpa [Prod.dist_eq, Real.dist_eq] using lipschitzWith_min.dist_le_mul (A, x) (B, y) + +/-- The clamp level itself moves with the rate, but only by a bounded amount. -/ +theorem abs_maxConsumption_sub {P Q : IncomeFluctuation Z assetFloor assetCap} + (hmax : P.maxIncome = Q.maxIncome) : + |P.maxConsumption - Q.maxConsumption| = |P.interest - Q.interest| * |assetCap| := by + have hexp : P.maxConsumption - Q.maxConsumption + = (P.interest - Q.interest) * assetCap := by + simp only [maxConsumption, hmax]; ring + rw [hexp, abs_mul] + +/-- **Where the consumption clamp fails to bind, the state is confined.** The same move as +`abs_max_le_of_resources_lt`, one clamp further along. -/ +theorem abs_max_le_of_consumption_lt {P : IncomeFluctuation Z assetFloor assetCap} {rlo : ℝ} + (hr : 0 < 1 + rlo) (hP : rlo ≤ P.interest) {θ : ℝ} (hθ : θ ∈ Icc (0 : ℝ) 1) {s : ℝ × Z} + (hlt : P.consumption s (assetFloor + θ * P.savingRoom s) < P.maxConsumption) : + |max assetFloor s.1| ≤ P.stateBoundCross rlo := by + have hp1 : 0 < 1 + P.interest := P.interest_gt_neg_one + have hmi := P.maxIncome_pos + have hy : 0 < P.income s.2 := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le s.2) + have hres : P.resources s = P.income s.2 + (1 + P.interest) * max assetFloor s.1 := rfl + have hmc : P.maxConsumption = P.maxIncome + (1 + P.interest) * assetCap - assetFloor := rfl + have hcons : P.consumption s (assetFloor + θ * P.savingRoom s) + = P.resources s - (assetFloor + θ * P.savingRoom s) := rfl + have hroom := P.savingRoom_nonneg s + have hroomle : P.savingRoom s ≤ assetCap - assetFloor := by + simp only [savingRoom]; linarith [P.maxSaving_le_assetCap s] + -- saving at most the cap, so consumption is at least resources minus the cap + have hsav : assetFloor + θ * P.savingRoom s ≤ assetCap := by nlinarith [hθ.1, hθ.2] + have hcapabs := le_abs_self assetCap + have hflabs : -assetFloor ≤ |assetFloor| := neg_le_abs _ + have hfl := abs_nonneg assetFloor + have hca := abs_nonneg assetCap + have hcapmul : (1 + P.interest) * assetCap ≤ (1 + P.interest) * |assetCap| := + mul_le_mul_of_nonneg_left hcapabs hp1.le + have hstep : (max assetFloor s.1 - |assetCap|) * (1 + P.interest) + < P.maxIncome + |assetFloor| + |assetCap| := by + rw [hcons, hres, hmc] at hlt + nlinarith + have hlt' : max assetFloor s.1 - |assetCap| + < (P.maxIncome + |assetFloor| + |assetCap|) / (1 + P.interest) := + (lt_div_iff₀ hp1).mpr hstep + have hdiv : (P.maxIncome + |assetFloor| + |assetCap|) / (1 + P.interest) + ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := + div_le_div_of_le_denom (by linarith) hr hp1 (by linarith) + have hlow : -|assetFloor| ≤ max assetFloor s.1 := + le_trans (neg_abs_le _) (le_max_left _ _) + have hdivnn : 0 ≤ (P.maxIncome + |assetFloor| + |assetCap|) / (1 + rlo) := by positivity + rw [abs_le] + simp only [stateBoundCross] + constructor <;> linarith + +/-- **The clamped consumption gap.** Uniform over the state space and over the choice `θ`. -/ +theorem abs_clamped_consumption_sub_le {P Q : IncomeFluctuation Z assetFloor assetCap} + (hinc : P.income = Q.income) (hmax : P.maxIncome = Q.maxIncome) + {rlo : ℝ} (hr : 0 < 1 + rlo) (hP : rlo ≤ P.interest) (hQ : rlo ≤ Q.interest) + {θ : ℝ} (hθ : θ ∈ Icc (0 : ℝ) 1) (s : ℝ × Z) : + |min P.maxConsumption (P.consumption s (assetFloor + θ * P.savingRoom s)) + - min Q.maxConsumption (Q.consumption s (assetFloor + θ * Q.savingRoom s))| + ≤ |P.interest - Q.interest| * (2 * P.stateBoundCross rlo) := by + have hgap : (0 : ℝ) ≤ |P.interest - Q.interest| := abs_nonneg _ + have hB : 0 ≤ P.stateBoundCross rlo := P.stateBoundCross_nonneg hr + have hBQ : Q.stateBoundCross rlo = P.stateBoundCross rlo := by + simp only [stateBoundCross, hmax] + -- the gap in unclamped consumption, given a bound on the state + have hcons : |max assetFloor s.1| ≤ P.stateBoundCross rlo → + |P.consumption s (assetFloor + θ * P.savingRoom s) + - Q.consumption s (assetFloor + θ * Q.savingRoom s)| + ≤ |P.interest - Q.interest| * (2 * P.stateBoundCross rlo) := by + intro hBd + have hms := abs_maxSaving_sub_le hinc hmax hr hP hQ s + have hres := resources_sub hinc s + have hexp : P.consumption s (assetFloor + θ * P.savingRoom s) + - Q.consumption s (assetFloor + θ * Q.savingRoom s) + = (P.resources s - Q.resources s) - θ * (P.maxSaving s - Q.maxSaving s) := by + simp only [consumption, savingRoom]; ring + rw [hexp, hres] + have h1 : |(P.interest - Q.interest) * max assetFloor s.1| + ≤ |P.interest - Q.interest| * P.stateBoundCross rlo := by + rw [abs_mul] + exact mul_le_mul_of_nonneg_left hBd hgap + have h2 : |θ * (P.maxSaving s - Q.maxSaving s)| + ≤ |P.interest - Q.interest| * P.stateBoundCross rlo := by + rw [abs_mul, abs_of_nonneg hθ.1] + calc θ * |P.maxSaving s - Q.maxSaving s| + ≤ 1 * |P.maxSaving s - Q.maxSaving s| := + mul_le_mul_of_nonneg_right hθ.2 (abs_nonneg _) + _ = |P.maxSaving s - Q.maxSaving s| := one_mul _ + _ ≤ _ := hms + have h3 := abs_sub (((P.interest - Q.interest) * max assetFloor s.1)) + (θ * (P.maxSaving s - Q.maxSaving s)) + nlinarith [h1, h2, h3] + -- the clamp-level gap is always available as a bound + have hlevel : |P.maxConsumption - Q.maxConsumption| + ≤ |P.interest - Q.interest| * (2 * P.stateBoundCross rlo) := by + rw [abs_maxConsumption_sub hmax] + refine mul_le_mul_of_nonneg_left ?_ hgap + linarith [P.abs_assetCap_le_stateBoundCross hr] + rcases le_or_gt P.maxConsumption (P.consumption s (assetFloor + θ * P.savingRoom s)) + with hPge | hPlt + · rcases le_or_gt Q.maxConsumption (Q.consumption s (assetFloor + θ * Q.savingRoom s)) + with hQge | hQlt + · -- BOTH clamps bind, so the two operators see the same clamped value and the + -- unclamped gap -- which may be enormous -- is invisible + rw [min_eq_left hPge, min_eq_left hQge] + exact hlevel + · -- `Q`'s clamp fails to bind, which confines the state + refine le_trans (abs_min_sub_min_le_max _ _ _ _) (max_le hlevel ?_) + refine hcons ?_ + have := abs_max_le_of_consumption_lt hr hQ hθ hQlt + rwa [hBQ] at this + · -- `P`'s clamp fails to bind, which confines the state + refine le_trans (abs_min_sub_min_le_max _ _ _ _) (max_le hlevel ?_) + exact hcons (abs_max_le_of_consumption_lt hr hP hθ hPlt) + +/-! ### Assembling: from pointwise bounds to a bound on the supremum + +The sup-norm estimate compares two suprema, and the reparametrisation has already made them +suprema over the SAME set `[0,1]`. What remains is to get from a bound on the objectives to a +bound on their suprema. + +A naive pointwise bound would not do, and the reason is worth stating. Where consumption is +tiny but positive, `u` of the two consumptions can differ by an arbitrary amount however close +the rates are, because `u` falls to `-∞`; so + + ∀ θ, objective_P θ ≤ objective_Q θ + ε + +is FALSE. Those actions are dominated -- consuming almost nothing is far worse than the +always-available choice `θ = 0` -- so they cannot affect either supremum, but a pointwise +statement cannot see that. + +The cutoff `L` is what encodes it. The hypothesis is only required at actions worth at least +`L`, and `L` is separately known to sit below the other program's supremum. Actions below `L` +are then bounded by `L` itself and need no comparison at all. In use, `L` will be the value of +`θ = 0`, which both programs have available. -/ + +theorem maxE_le_of_pointwise {P Q : IncomeFluctuation Z assetFloor assetCap} (v : (ℝ × Z) →ᵇ ℝ) + (s : ℝ × Z) (L : EReal) (ε : EReal) + (hL : L ≤ Q.toExtended.maxE v s + ε) + (h : ∀ θ ∈ Icc (0 : ℝ) 1, + L ≤ P.toExtended.objectiveE v s (assetFloor + θ * P.savingRoom s) → + P.toExtended.objectiveE v s (assetFloor + θ * P.savingRoom s) + ≤ Q.toExtended.objectiveE v s (assetFloor + θ * Q.savingRoom s) + ε) : + P.toExtended.maxE v s ≤ Q.toExtended.maxE v s + ε := by + rw [P.maxE_eq_sSup_unit] + refine sSup_le ?_ + rintro x ⟨θ, hθ, rfl⟩ + rcases le_or_gt L (P.toExtended.objectiveE v s (assetFloor + θ * P.savingRoom s)) + with hge | hlt + · -- worth at least the cutoff: the pointwise hypothesis applies + refine le_trans (h θ hθ hge) ?_ + have hmem : assetFloor + θ * Q.savingRoom s ∈ Q.toExtended.feasible s := by + rw [Q.feasible_eq_image]; exact ⟨θ, hθ, rfl⟩ + gcongr + exact le_maxValueE hmem + · -- below the cutoff: dominated, and bounded by the cutoff itself + exact le_trans hlt.le hL + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean new file mode 100644 index 000000000..3a74d8804 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean @@ -0,0 +1,201 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Analysis.DifferentiableSandwich +public import Mathlib.Analysis.Calculus.FDeriv.Extend + + +/-! +# The lazy agent's value, and the envelope condition + +Clausen and Strub's construction: to show the value function is differentiable in assets, +exhibit a differentiable LOWER bound touching it. Freeze the optimal saving and vary the +state — the "lazy" agent, who meets an unfamiliar state by making the choice that was optimal +at a familiar one. + +The point is structural. Once the saving is frozen, current assets enter the objective ONLY +through the reward, because the continuation value depends on the saving chosen and on the +current income state, never on current assets. So the lazy value is `u` composed with an +affine map plus a constant, and is differentiable wherever `u` is. + +Concavity of the value function, already proved, then supplies the upper half of the sandwich +via `ConcaveOn.hasDerivAt_of_lowerBound`, and the derivative is the envelope condition + + V'(a) = (1 + r) · u'(c), + +which is the expression Light (2018) Theorem 1 is built on. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **The lazy agent's value.** The saving `a'` is frozen; only current assets vary. -/ +noncomputable def lazyValue (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a' a : ℝ) : ℝ := + P.u (P.resources (a, z) - a') + + P.discount * ∑ z' : Z, P.transitionMatrix z z' * v (a', z') + +/-- Resources are affine in assets above the borrowing constraint. -/ +theorem hasDerivAt_resources {z : Z} {a : ℝ} (ha : assetFloor < a) : + HasDerivAt (fun x => P.resources (x, z)) (1 + P.interest) a := by + have heq : (fun x => P.resources (x, z)) =ᶠ[𝓝 a] fun x => P.income z + (1 + P.interest) * x := by + filter_upwards [lt_mem_nhds ha] with x hx + simp only [resources, max_eq_right hx.le] + refine HasDerivAt.congr_of_eventuallyEq ?_ heq + simpa using ((hasDerivAt_id a).const_mul (1 + P.interest)).const_add (P.income z) + +/-- **The lazy value is differentiable**, because current assets enter only through the +reward once the saving is frozen. -/ +theorem hasDerivAt_lazyValue (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a' : ℝ) {a du : ℝ} + (ha : assetFloor < a) + (hu : HasDerivAt P.u du (P.resources (a, z) - a')) : + HasDerivAt (P.lazyValue v z a') ((1 + P.interest) * du) a := by + have hinner : HasDerivAt (fun x => P.resources (x, z) - a') (1 + P.interest) a := + (P.hasDerivAt_resources ha).sub_const a' + have hcomp : HasDerivAt (fun x => P.u (P.resources (x, z) - a')) + (du * (1 + P.interest)) a := hu.comp a hinner + have hsum := hcomp.add_const + ((P.discount : ℝ) * ∑ z' : Z, P.transitionMatrix z z' * v (a', z')) + rw [mul_comm du (1 + P.interest)] at hsum + exact hsum + +/-- **The lazy value is a lower bound**: freezing a feasible saving can only do worse than +optimising. Stated on the utility DOMAIN, so that it survives the bounded family, where zero +consumption is admissible and positivity has to be earned. -/ +theorem lazyValue_le_dom (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a' a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hmem : a' ∈ P.toExtended.feasible (a, z)) (hc : P.consumption (a, z) a' ∈ P.dom) : + P.lazyValue v z a' a ≤ P.toExtended.bellmanFn v (a, z) := by + have hobj := P.toExtended.le_bellmanFn v hmem + have hrw := P.reward_eq_coe_dom (s := (a, z)) ha hmem hc + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + rw [ExtendedStochasticProgram.objectiveE, hrw, hdx, ← EReal.coe_add, + EReal.coe_le_coe_iff] at hobj + exact hobj + +theorem lazyValue_le (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a' a : ℝ} (ha : a ∈ Icc assetFloor assetCap) + (hmem : a' ∈ P.toExtended.feasible (a, z)) (hc : 0 < P.consumption (a, z) a') : + P.lazyValue v z a' a ≤ P.toExtended.bellmanFn v (a, z) := + P.lazyValue_le_dom v z ha hmem (P.mem_dom_of_pos hc) + +/-- At the optimum the lazy value touches the Bellman value. -/ +theorem lazyValue_eq_dom (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a' a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hmem : a' ∈ P.toExtended.feasible (a, z)) (hc : P.consumption (a, z) a' ∈ P.dom) + (hopt : P.toExtended.objectiveE v (a, z) a' + = ((P.toExtended.bellmanFn v (a, z) : ℝ) : EReal)) : + P.lazyValue v z a' a = P.toExtended.bellmanFn v (a, z) := by + have hrw := P.reward_eq_coe_dom (s := (a, z)) ha hmem hc + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + rw [ExtendedStochasticProgram.objectiveE, hrw, hdx, ← EReal.coe_add, + EReal.coe_eq_coe_iff] at hopt + exact hopt + +theorem lazyValue_eq (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a' a : ℝ} (ha : a ∈ Icc assetFloor assetCap) + (hmem : a' ∈ P.toExtended.feasible (a, z)) (hc : 0 < P.consumption (a, z) a') + (hopt : P.toExtended.objectiveE v (a, z) a' + = ((P.toExtended.bellmanFn v (a, z) : ℝ) : EReal)) : + P.lazyValue v z a' a = P.toExtended.bellmanFn v (a, z) := + P.lazyValue_eq_dom v z ha hmem (P.mem_dom_of_pos hc) hopt + +/-! ### The envelope theorem + +Concavity supplies the upper half of the sandwich, the lazy agent the lower half. The only +work is the neighbourhood: the frozen saving is feasible at the state it came from, but at +poorer states it need not be, so the lower bound is only local. Continuity of the feasible +boundary gives an interval on which it survives, provided the saving is interior there. -/ + +theorem hasDerivAt_valueFunction (hpc : P.PositiveConsumption) {z : Z} {a du : ℝ} + (ha : assetFloor < a) (hacap : a < assetCap) + (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) + fun x => P.toExtended.valueFunction (x, z)) + (hint : P.policy (a, z) < P.maxSaving (a, z)) + (hu : HasDerivAt P.u du (P.resources (a, z) - P.policy (a, z))) : + HasDerivAt (fun x => P.toExtended.valueFunction (x, z)) ((1 + P.interest) * du) a := by + set V := P.toExtended.valueFunction with hV + set a' := P.policy (a, z) with ha' + have hamem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := ⟨ha.le, hacap.le⟩ + have hc : 0 < P.consumption (a, z) a' := P.consumption_policy_pos hpc hamem + have ha'0 : assetFloor ≤ a' := (P.policy_mem (a, z)).1 + -- the frozen saving stays feasible, and consumption stays positive, near `a` + have hcms : ContinuousAt (fun x => P.maxSaving (x, z)) a := + (P.continuous_maxSaving.comp (continuous_id.prodMk continuous_const)).continuousAt + have hccon : ContinuousAt (fun x => P.consumption (x, z) a') a := + ((P.continuous_resources.comp (continuous_id.prodMk continuous_const)).sub + continuous_const).continuousAt + have hev : {x : ℝ | assetFloor < x ∧ x < assetCap ∧ a' < P.maxSaving (x, z) + ∧ 0 < P.consumption (x, z) a'} ∈ 𝓝 a := by + have h1 : ∀ᶠ x in 𝓝 a, assetFloor < x := lt_mem_nhds ha + have h2 : ∀ᶠ x in 𝓝 a, x < assetCap := gt_mem_nhds hacap + have h3 : ∀ᶠ x in 𝓝 a, a' < P.maxSaving (x, z) := hcms.eventually_const_lt hint + have h4 : ∀ᶠ x in 𝓝 a, 0 < P.consumption (x, z) a' := hccon.eventually_const_lt hc + filter_upwards [h1, h2, h3, h4] with x hx1 hx2 hx3 hx4 using ⟨hx1, hx2, hx3, hx4⟩ + obtain ⟨l, r, hmem, hsub⟩ := mem_nhds_iff_exists_Ioo_subset.mp hev + -- on that interval the lazy value is a lower bound + have hIsub : Ioo l r ⊆ Icc assetFloor assetCap := fun x hx => + ⟨(hsub hx).1.le, (hsub hx).2.1.le⟩ + have hlow : ∀ x ∈ Ioo l r, P.lazyValue V z a' x ≤ V (x, z) := by + intro x hx + obtain ⟨hx1, hx2, hx3, hx4⟩ := hsub hx + have hfe : a' ∈ P.toExtended.feasible (x, z) := ⟨ha'0, hx3.le⟩ + have := P.lazyValue_le V z ⟨hx1.le, hx2.le⟩ hfe hx4 + rwa [hV, show P.toExtended.bellmanFn P.toExtended.valueFunction (x, z) + = P.toExtended.valueFunction (x, z) from by + rw [← P.toExtended.bellman_apply, P.toExtended.bellman_valueFunction]] at this + -- and it touches at `a` + have htouch : P.lazyValue V z a' a = V (a, z) := by + have hfe : a' ∈ P.toExtended.feasible (a, z) := P.policy_mem (a, z) + have := P.lazyValue_eq V z hamem hfe hc (P.policy_optimal (a, z)) + rwa [hV, show P.toExtended.bellmanFn P.toExtended.valueFunction (a, z) + = P.toExtended.valueFunction (a, z) from by + rw [← P.toExtended.bellman_apply, P.toExtended.bellman_valueFunction]] at this + refine (hconc.subset hIsub (convex_Ioo l r)).hasDerivAt_of_lowerBound hmem + (Ioo_mem_nhds hmem.1 hmem.2) + ⟨(l + a) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, + by linarith [hmem.1, hmem.2]⟩ + ⟨(a + r) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, + by linarith [hmem.1, hmem.2]⟩ + hlow htouch (P.hasDerivAt_lazyValue V z a' ha hu) + +/-! ### At the borrowing constraint + +The sandwich does NOT extend to the left endpoint, and the failure is not technical. Its first +step is that `U - L` has a local minimum where the bounds touch, forcing the two derivatives to +agree; at an endpoint a minimum gives only `d' ≥ 0`, so the upper bound may be strictly +steeper and the derivative is not pinned. + +The right-derivative at the constraint is instead obtained as a LIMIT of the interior +derivatives, using that a derivative extends continuously to an endpoint +(`hasDerivWithinAt_Ici_of_tendsto_deriv`). This matters because `a = 0` is precisely the atom +the whole ergodic argument runs on, so a statement that stops short of it would stop short of +the states that carry the stationary distribution. -/ + +theorem hasDerivWithinAt_valueFunction_Ici {z : Z} {g : ℝ → ℝ} {e b : ℝ} (hb : 0 < b) + (hderiv : ∀ x ∈ Ioo (0 : ℝ) b, + HasDerivAt (fun y => P.toExtended.valueFunction (y, z)) (g x) x) + (hlim : Tendsto g (𝓝[>] (0 : ℝ)) (𝓝 e)) : + HasDerivWithinAt (fun y => P.toExtended.valueFunction (y, z)) e (Ici 0) 0 := by + have hmem : Ioo (0 : ℝ) b ∈ 𝓝[>] (0 : ℝ) := Ioo_mem_nhdsGT hb + refine hasDerivWithinAt_Ici_of_tendsto_deriv (s := Ioo 0 b) ?_ ?_ hmem ?_ + · exact fun x hx => ((hderiv x hx).differentiableAt).differentiableWithinAt + · exact (P.toExtended.valueFunction.continuous.comp + (continuous_id.prodMk continuous_const)).continuousWithinAt + · refine hlim.congr' ?_ + filter_upwards [hmem] with x hx + exact ((hderiv x hx).deriv).symm + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean new file mode 100644 index 000000000..503a1108c --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean @@ -0,0 +1,737 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEnvelope +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCarrollKimball +public import LeanPool.AiyagariEquilibrium.Models.CRRA + + +/-! +# The Euler equation along the iteration, and the borrowing limit + +Carroll and Kimball's induction runs along the ITERATES `Tⁿ 0`, so everything it needs must hold +one step of the iteration at a time, and at states where the borrowing limit BINDS. + +## The envelope condition + +`hasDerivAt_bellman` is Clausen and Strub's theorem for `bellman v` rather than for the fixed +point. Nothing in the lazy-agent argument used the fixed point: freezing a feasible saving and +varying current assets gives a differentiable lower bound touching `bellman v` at the optimum, +and concavity of the slice supplies the upper half of the sandwich. `euler_of_interior` reads +the first-order condition off it. + +## But the Euler equation does not need it + +The same freezing trick gives BOTH one-sided bounds on the marginal value of assets — carry the +poorer household's plan forward (`bellman_sub_ge`), or the richer household's plan back +(`bellman_sub_le`) — and those are enough. Comparing the optimum with a deviation of size `ε` +and letting `ε` shrink gives + +* `euler_le`, from saving MORE, which is feasible unless the household already saves the + maximum, and +* `euler_ge`, from saving LESS, which is feasible unless the household is at the borrowing + limit; + +and `euler_eq` is the two together. No envelope theorem, no second derivative: only that `u` is +differentiable at the consumptions concerned. + +## The borrowing limit + +That asymmetry is the whole point. At the limit `euler_ge` fails and the first-order condition +degenerates to the inequality `euler_le`, which in endogenous-gridpoint form says consumption is +at most `egmMap` (`egmMap_ge`). `concaveOn_of_egm_corner` shows that is enough: at a constrained +state the saving is `assetFloor`, which lies below ANY average of savings, and that is the only +thing the transfer of `IncomeFluctuationCarrollKimball` ever needed. So the kink costs nothing, +and `concaveOn_consumptionFnOf_bellman` is Carroll and Kimball's induction step with the +borrowing limit allowed to bind anywhere. + +What it still assumes is that the asset CAP is slack — a different condition, discharged by +calibration in `crra_policy_lt_assetCap` — and the induction hypotheses on the continuation's +consumption function. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### The envelope condition for an iterate -/ + +/-- **The envelope condition for `bellman v`.** The marginal value of assets is the gross +interest rate times the marginal utility of the consumption the household actually chooses. +Clausen and Strub's sandwich, run one step of the iteration at a time rather than at the fixed +point. -/ +theorem hasDerivAt_bellman {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a du : ℝ} + (ha : assetFloor < a) (hacap : a < assetCap) + (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) fun x => (P.toExtended.bellman v) (x, z)) + (hint : P.policyOf v (a, z) < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf v z a) + (hu : HasDerivAt P.u du (P.consumptionFnOf v z a)) : + HasDerivAt (fun x => (P.toExtended.bellman v) (x, z)) ((1 + P.interest) * du) a := by + set a' := P.policyOf v (a, z) with ha' + have hamem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := ⟨ha.le, hacap.le⟩ + have ha'0 : assetFloor ≤ a' := (P.policyOf_mem v (a, z)).1 + have hba : ∀ x : ℝ, P.toExtended.bellmanFn v (x, z) = (P.toExtended.bellman v) (x, z) := + fun x => by rw [← P.toExtended.bellman_apply] + -- the frozen saving stays feasible, and consumption stays positive, near `a` + have hcms : ContinuousAt (fun x => P.maxSaving (x, z)) a := + (P.continuous_maxSaving.comp (continuous_id.prodMk continuous_const)).continuousAt + have hccon : ContinuousAt (fun x => P.consumption (x, z) a') a := + ((P.continuous_resources.comp (continuous_id.prodMk continuous_const)).sub + continuous_const).continuousAt + have hev : {x : ℝ | assetFloor < x ∧ x < assetCap ∧ a' < P.maxSaving (x, z) + ∧ 0 < P.consumption (x, z) a'} ∈ 𝓝 a := by + have h1 : ∀ᶠ x in 𝓝 a, assetFloor < x := lt_mem_nhds ha + have h2 : ∀ᶠ x in 𝓝 a, x < assetCap := gt_mem_nhds hacap + have h3 : ∀ᶠ x in 𝓝 a, a' < P.maxSaving (x, z) := hcms.eventually_const_lt hint + have h4 : ∀ᶠ x in 𝓝 a, 0 < P.consumption (x, z) a' := hccon.eventually_const_lt hc + filter_upwards [h1, h2, h3, h4] with x hx1 hx2 hx3 hx4 using ⟨hx1, hx2, hx3, hx4⟩ + obtain ⟨l, r, hmem, hsub⟩ := mem_nhds_iff_exists_Ioo_subset.mp hev + have hIsub : Ioo l r ⊆ Icc assetFloor assetCap := fun x hx => + ⟨(hsub hx).1.le, (hsub hx).2.1.le⟩ + have hlow : ∀ x ∈ Ioo l r, P.lazyValue v z a' x ≤ (P.toExtended.bellman v) (x, z) := by + intro x hx + obtain ⟨hx1, hx2, hx3, hx4⟩ := hsub hx + have hfe : a' ∈ P.toExtended.feasible (x, z) := ⟨ha'0, hx3.le⟩ + have := P.lazyValue_le v z ⟨hx1.le, hx2.le⟩ hfe hx4 + rwa [hba x] at this + have htouch : P.lazyValue v z a' a = (P.toExtended.bellman v) (a, z) := by + have hfe : a' ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v (a, z) + have := P.lazyValue_eq v z hamem hfe hc (P.policyOf_optimal v (a, z)) + rwa [hba a] at this + refine (hconc.subset hIsub (convex_Ioo l r)).hasDerivAt_of_lowerBound hmem + (Ioo_mem_nhds hmem.1 hmem.2) + ⟨(l + a) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, + by linarith [hmem.1, hmem.2]⟩ + ⟨(a + r) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, + by linarith [hmem.1, hmem.2]⟩ + hlow htouch (P.hasDerivAt_lazyValue v z a' ha hu) + +/-! ### The two lazy bounds on the marginal value of assets + +Both directions come from the same trick: carry a plan that was optimal at one asset level over +to another. Neither needs the envelope theorem, and neither needs the borrowing limit to be +slack. -/ + +/-- **The poorer household's plan, carried forward.** The generalisation of +`valueFunction_sub_ge` to one step of the iteration. -/ +theorem bellman_sub_ge {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (hxy : x ≤ y) + (hc : 0 < P.consumptionFnOf v z x) : + P.u (P.consumptionFnOf v z x + (1 + P.interest) * (y - x)) - P.u (P.consumptionFnOf v z x) + ≤ (P.toExtended.bellman v) (y, z) - (P.toExtended.bellman v) (x, z) := by + set b := P.policyOf v (x, z) with hb + have hbm : b ∈ P.toExtended.feasible (x, z) := P.policyOf_mem v (x, z) + have hbm' : b ∈ P.toExtended.feasible (y, z) := P.feasible_mono hxy hbm + have hres : P.resources (y, z) = P.resources (x, z) + (1 + P.interest) * (y - x) := by + simp only [resources, max_eq_right hx.1, max_eq_right hy.1]; ring + have hch : P.consumption (y, z) b + = P.consumptionFnOf v z x + (1 + P.interest) * (1 * (y - x)) := by + simp only [consumptionFnOf, consumption, hres, ← hb]; ring + have hchpos : 0 < P.consumption (y, z) b := by + rw [hch] + have : (0 : ℝ) ≤ (1 + P.interest) * (1 * (y - x)) := + mul_nonneg P.interest_gt_neg_one.le (by linarith) + linarith + have hle := P.lazyValue_le v z hy hbm' hchpos + have heq := P.lazyValue_eq v z hx hbm hc (P.policyOf_optimal v (x, z)) + have hba : ∀ t : ℝ, P.toExtended.bellmanFn v (t, z) = (P.toExtended.bellman v) (t, z) := + fun t => by rw [← P.toExtended.bellman_apply] + rw [hba y] at hle + rw [hba x] at heq + simp only [lazyValue] at hle heq + have hcx : P.resources (x, z) - b = P.consumptionFnOf v z x := rfl + have hcy : P.resources (y, z) - b + = P.consumptionFnOf v z x + (1 + P.interest) * (y - x) := by + simp only [← hcx, hres]; ring + rw [hcy] at hle + rw [hcx] at heq + linarith + +/-- **The richer household's plan, carried back.** Feasible only if the richer plan is still +affordable at the poorer state, which is what `hfe` says. -/ +theorem bellman_sub_le {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hfe : P.policyOf v (y, z) ≤ P.maxSaving (x, z)) + (hcx : 0 < P.consumptionFnOf v z y - (1 + P.interest) * (y - x)) + (hc : 0 < P.consumptionFnOf v z y) : + (P.toExtended.bellman v) (y, z) - (P.toExtended.bellman v) (x, z) + ≤ P.u (P.consumptionFnOf v z y) + - P.u (P.consumptionFnOf v z y - (1 + P.interest) * (y - x)) := by + set b := P.policyOf v (y, z) with hb + have hbm : b ∈ P.toExtended.feasible (y, z) := P.policyOf_mem v (y, z) + have hbm' : b ∈ P.toExtended.feasible (x, z) := ⟨hbm.1, hfe⟩ + have hres : P.resources (y, z) = P.resources (x, z) + (1 + P.interest) * (y - x) := by + simp only [resources, max_eq_right hx.1, max_eq_right hy.1]; ring + have hcy : P.resources (y, z) - b = P.consumptionFnOf v z y := rfl + have hcxv : P.resources (x, z) - b + = P.consumptionFnOf v z y - (1 + P.interest) * (y - x) := by + simp only [← hcy, hres]; ring + have hcpos : 0 < P.consumption (x, z) b := by + simp only [consumption, hcxv]; exact hcx + have hle := P.lazyValue_le v z hx hbm' hcpos + have heq := P.lazyValue_eq v z hy hbm hc (P.policyOf_optimal v (y, z)) + have hba : ∀ t : ℝ, P.toExtended.bellmanFn v (t, z) = (P.toExtended.bellman v) (t, z) := + fun t => by rw [← P.toExtended.bellman_apply] + rw [hba x] at hle + rw [hba y] at heq + simp only [lazyValue] at hle heq + rw [hcxv] at hle + rw [hcy] at heq + linarith + +/-! ### The Euler inequality, which survives the borrowing limit + +Saving MORE is feasible whenever the household is not already saving the maximum, and that has +nothing to do with the borrowing limit. So this half of the first-order condition holds at the +corner too, and it is the half the concavity argument needs there. -/ + +/-- **The Euler inequality.** Marginal utility today is at least the discounted, interest- +augmented expected marginal utility tomorrow. The only interiority is `hAmax`: the household +could save a little more. -/ +theorem euler_le {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A du : ℝ} {du' : Z → ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hu : HasDerivAt P.u du (P.consumptionFnOf (P.toExtended.bellman v) z a)) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) + (hu' : ∀ z' : Z, HasDerivAt P.u (du' z') (P.consumptionFnOf v z' A)) : + (P.discount : ℝ) * ((1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z') + ≤ du := by + subst hA + set W := P.toExtended.bellman v with hWdef + set A := P.policyOf W (a, z) with hAdef + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hAmem : A ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem W (a, z)) + set c := P.consumptionFnOf W z a with hcdef + have hcA : c = P.resources (a, z) - A := rfl + set D : ℝ := du - (P.discount : ℝ) + * ((1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z') with hDdef + set F : ℝ → ℝ := fun ε => (P.u c - P.u (c - ε)) + - (P.discount : ℝ) * ∑ z' : Z, P.transitionMatrix z z' + * (P.u (P.consumptionFnOf v z' A + (1 + P.interest) * ε) + - P.u (P.consumptionFnOf v z' A)) with hFdef + have hF0 : F 0 = 0 := by simp [hFdef] + -- `F` is differentiable at zero, with derivative the Euler gap + have hd1 : HasDerivAt (fun ε : ℝ => P.u c - P.u (c - ε)) du 0 := by + have h1 : HasDerivAt (fun ε : ℝ => c - ε) (-1) 0 := by + simpa using (hasDerivAt_id (0 : ℝ)).const_sub c + have h2 : HasDerivAt (fun ε : ℝ => P.u (c - ε)) (du * (-1)) 0 := + HasDerivAt.comp (0 : ℝ) (by simpa using hu) h1 + simpa using h2.const_sub (P.u c) + have hd2 : ∀ z' : Z, HasDerivAt + (fun ε : ℝ => P.u (P.consumptionFnOf v z' A + (1 + P.interest) * ε) + - P.u (P.consumptionFnOf v z' A)) (du' z' * (1 + P.interest)) 0 := by + intro z' + have h1 : HasDerivAt (fun ε : ℝ => P.consumptionFnOf v z' A + (1 + P.interest) * ε) + (1 + P.interest) 0 := by + simpa using ((hasDerivAt_id (0 : ℝ)).const_mul (1 + P.interest)).const_add + (P.consumptionFnOf v z' A) + have h2 := HasDerivAt.comp (0 : ℝ) (by simpa using hu' z') h1 + simpa using h2.sub_const (P.u (P.consumptionFnOf v z' A)) + have hFd : HasDerivAt F D 0 := by + have hsum : HasDerivAt (fun ε : ℝ => ∑ z' : Z, P.transitionMatrix z z' + * (P.u (P.consumptionFnOf v z' A + (1 + P.interest) * ε) + - P.u (P.consumptionFnOf v z' A))) + (∑ z' : Z, P.transitionMatrix z z' * (du' z' * (1 + P.interest))) 0 := + HasDerivAt.fun_sum fun z' _ => (hd2 z').const_mul _ + have hcomm : (1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z' + = ∑ z' : Z, P.transitionMatrix z z' * (du' z' * (1 + P.interest)) := by + rw [Finset.mul_sum]; exact Finset.sum_congr rfl fun z' _ => by ring + rw [hDdef, hcomm] + exact hd1.sub (hsum.const_mul (P.discount : ℝ)) + -- and it is non-negative to the right of zero, because saving more is not profitable + have hFnn : ∀ᶠ ε in 𝓝[>] (0 : ℝ), 0 ≤ F ε := by + have hδ : 0 < min (P.maxSaving (a, z) - A) c := lt_min (by linarith) hc + filter_upwards [Ioo_mem_nhdsGT hδ] with ε hε + have hε0 : 0 < ε := hε.1 + have hε1 : ε < P.maxSaving (a, z) - A := lt_of_lt_of_le hε.2 (min_le_left _ _) + have hε2 : ε < c := lt_of_lt_of_le hε.2 (min_le_right _ _) + have hAe : A + ε ∈ Icc assetFloor assetCap := + ⟨by linarith [hAmem.1], by linarith [P.maxSaving_le_assetCap (a, z)]⟩ + have hfeas : A + ε ∈ P.toExtended.feasible (a, z) := + ⟨by linarith [hAmem.1], by linarith⟩ + have hcpos : 0 < P.consumption (a, z) (A + ε) := by + simp only [consumption]; rw [hcA] at hε2; linarith + have hle := P.lazyValue_le W z ha hfeas hcpos + have heq := P.lazyValue_eq W z ha (P.policyOf_mem W (a, z)) hc + (P.policyOf_optimal W (a, z)) + have hstep : P.lazyValue W z (A + ε) a ≤ P.lazyValue W z A a := by rw [heq]; exact hle + simp only [lazyValue] at hstep + rw [show P.resources (a, z) - (A + ε) = c - ε from by rw [hcA]; ring, + show P.resources (a, z) - A = c from hcA.symm] at hstep + have hgain : ∀ z' : Z, + P.u (P.consumptionFnOf v z' A + (1 + P.interest) * ε) + - P.u (P.consumptionFnOf v z' A) ≤ W (A + ε, z') - W (A, z') := by + intro z' + have hg := P.bellman_sub_ge (z := z') hAmem hAe (by linarith) (hc' z') + rwa [show A + ε - A = ε from by ring] at hg + have hsum := Finset.sum_le_sum fun z' (_ : z' ∈ Finset.univ) => + mul_le_mul_of_nonneg_left (hgain z') (P.transitionMatrix_nonneg z z') + have hsplit : ∑ z' : Z, P.transitionMatrix z z' * (W (A + ε, z') - W (A, z')) + = (∑ z' : Z, P.transitionMatrix z z' * W (A + ε, z')) + - ∑ z' : Z, P.transitionMatrix z z' * W (A, z') := by + rw [← Finset.sum_sub_distrib]; exact Finset.sum_congr rfl fun z' _ => by ring + have hb1 := mul_le_mul_of_nonneg_left hsum hβ + rw [hsplit] at hb1 + simp only [hFdef] + linarith + -- a differentiable function that is zero at zero and non-negative to the right of it has a + -- non-negative derivative + have hmono : Tendsto (slope F 0) (𝓝[>] (0 : ℝ)) (𝓝 D) := + (hasDerivAt_iff_tendsto_slope.mp hFd).mono_left + (nhdsWithin_mono _ fun x hx => ne_of_gt hx) + have hnn : ∀ᶠ ε in 𝓝[>] (0 : ℝ), 0 ≤ slope F 0 ε := by + filter_upwards [hFnn, self_mem_nhdsWithin] with ε hFe hε0 + rw [slope_def_field, hF0, sub_zero, sub_zero] + exact div_nonneg hFe (le_of_lt hε0) + have hD : 0 ≤ D := ge_of_tendsto hmono hnn + rw [hDdef] at hD + linarith + +/-! ### The first-order condition + +With the envelope available at the saving actually chosen, the objective is differentiable in +the saving, the choice is a local maximum, and the derivative vanishes. That is the Euler +equation: the marginal utility of consuming now equals the discounted, interest-augmented +expected marginal utility of consuming next period. -/ + +/-- **The Euler equation along the iteration.** Every hypothesis is an interiority or +differentiability condition at the single state `(a, z)` and at the saving it chooses; nothing +is assumed globally. -/ +theorem euler_of_interior {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A du : ℝ} {du' : Z → ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hA0 : assetFloor < A) (hAcap : A < assetCap) (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hu : HasDerivAt P.u du (P.consumptionFnOf (P.toExtended.bellman v) z a)) + (hconc : ∀ z' : Z, ConcaveOn ℝ (Icc assetFloor assetCap) + fun x => (P.toExtended.bellman v) (x, z')) + (hint' : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z')) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) + (hu' : ∀ z' : Z, HasDerivAt P.u (du' z') (P.consumptionFnOf v z' A)) : + du = (P.discount : ℝ) * ((1 + P.interest) * ∑ z', P.transitionMatrix z z' * du' z') := by + subst hA + set W := P.toExtended.bellman v with hWdef + set A := P.policyOf W (a, z) with hAdef + have hcA : P.consumptionFnOf W z a = P.resources (a, z) - A := rfl + -- the continuation is differentiable in the saving, by the envelope one step on + have hWderiv : ∀ z' : Z, HasDerivAt (fun x => W (x, z')) ((1 + P.interest) * du' z') A := + fun z' => P.hasDerivAt_bellman hA0 hAcap (hconc z') (hint' z') (hc' z') (hu' z') + have hsum : HasDerivAt (fun x => ∑ z' : Z, P.transitionMatrix z z' * W (x, z')) + (∑ z' : Z, P.transitionMatrix z z' * ((1 + P.interest) * du' z')) A := + HasDerivAt.fun_sum fun z' _ => (hWderiv z').const_mul _ + -- and so is the reward + have hres : HasDerivAt (fun x : ℝ => P.resources (a, z) - x) (-1) A := by + simpa using (hasDerivAt_id A).const_sub (P.resources (a, z)) + have hrew : HasDerivAt (fun x => P.u (P.resources (a, z) - x)) (du * (-1)) A := + HasDerivAt.comp A (hcA ▸ hu) hres + have hΦ : HasDerivAt (fun x => P.lazyValue W z x a) + (du * (-1) + (P.discount : ℝ) + * ∑ z' : Z, P.transitionMatrix z z' * ((1 + P.interest) * du' z')) A := + hrew.add (hsum.const_mul (P.discount : ℝ)) + -- the chosen saving is a local maximum of the objective + have hlm : IsLocalMax (fun x => P.lazyValue W z x a) A := by + have hcont : ContinuousAt (fun x : ℝ => P.resources (a, z) - x) A := + (continuous_const.sub continuous_id).continuousAt + have h1 : ∀ᶠ x in 𝓝 A, assetFloor < x := lt_mem_nhds hA0 + have h2 : ∀ᶠ x in 𝓝 A, x < P.maxSaving (a, z) := gt_mem_nhds hAmax + have hcpos : (0 : ℝ) < P.resources (a, z) - A := by rw [← hcA]; exact hc + have h3 : ∀ᶠ x in 𝓝 A, 0 < P.resources (a, z) - x := hcont.eventually_const_lt hcpos + have hAopt : P.lazyValue W z A a = P.toExtended.bellmanFn W (a, z) := + P.lazyValue_eq W z ha (P.policyOf_mem W (a, z)) hc (P.policyOf_optimal W (a, z)) + filter_upwards [h1, h2, h3] with x hx1 hx2 hx3 + rw [hAopt] + exact P.lazyValue_le W z ha ⟨hx1.le, hx2.le⟩ hx3 + -- so the derivative vanishes, which is the Euler equation + have hzero := hlm.hasDerivAt_eq_zero hΦ + have hfac : ∑ z' : Z, P.transitionMatrix z z' * ((1 + P.interest) * du' z') + = (1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z' := by + rw [Finset.mul_sum] + exact Finset.sum_congr rfl fun z' _ => by ring + rw [hfac] at hzero + linarith + +/-- **The reverse Euler inequality.** Saving LESS is feasible only away from the borrowing +limit, which is why this half fails at the corner. Like `euler_le` it uses only the two lazy +bounds: the envelope theorem is not needed for the Euler equation. -/ +theorem euler_ge {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A du : ℝ} {du' : Z → ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hA0 : assetFloor < A) + (hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z')) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hu : HasDerivAt P.u du (P.consumptionFnOf (P.toExtended.bellman v) z a)) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) + (hu' : ∀ z' : Z, HasDerivAt P.u (du' z') (P.consumptionFnOf v z' A)) : + du ≤ (P.discount : ℝ) + * ((1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z') := by + subst hA + set W := P.toExtended.bellman v with hWdef + set A := P.policyOf W (a, z) with hAdef + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hAmem : A ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem W (a, z)) + set c := P.consumptionFnOf W z a with hcdef + have hcA : c = P.resources (a, z) - A := rfl + set D : ℝ := (P.discount : ℝ) + * ((1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z') - du with hDdef + set G : ℝ → ℝ := fun ε => (P.discount : ℝ) * ∑ z' : Z, P.transitionMatrix z z' + * (P.u (P.consumptionFnOf v z' A) + - P.u (P.consumptionFnOf v z' A - (1 + P.interest) * ε)) + - (P.u (c + ε) - P.u c) with hGdef + have hG0 : G 0 = 0 := by simp [hGdef] + -- the derivative at zero is the Euler gap, with the sign reversed + have hd1 : HasDerivAt (fun ε : ℝ => P.u (c + ε) - P.u c) du 0 := by + have h1 : HasDerivAt (fun ε : ℝ => c + ε) 1 0 := by + simpa using (hasDerivAt_id (0 : ℝ)).const_add c + have h2 : HasDerivAt (fun ε : ℝ => P.u (c + ε)) (du * 1) 0 := + HasDerivAt.comp (0 : ℝ) (by simpa using hu) h1 + simpa using h2.sub_const (P.u c) + have hd2 : ∀ z' : Z, HasDerivAt + (fun ε : ℝ => P.u (P.consumptionFnOf v z' A) + - P.u (P.consumptionFnOf v z' A - (1 + P.interest) * ε)) + (du' z' * (1 + P.interest)) 0 := by + intro z' + have h1 : HasDerivAt (fun ε : ℝ => P.consumptionFnOf v z' A - (1 + P.interest) * ε) + (-(1 + P.interest)) 0 := by + simpa using ((hasDerivAt_id (0 : ℝ)).const_mul (1 + P.interest)).const_sub + (P.consumptionFnOf v z' A) + have h2 := HasDerivAt.comp (0 : ℝ) (by simpa using hu' z') h1 + have h3 := h2.const_sub (P.u (P.consumptionFnOf v z' A)) + have hrw : -(du' z' * -(1 + P.interest)) = du' z' * (1 + P.interest) := by ring + rwa [hrw] at h3 + have hGd : HasDerivAt G D 0 := by + have hsum : HasDerivAt (fun ε : ℝ => ∑ z' : Z, P.transitionMatrix z z' + * (P.u (P.consumptionFnOf v z' A) + - P.u (P.consumptionFnOf v z' A - (1 + P.interest) * ε))) + (∑ z' : Z, P.transitionMatrix z z' * (du' z' * (1 + P.interest))) 0 := + HasDerivAt.fun_sum fun z' _ => (hd2 z').const_mul _ + have hcomm : (1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z' + = ∑ z' : Z, P.transitionMatrix z z' * (du' z' * (1 + P.interest)) := by + rw [Finset.mul_sum]; exact Finset.sum_congr rfl fun z' _ => by ring + rw [hDdef, hcomm] + exact (hsum.const_mul (P.discount : ℝ)).sub hd1 + -- and it is non-negative to the right of zero, because saving less is not profitable + have hGnn : ∀ᶠ ε in 𝓝[>] (0 : ℝ), 0 ≤ G ε := by + have hloc : ∀ z' : Z, ∀ᶠ ε in 𝓝[>] (0 : ℝ), + P.policyOf v (A, z') ≤ P.maxSaving (A - ε, z') + ∧ 0 < P.consumptionFnOf v z' A - (1 + P.interest) * ε := by + intro z' + have hcont : ContinuousAt (fun ε : ℝ => P.maxSaving (A - ε, z')) 0 := + (P.continuous_maxSaving.comp + ((continuous_const.sub continuous_id).prodMk continuous_const)).continuousAt + have h1 : ∀ᶠ ε in 𝓝 (0 : ℝ), P.policyOf v (A, z') < P.maxSaving (A - ε, z') := + hcont.eventually_const_lt (by simpa using hslack z') + have hcont2 : ContinuousAt + (fun ε : ℝ => P.consumptionFnOf v z' A - (1 + P.interest) * ε) 0 := + (continuous_const.sub (continuous_const.mul continuous_id)).continuousAt + have h2 : ∀ᶠ ε in 𝓝 (0 : ℝ), + 0 < P.consumptionFnOf v z' A - (1 + P.interest) * ε := + hcont2.eventually_const_lt (by simpa using hc' z') + filter_upwards [(h1.filter_mono nhdsWithin_le_nhds), + (h2.filter_mono nhdsWithin_le_nhds)] with ε he1 he2 using ⟨he1.le, he2⟩ + have hall : ∀ᶠ ε in 𝓝[>] (0 : ℝ), ∀ z' : Z, + P.policyOf v (A, z') ≤ P.maxSaving (A - ε, z') + ∧ 0 < P.consumptionFnOf v z' A - (1 + P.interest) * ε := + Filter.eventually_all.mpr hloc + filter_upwards [Ioo_mem_nhdsGT (show (0 : ℝ) < A - assetFloor by linarith), hall] + with ε hε hz' + have hε0 : 0 < ε := hε.1 + have hε1 : ε < A - assetFloor := hε.2 + have hAe : A - ε ∈ Icc assetFloor assetCap := + ⟨by linarith, by linarith [hAmem.2]⟩ + have hfeas : A - ε ∈ P.toExtended.feasible (a, z) := + ⟨by linarith, by linarith [(P.policyOf_mem W (a, z)).2]⟩ + have hcpos : 0 < P.consumption (a, z) (A - ε) := by + simp only [consumption]; rw [hcA] at hc; linarith + have hle := P.lazyValue_le W z ha hfeas hcpos + have heq := P.lazyValue_eq W z ha (P.policyOf_mem W (a, z)) hc + (P.policyOf_optimal W (a, z)) + have hstep : P.lazyValue W z (A - ε) a ≤ P.lazyValue W z A a := by rw [heq]; exact hle + simp only [lazyValue] at hstep + rw [show P.resources (a, z) - (A - ε) = c + ε from by rw [hcA]; ring, + show P.resources (a, z) - A = c from hcA.symm] at hstep + have hloss : ∀ z' : Z, W (A, z') - W (A - ε, z') + ≤ P.u (P.consumptionFnOf v z' A) + - P.u (P.consumptionFnOf v z' A - (1 + P.interest) * ε) := by + intro z' + have hg := P.bellman_sub_le (z := z') hAe hAmem (hz' z').1 + (by have := (hz' z').2; rw [show A - (A - ε) = ε from by ring]; linarith) + (hc' z') + rwa [show A - (A - ε) = ε from by ring] at hg + have hsum := Finset.sum_le_sum fun z' (_ : z' ∈ Finset.univ) => + mul_le_mul_of_nonneg_left (hloss z') (P.transitionMatrix_nonneg z z') + have hsplit : ∑ z' : Z, P.transitionMatrix z z' * (W (A, z') - W (A - ε, z')) + = (∑ z' : Z, P.transitionMatrix z z' * W (A, z')) + - ∑ z' : Z, P.transitionMatrix z z' * W (A - ε, z') := by + rw [← Finset.sum_sub_distrib]; exact Finset.sum_congr rfl fun z' _ => by ring + have hb1 := mul_le_mul_of_nonneg_left hsum hβ + rw [hsplit] at hb1 + simp only [hGdef] + linarith + have hmono : Tendsto (slope G 0) (𝓝[>] (0 : ℝ)) (𝓝 D) := + (hasDerivAt_iff_tendsto_slope.mp hGd).mono_left + (nhdsWithin_mono _ fun x hx => ne_of_gt hx) + have hnn : ∀ᶠ ε in 𝓝[>] (0 : ℝ), 0 ≤ slope G 0 ε := by + filter_upwards [hGnn, self_mem_nhdsWithin] with ε hGe hε0 + rw [slope_def_field, hG0, sub_zero, sub_zero] + exact div_nonneg hGe (le_of_lt hε0) + have hD : 0 ≤ D := ge_of_tendsto hmono hnn + rw [hDdef] at hD + linarith + +/-- **The Euler equation**, from the two deviations. No envelope theorem, no second +derivatives: only that `u` is differentiable at the consumptions concerned. -/ +theorem euler_eq {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A du : ℝ} {du' : Z → ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hA0 : assetFloor < A) (hAmax : A < P.maxSaving (a, z)) + (hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z')) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hu : HasDerivAt P.u du (P.consumptionFnOf (P.toExtended.bellman v) z a)) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) + (hu' : ∀ z' : Z, HasDerivAt P.u (du' z') (P.consumptionFnOf v z' A)) : + du = (P.discount : ℝ) + * ((1 + P.interest) * ∑ z' : Z, P.transitionMatrix z z' * du' z') := + le_antisymm (P.euler_ge ha hA hA0 hslack hc hu hc' hu') + (P.euler_le ha hA hAmax hc hu hc' hu') + +/-! ### CRRA: the Euler equation in endogenous-gridpoint form + +With `u' c = c ^ (-γ)` the Euler equation says that consumption is a positive multiple of the +weighted power mean, exponent `-γ`, of next period's consumptions — which is exactly `egmMap`, +and exactly what `concaveOn_consumptionFnOf_bellman_of_euler` asks for. -/ + +theorem egm_of_euler {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) + (heuler : P.consumptionFnOf (P.toExtended.bellman v) z a ^ (-γ) + = (P.discount : ℝ) * ((1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ))) : + P.consumptionFnOf (P.toExtended.bellman v) z a = P.egmMap v γ z A := by + have hγn : γ ≠ 0 := ne_of_gt hγ0 + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hS : (0 : ℝ) < ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hc' + have hprod : P.consumptionFnOf (P.toExtended.bellman v) z a ^ (-γ) + = ((P.discount : ℝ) * (1 + P.interest)) + * ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ) := by + rw [heuler]; ring + have hexp : (-γ) * (-(1 / γ)) = 1 := by field_simp + have hinv : (P.consumptionFnOf (P.toExtended.bellman v) z a ^ (-γ)) ^ (-(1 / γ)) + = P.consumptionFnOf (P.toExtended.bellman v) z a := by + rw [← Real.rpow_mul hc.le, hexp, Real.rpow_one] + rw [← hinv, hprod, Real.mul_rpow hK.le hS.le] + simp only [egmMap, powerMean] + congr 1 + rw [show -(1 / γ) = 1 / (-γ) by field_simp] + +/-- **The hypothesis `hEuler` of `concaveOn_consumptionFnOf_bellman_of_euler`, discharged.** +Everything that remains is interiority: the saving chosen must be strictly inside the asset +region and strictly below the feasible maximum, this period and next. -/ +theorem egmMap_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + (hu : P.u = crraUtility γ) {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hA0 : assetFloor < A) (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hint' : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z')) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.consumptionFnOf (P.toExtended.bellman v) z a = P.egmMap v γ z A := by + have hd : HasDerivAt P.u (P.consumptionFnOf (P.toExtended.bellman v) z a ^ (-γ)) + (P.consumptionFnOf (P.toExtended.bellman v) z a) := by + rw [hu]; exact hasDerivAt_crraUtility γ hc + have hd' : ∀ z' : Z, HasDerivAt P.u (P.consumptionFnOf v z' A ^ (-γ)) + (P.consumptionFnOf v z' A) := fun z' => by + rw [hu]; exact hasDerivAt_crraUtility γ (hc' z') + exact P.egm_of_euler hγ0 hβ hc hc' (P.euler_eq ha hA hA0 hAmax hint' hc hd hc' hd') + +/-- The endogenous-gridpoint map, written as a single power of the Euler right-hand side. -/ +theorem egmMap_eq_rpow {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {A : ℝ} (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.egmMap v γ z A = ((P.discount : ℝ) * (1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ)) ^ (-(1 / γ)) := by + have hγn : γ ≠ 0 := ne_of_gt hγ0 + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hS : (0 : ℝ) < ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hc' + rw [Real.mul_rpow hK.le hS.le] + simp only [egmMap, powerMean] + congr 1 + rw [show -(1 / γ) = 1 / (-γ) by field_simp] + +/-- **The Euler inequality in endogenous-gridpoint form.** Consumption never exceeds the value +the endogenous grid assigns to the saving actually chosen — and this holds AT the borrowing +limit, where the equality fails. -/ +theorem egmMap_ge {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + (hu : P.u = crraUtility γ) {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.consumptionFnOf (P.toExtended.bellman v) z a ≤ P.egmMap v γ z A := by + have hγn : γ ≠ 0 := ne_of_gt hγ0 + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hS : (0 : ℝ) < ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hc' + have hd : HasDerivAt P.u (P.consumptionFnOf (P.toExtended.bellman v) z a ^ (-γ)) + (P.consumptionFnOf (P.toExtended.bellman v) z a) := by + rw [hu]; exact hasDerivAt_crraUtility γ hc + have hd' : ∀ z' : Z, HasDerivAt P.u (P.consumptionFnOf v z' A ^ (-γ)) + (P.consumptionFnOf v z' A) := fun z' => by + rw [hu]; exact hasDerivAt_crraUtility γ (hc' z') + have heuler := P.euler_le ha hA hAmax hc hd hc' hd' + rw [P.egmMap_eq_rpow hγ0 hβ hc'] + have hKS : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' * P.consumptionFnOf v z' A ^ (-γ) := mul_pos hK hS + have hstep := Real.rpow_le_rpow_of_nonpos hKS (by linarith [heuler]) + (show -(1 / γ) ≤ 0 by rw [neg_nonpos]; positivity) + have hexp : (-γ) * (-(1 / γ)) = 1 := by field_simp + rwa [← Real.rpow_mul hc.le, hexp, Real.rpow_one] at hstep + +/-! ### The borrowing limit, and the kink it makes + +Where the constraint binds the Euler equation is only an inequality, and the endogenous-gridpoint +transfer has to absorb that. It does, and the reason is short: at a constrained state the saving +is `assetFloor`, which is below ANY average of savings, so the step the transfer needs is free +there. -/ + +/-- **The endogenous-gridpoint transfer, with the corner.** Concavity of the consumption +function needs the Euler equation only where the borrowing limit is slack; at the limit the +inequality `hle` suffices, because the saving is then at its minimum. -/ +theorem concaveOn_of_egm_corner {z : Z} {g C h : ℝ → ℝ} + (hC : ConcaveOn ℝ (Icc assetFloor assetCap) C) + (hM : StrictMonoOn (fun A => A + C A) (Icc assetFloor assetCap)) + (hg : ∀ a ∈ Icc assetFloor assetCap, g a ∈ Icc assetFloor assetCap) + (hbud : ∀ a ∈ Icc assetFloor assetCap, + P.income z + (1 + P.interest) * a = g a + h a) + (hle : ∀ a ∈ Icc assetFloor assetCap, h a ≤ C (g a)) + (heq : ∀ a ∈ Icc assetFloor assetCap, assetFloor < g a → h a = C (g a)) : + ConcaveOn ℝ (Icc assetFloor assetCap) h := by + refine ⟨convex_Icc _ _, fun a ha b hb θ φ hθ hφ hθφ => ?_⟩ + simp only [smul_eq_mul] + have hm : θ * a + φ * b ∈ Icc assetFloor assetCap := by + simpa only [smul_eq_mul] using convex_Icc assetFloor assetCap ha hb hθ hφ hθφ + have hA₁ := hg a ha + have hA₂ := hg b hb + have hAθ := hg _ hm + have hAm : θ * g a + φ * g b ∈ Icc assetFloor assetCap := by + simpa only [smul_eq_mul] using convex_Icc assetFloor assetCap hA₁ hA₂ hθ hφ hθφ + have h1 := hbud a ha + have h2 := hbud b hb + have h3 := hbud _ hm + have hcomb : P.income z + (1 + P.interest) * (θ * a + φ * b) + = (θ * g a + φ * g b) + (θ * h a + φ * h b) := by + linear_combination θ * h1 + φ * h2 - P.income z * hθφ + have hAle : g (θ * a + φ * b) ≤ θ * g a + φ * g b := by + rcases eq_or_lt_of_le hAθ.1 with hcorner | hinterior + · rw [← hcorner] + have hsum : θ * assetFloor + φ * assetFloor = assetFloor := by + rw [← add_mul, hθφ, one_mul] + linarith [mul_nonneg hθ (sub_nonneg.mpr hA₁.1), mul_nonneg hφ (sub_nonneg.mpr hA₂.1)] + · have hCc : θ * h a + φ * h b ≤ C (θ * g a + φ * g b) := by + have hca := hle a ha + have hcb := hle b hb + have hmid := hC.2 hA₁ hA₂ hθ hφ hθφ + simp only [smul_eq_mul] at hmid + nlinarith [mul_le_mul_of_nonneg_left hca hθ, mul_le_mul_of_nonneg_left hcb hφ] + have hstep : g (θ * a + φ * b) + C (g (θ * a + φ * b)) + ≤ (θ * g a + φ * g b) + C (θ * g a + φ * g b) := by + rw [← heq _ hm hinterior, ← h3, hcomb]; linarith + by_contra hcon + exact absurd (hM hAm hAθ (not_le.mp hcon)) (not_lt.mpr hstep) + have hval : h (θ * a + φ * b) + = (θ * g a + φ * g b) + (θ * h a + φ * h b) - g (θ * a + φ * b) := by + rw [← hcomb, h3]; ring + rw [hval] + linarith + +/-! ### Carroll and Kimball's induction step, with the Euler equation discharged -/ + +/-- **The interiority the first-order condition needs.** At `(a, z)` the household saves +strictly inside the asset region and strictly below its feasible maximum, and does so again next +period in every income state. This is exactly what fails at the borrowing limit, where +`policy_eq_zero_of_corner_at` shows the constraint binds. -/ +def EulerInterior (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a : ℝ) : Prop := + assetFloor < P.policyOf (P.toExtended.bellman v) (a, z) + ∧ P.policyOf (P.toExtended.bellman v) (a, z) < P.maxSaving (a, z) + ∧ ∀ z' : Z, P.policyOf v (P.policyOf (P.toExtended.bellman v) (a, z), z') + < P.maxSaving (P.policyOf (P.toExtended.bellman v) (a, z), z') + +/-- **Carroll and Kimball (1996), the induction step for CRRA, with no Euler hypothesis.** + +`concaveOn_consumptionFnOf_bellman_of_euler` assumed the first-order condition; here it is +proved, from the Clausen–Strub envelope run along the iteration. What is assumed instead is +interiority, which is a property of the state rather than of the model, and the reason the +statement is not yet unconditional. -/ +theorem concaveOn_consumptionFnOf_bellman_of_interior {γ : ℝ} (hγ0 : 0 < γ) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z' : Z, ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconcv : ∀ z' : Z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmonov : ∀ z' : Z, MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) + (hcW : ∀ a ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hin : ∀ a ∈ Icc assetFloor assetCap, P.EulerInterior v z a) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_consumptionFnOf_bellman_of_euler hγ0 hpos hconcv hmonov z fun a ha => ?_ + obtain ⟨h1, h2, h3⟩ := hin a ha + exact P.egmMap_consumptionFnOf hγ0 hβ hu ha rfl h1 h2 (hcW a ha) h3 + fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z))) + +/-- **Carroll and Kimball (1996), the induction step for CRRA, with the borrowing limit allowed +to BIND.** No interiority at the limit is assumed; the only slackness required is of the asset +CAP, which `crra_policy_lt_assetCap` discharges by calibration and which is a different +condition entirely. + +Where the limit binds the Euler equation degenerates to an inequality, and `concaveOn_of_egm_corner` +absorbs that: the saving is then at its minimum, so it sits below any average of savings, which +is the only thing the transfer needs. -/ +theorem concaveOn_consumptionFnOf_bellman {γ : ℝ} (hγ0 : 0 < γ) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z' : Z, ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconcv : ∀ z' : Z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmonov : ∀ z' : Z, MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) + (hcW : ∀ a ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hslackW : ∀ a ∈ Icc assetFloor assetCap, + P.policyOf (P.toExtended.bellman v) (a, z) < P.maxSaving (a, z)) + (hslackv : ∀ A ∈ Icc assetFloor assetCap, ∀ z' : Z, + P.policyOf v (A, z') < P.maxSaving (A, z')) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_of_egm_corner (z := z) + (g := fun a => P.policyOf (P.toExtended.bellman v) (a, z)) (C := P.egmMap v γ z) + (P.concaveOn_egmMap hγ0 z hpos hconcv) + (strictMonoOn_add_egm (P.monotoneOn_egmMap hγ0 z hpos hmonov)) + (fun a _ => P.feasible_subset_region (P.policyOf_mem _ (a, z))) (fun a ha => ?_) + (fun a ha => ?_) fun a ha hfloor => ?_ + · have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + simp only [consumptionFnOf, consumption, hres]; ring + · exact P.egmMap_ge hγ0 hβ hu ha rfl (hslackW a ha) (hcW a ha) + fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z))) + · exact P.egmMap_consumptionFnOf hγ0 hβ hu ha rfl hfloor (hslackW a ha) (hcW a ha) + (hslackv _ (P.feasible_subset_region (P.policyOf_mem _ (a, z)))) + fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z))) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean new file mode 100644 index 000000000..cf7c8d524 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -0,0 +1,872 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateStep +public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline +public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange +public import LeanPool.AiyagariEquilibrium.Analysis.HARA + + +/-! +# Carroll and Kimball beyond CRRA: the HARA induction step + +`IncomeFluctuationCarrollKimball` proves the induction step for `u = crraUtility γ`, where the +Euler equation makes consumption a multiple of the power mean of next period's consumptions and +`powerMean_concave` — Kimball's risk-tolerance aggregation — does the rest. Carroll and Kimball's +theorem is not about CRRA but about HARA, and this file covers the `b ≠ 0` branch, +`u c = crraUtility γ (η + c)`: CRRA with a subsistence level, which includes CRRA itself at +`η = 0`. + +## Nothing new is needed + +Marginal utility is `(η + c) ^ (-γ)`, so the Euler equation reads + + `(η + C(A)) ^ (-γ) = β R · Σ_{z'} π_{z z'} (η + c_{z'}(A)) ^ (-γ)`, + +which is the CRRA equation in the variable `η + c`. So the endogenous-gridpoint map is the CRRA +one applied to SHIFTED consumptions, with the shift removed at the end (`haraEgmMap`), and it is +concave for exactly the same reason: `η + c_{z'}` is concave when `c_{z'}` is, the power mean of +concave positive functions is concave, and a constant shift changes nothing. + +That the shift is harmless is the content of "linear risk tolerance". Risk tolerance +`-u' / u''` is `(η + c) / γ`, affine in consumption, and Kimball's aggregation theorem needs +precisely affineness — the level `η` rides through untouched. + +## What is and is not covered + +The induction STEP is here, for the whole `b ≠ 0` HARA branch. What is not is the assembled +statement: the iteration also needs positive consumption and the asset cap slack. Those are no +longer CRRA-specific — `consumptionFnOf_pos_of_marginal` gets positivity from a FINITE marginal +bound, which is what a shifted CRRA has at zero (`(η + c) ^ (-γ)` at `c = 0` is `η ^ (-γ)`), and +`policyOf_le_of_marginal_bound` was always general. Both branches now carry the assembly +through to a witness: `IncomeFluctuationCARA.caraCal` for CARA, and `StoneGearyWitness.stoneGeary` +for this one. The CARA branch `b = 0`, where marginal utility is `exp (-α c)`, needs a +different aggregator — the soft minimum `-(1/α) log Σ π exp (-α c)` — and is in +`IncomeFluctuationCARA`, on top of `Analysis.SoftMin`. +-/ + +@[expose] public section + +open scoped NNReal +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **The endogenous-gridpoint map for shifted CRRA.** The CRRA map in the variable `η + c`. -/ +noncomputable def haraEgmMap (v : (ℝ × Z) →ᵇ ℝ) (γ η : ℝ) (z : Z) (A : ℝ) : ℝ := + ((P.discount : ℝ) * (1 + P.interest)) ^ (-(1 / γ)) + * powerMean (P.transitionMatrix z) (-γ) (fun z' => η + P.consumptionFnOf v z' A) - η + +@[simp] theorem haraEgmMap_zero_shift (v : (ℝ × Z) →ᵇ ℝ) (γ : ℝ) (z : Z) (A : ℝ) : + P.haraEgmMap v γ 0 z A = P.egmMap v γ z A := by + simp only [haraEgmMap, egmMap, zero_add, sub_zero] + +theorem concaveOn_haraEgmMap {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.haraEgmMap v γ η z) := by + have hmean := concaveOn_powerMean_comp (convex_Icc assetFloor assetCap) + (neg_neg_of_pos hγ) (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) + (f := fun z' a => η + P.consumptionFnOf v z' a) + (fun z' A hA => by linarith [hpos z' A hA]) + (fun z' => ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => by + have h := (hconc z').2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at h ⊢ + nlinarith [h, hθφ]⟩) + have hk : (0 : ℝ) ≤ ((P.discount : ℝ) * (1 + P.interest)) ^ (-(1 / γ)) := + Real.rpow_nonneg (mul_nonneg P.discount.coe_nonneg P.interest_gt_neg_one.le) _ + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hstep := hmean.2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hstep ⊢ + simp only [haraEgmMap] + nlinarith [mul_le_mul_of_nonneg_left hstep hk, hθφ] + +theorem monotoneOn_haraEgmMap {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) : + MonotoneOn (P.haraEgmMap v γ η z) (Icc assetFloor assetCap) := by + have hmean := monotoneOn_powerMean_comp (D := Icc assetFloor assetCap) + (neg_neg_of_pos hγ) (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) + (f := fun z' a => η + P.consumptionFnOf v z' a) + (fun z' A hA => by linarith [hpos z' A hA]) + (fun z' x hx y hy hxy => by linarith [hmono z' hx hy hxy]) + intro x hx y hy hxy + simp only [haraEgmMap] + have := mul_le_mul_of_nonneg_left (hmean hx hy hxy) + (Real.rpow_nonneg (mul_nonneg P.discount.coe_nonneg P.interest_gt_neg_one.le) + (-(1 / γ))) + linarith + +/-- **The Euler equation puts consumption on the endogenous grid**, for shifted CRRA. -/ +theorem hara_egm_of_euler {γ η : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (hc : 0 < η + P.consumptionFnOf (P.toExtended.bellman v) z a) + (hc' : ∀ z' : Z, 0 < η + P.consumptionFnOf v z' A) + (heuler : (η + P.consumptionFnOf (P.toExtended.bellman v) z a) ^ (-γ) + = (P.discount : ℝ) * ((1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ))) : + P.consumptionFnOf (P.toExtended.bellman v) z a = P.haraEgmMap v γ η z A := by + have hγn : γ ≠ 0 := ne_of_gt hγ0 + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hS : (0 : ℝ) + < ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hc' + have hprod : (η + P.consumptionFnOf (P.toExtended.bellman v) z a) ^ (-γ) + = ((P.discount : ℝ) * (1 + P.interest)) + * ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) := by + rw [heuler]; ring + have hexp : (-γ) * (-(1 / γ)) = 1 := by field_simp + have hinv : ((η + P.consumptionFnOf (P.toExtended.bellman v) z a) ^ (-γ)) ^ (-(1 / γ)) + = η + P.consumptionFnOf (P.toExtended.bellman v) z a := by + rw [← Real.rpow_mul hc.le, hexp, Real.rpow_one] + have hkey : η + P.consumptionFnOf (P.toExtended.bellman v) z a + = ((P.discount : ℝ) * (1 + P.interest)) ^ (-(1 / γ)) + * powerMean (P.transitionMatrix z) (-γ) + (fun z' => η + P.consumptionFnOf v z' A) := by + rw [← hinv, hprod, Real.mul_rpow hK.le hS.le] + simp only [powerMean] + congr 1 + rw [show -(1 / γ) = 1 / (-γ) by field_simp] + simp only [haraEgmMap] + linarith + +/-- **The Euler hypothesis discharged**, for shifted CRRA: interiority is all that is left. -/ +theorem hara_egmMap_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + (hu : P.u = haraUtility γ η) {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (hη : 0 ≤ η) (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hA0 : assetFloor < A) (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hint' : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z')) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.consumptionFnOf (P.toExtended.bellman v) z a = P.haraEgmMap v γ η z A := by + have hcη : 0 < η + P.consumptionFnOf (P.toExtended.bellman v) z a := by linarith + have hcη' : ∀ z' : Z, 0 < η + P.consumptionFnOf v z' A := fun z' => by linarith [hc' z'] + have hd : HasDerivAt P.u ((η + P.consumptionFnOf (P.toExtended.bellman v) z a) ^ (-γ)) + (P.consumptionFnOf (P.toExtended.bellman v) z a) := by + rw [hu]; exact hasDerivAt_haraUtility γ hcη + have hd' : ∀ z' : Z, HasDerivAt P.u ((η + P.consumptionFnOf v z' A) ^ (-γ)) + (P.consumptionFnOf v z' A) := fun z' => by + rw [hu]; exact hasDerivAt_haraUtility γ (hcη' z') + exact P.hara_egm_of_euler hγ0 hβ hcη hcη' + (P.euler_eq ha hA hA0 hAmax hint' hc hd hc' hd') + +/-- **Carroll and Kimball (1996), the induction step for shifted CRRA.** Given the Euler +equation, the Bellman operator carries a concave consumption function to a concave one. -/ +theorem concaveOn_consumptionFnOf_bellman_of_hara_euler {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) + {v : (ℝ × Z) →ᵇ ℝ} + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) (z : Z) + (hEuler : ∀ a ∈ Icc assetFloor assetCap, + P.consumptionFnOf (P.toExtended.bellman v) z a + = P.haraEgmMap v γ η z (P.policyOf (P.toExtended.bellman v) (a, z))) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_of_egm (z := z) (g := fun a => P.policyOf (P.toExtended.bellman v) (a, z)) + (C := P.haraEgmMap v γ η z) (P.concaveOn_haraEgmMap hγ hη z hpos hconc) + (strictMonoOn_add_egm (P.monotoneOn_haraEgmMap hγ hη z hpos hmono)) + (fun a _ => P.feasible_subset_region (P.policyOf_mem _ (a, z))) (fun a ha => ?_) + (fun a ha => hEuler a ha) + rw [← hEuler a ha] + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + simp only [consumptionFnOf, consumption, hres] + ring + +/-- **The induction step for shifted CRRA, with the Euler equation proved**, exactly as +`concaveOn_consumptionFnOf_bellman_of_interior` does it for CRRA: what is assumed is interiority, +a property of the state, not of the model. -/ +theorem concaveOn_consumptionFnOf_bellman_of_hara {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = haraUtility γ η) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) + (hcpos : ∀ a ∈ Icc assetFloor assetCap, + 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hint : ∀ a ∈ Icc assetFloor assetCap, P.EulerInterior v z a) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_consumptionFnOf_bellman_of_hara_euler hγ hη hpos hconc hmono z + fun a ha => ?_ + obtain ⟨hA0, hAmax, hint'⟩ := hint a ha + exact P.hara_egmMap_consumptionFnOf hγ hβ hu hη ha rfl hA0 hAmax (hcpos a ha) hint' + (fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z)))) + + +/-- **CRRA is the `η = 0` member**, so the HARA step really does generalise the CRRA one rather +than sitting beside it. -/ +theorem concaveOn_consumptionFnOf_bellman_of_crra_via_hara {γ : ℝ} (hγ : 0 < γ) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z', ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z', ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmono : ∀ z', MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) + (hcpos : ∀ a ∈ Icc assetFloor assetCap, + 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hint : ∀ a ∈ Icc assetFloor assetCap, P.EulerInterior v z a) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := + P.concaveOn_consumptionFnOf_bellman_of_hara hγ le_rfl hβ + (by rw [hu, haraUtility_zero_shift]) z hpos hconc hmono hcpos hint + + +/-! ### Marginal bounds + +A shifted CRRA has FINITE marginal utility at zero, `η ^ (-γ)`, so like CARA it satisfies no +Inada condition and its consumption floor is an inequality rather than a limit. Both bounds are +the CRRA ones read at `η + c`. -/ + +theorem haraUtility_marginal_bound {γ η R : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hη : 0 ≤ η) + {c d : ℝ} (hd : 0 ≤ d) (hdc : d ≤ c) (hcR : c ≤ R) : + (η + R) ^ (-γ) * (c - d) ≤ haraUtility γ η c - haraUtility γ η d := by + have hstep := crra_marginal_bound_Ici (γ := γ) (R := η + R) hγ0 hγ1 + (d := η + d) (c := η + c) (mem_Ici.mpr (by linarith)) (by linarith) (by linarith) + simp only [haraUtility] + have he : η + c - (η + d) = c - d := by ring + rwa [he] at hstep + +/-- The same with the restriction moved from `γ` to the ARGUMENT: a strictly positive +subsistence level keeps `η + c` away from zero, so `γ ≥ 1` — log Stone–Geary included — is +admissible. -/ +theorem haraUtility_marginal_bound_pos {γ η R : ℝ} (hγ0 : 0 < γ) {c d : ℝ} (hd : 0 < η + d) + (hdc : d ≤ c) (hcR : c ≤ R) : + (η + R) ^ (-γ) * (c - d) ≤ haraUtility γ η c - haraUtility γ η d := by + have hstep := crra_marginal_bound (γ := γ) (R := η + R) (c := η + c) (d := η + d) hγ0 hd + (by linarith) (by linarith) + simp only [haraUtility] + have he : η + c - (η + d) = c - d := by ring + rwa [he] at hstep + +/-- **A marginal bound near zero for a strictly positive subsistence level.** -/ +theorem marginalBoundOn_haraUtility_pos {γ η δ : ℝ} (hγ0 : 0 < γ) (hη : 0 < η) (hδ : 0 < δ) : + MarginalBoundOn (Ici (0 : ℝ)) (haraUtility γ η) ((η + δ) ^ (-γ)) := + ⟨δ, hδ, fun _c _c' hc hcc' hc'δ => + haraUtility_marginal_bound_pos hγ0 (by linarith [mem_Ici.mp hc]) hcc'.le hc'δ⟩ + +theorem marginalBoundOn_haraUtility {γ η δ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hη : 0 ≤ η) + (hδ : 0 < δ) : MarginalBoundOn (Ici (0 : ℝ)) (haraUtility γ η) ((η + δ) ^ (-γ)) := + ⟨δ, hδ, fun _c _c' hc hcc' hc'δ => + haraUtility_marginal_bound hγ0 hγ1 hη (mem_Ici.mp hc) hcc'.le hc'δ⟩ + +/-! ### The Euler inequality, and the corner -/ + +theorem haraEgmMap_eq_rpow {γ η : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) (hη : 0 ≤ η) + {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {A : ℝ} (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.haraEgmMap v γ η z A = ((P.discount : ℝ) * (1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' + * (η + P.consumptionFnOf v z' A) ^ (-γ)) ^ (-(1 / γ)) - η := by + have hγn : γ ≠ 0 := ne_of_gt hγ0 + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hpos : ∀ z' : Z, 0 < η + P.consumptionFnOf v z' A := fun z' => by linarith [hc' z'] + have hS : (0 : ℝ) < ∑ z' : Z, P.transitionMatrix z z' + * (η + P.consumptionFnOf v z' A) ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hpos + rw [Real.mul_rpow hK.le hS.le] + simp only [haraEgmMap, powerMean] + congr 2 + rw [show -(1 / γ) = 1 / (-γ) by field_simp] + +/-- **The Euler INEQUALITY in endogenous-gridpoint form**, for shifted CRRA: it holds AT the +borrowing limit, where the equality fails. -/ +theorem hara_egmMap_ge {γ η : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a A : ℝ} + (ha : a ∈ Icc assetFloor assetCap) + (hA : P.policyOf (P.toExtended.bellman v) (a, z) = A) + (hAmax : A < P.maxSaving (a, z)) + (hc : 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A) : + P.consumptionFnOf (P.toExtended.bellman v) z a ≤ P.haraEgmMap v γ η z A := by + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hK : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + have hcη : 0 < η + P.consumptionFnOf (P.toExtended.bellman v) z a := by linarith + have hcη' : ∀ z' : Z, 0 < η + P.consumptionFnOf v z' A := fun z' => by linarith [hc' z'] + have hS : (0 : ℝ) < ∑ z' : Z, P.transitionMatrix z z' + * (η + P.consumptionFnOf v z' A) ^ (-γ) := + sum_rpow_pos (P.transitionMatrix_nonneg z) (P.transitionMatrix_sum z) hcη' + have hd : HasDerivAt P.u ((η + P.consumptionFnOf (P.toExtended.bellman v) z a) ^ (-γ)) + (P.consumptionFnOf (P.toExtended.bellman v) z a) := by + rw [hu]; exact hasDerivAt_haraUtility γ hcη + have hd' : ∀ z' : Z, HasDerivAt P.u ((η + P.consumptionFnOf v z' A) ^ (-γ)) + (P.consumptionFnOf v z' A) := fun z' => by + rw [hu]; exact hasDerivAt_haraUtility γ (hcη' z') + have heuler := P.euler_le ha hA hAmax hc hd hc' hd' + rw [P.haraEgmMap_eq_rpow hγ0 hβ hη hc'] + have hKS : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) + * ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) := + mul_pos hK hS + have hstep := Real.rpow_le_rpow_of_nonpos hKS (by linarith [heuler]) + (show -(1 / γ) ≤ 0 by rw [neg_nonpos]; positivity) + have hexp : (-γ) * (-(1 / γ)) = 1 := by field_simp + rw [← Real.rpow_mul hcη.le, hexp, Real.rpow_one] at hstep + linarith + +/-- **Carroll and Kimball for shifted CRRA, across the borrowing-limit kink.** -/ +theorem concaveOn_consumptionFnOf_bellman_of_hara_corner {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = haraUtility γ η) {v : (ℝ × Z) →ᵇ ℝ} (z : Z) + (hpos : ∀ z' : Z, ∀ A ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z' A) + (hconc : ∀ z' : Z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf v z')) + (hmono : ∀ z' : Z, MonotoneOn (P.consumptionFnOf v z') (Icc assetFloor assetCap)) + (hcW : ∀ a ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (hslackW : ∀ a ∈ Icc assetFloor assetCap, + P.policyOf (P.toExtended.bellman v) (a, z) < P.maxSaving (a, z)) + (hslackv : ∀ A ∈ Icc assetFloor assetCap, ∀ z' : Z, + P.policyOf v (A, z') < P.maxSaving (A, z')) : + ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf (P.toExtended.bellman v) z) := by + refine P.concaveOn_of_egm_corner (z := z) + (g := fun a => P.policyOf (P.toExtended.bellman v) (a, z)) (C := P.haraEgmMap v γ η z) + (P.concaveOn_haraEgmMap hγ hη z hpos hconc) + (strictMonoOn_add_egm (P.monotoneOn_haraEgmMap hγ hη z hpos hmono)) + (fun a _ => P.feasible_subset_region (P.policyOf_mem _ (a, z))) (fun a ha => ?_) + (fun a ha => ?_) fun a ha hfloor => ?_ + · have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + simp only [consumptionFnOf, consumption, hres]; ring + · exact P.hara_egmMap_ge hγ hβ hη hu ha rfl (hslackW a ha) (hcW a ha) + fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z))) + · exact P.hara_egmMap_consumptionFnOf hγ hβ hu hη ha rfl hfloor (hslackW a ha) (hcW a ha) + (hslackv _ (P.feasible_subset_region (P.policyOf_mem _ (a, z)))) + fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z))) + +/-! ### The proportional bound on saving + +`policyOf_le_of_marginal_bound` prices a deviation against the LEVEL of the continuation, which +for a bounded utility is far too crude to keep the cap slack at any interesting discount factor. +The CRRA development fixes that with `crra_policyOf_le_mul`: deviate to a FRACTION `θ` of the +saving rather than a fixed amount, and price the extra consumption at the margin where it lands. +The bound then involves the continuation's OSCILLATION and is proportional to consumption. + +The shifted-CRRA version is that argument with the marginal step read at `η + c`. Everything +else — the deviation, the concavity of the continuation, the oscillation — knows nothing about +preferences. -/ + +theorem hara_policyOf_le_mul {γ η θ G : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = haraUtility γ η) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) + (hosc : P.OscOn v G) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) + (hcpos : 0 < P.consumptionFnOf v z a) : + P.policyOf v (a, z) - assetFloor + ≤ ((P.discount : ℝ) * G / θ) + * (η + P.consumptionFnOf v z a + + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hbase : (0 : ℝ) < η + c + (1 - θ) * (b - assetFloor) := by + have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) + (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) + linarith + have hrpow : (0 : ℝ) < (η + c + (1 - θ) * (b - assetFloor)) ^ γ := Real.rpow_pos_of_pos hbase _ + rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos + · rw [← hzero, sub_self] + positivity + have hbf : (0 : ℝ) < b - assetFloor := by linarith + set w : ℝ := assetFloor + θ * (b - assetFloor) with hwdef + have hw0 : assetFloor < w := by rw [hwdef]; nlinarith + have hwb : w < b := by rw [hwdef]; nlinarith + have hwmem : w ∈ Icc assetFloor assetCap := ⟨hw0.le, by linarith [hbreg.2]⟩ + have hfeas : w ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcb : c = P.resources (a, z) - b := rfl + have hcons : P.consumption (a, z) w = c + (1 - θ) * (b - assetFloor) := by + simp only [consumption, hwdef]; rw [hcb]; ring + have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) + simp only [objROf, hcons] at hopt + rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, + show P.policyOf v (a, z) = b from rfl] at hopt + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hbreg hw0 hwb + have hgap := P.contOf_sub_le_osc hosc hwmem + (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z + have hbw : b - w = (1 - θ) * (b - assetFloor) := by rw [hwdef]; ring + have hwf : w - assetFloor = θ * (b - assetFloor) := by rw [hwdef]; ring + have hloss : P.contOf v z b - P.contOf v z w ≤ (1 - θ) / θ * G := by + rw [div_le_div_iff₀ (show (0 : ℝ) < b - w by linarith) + (show (0 : ℝ) < w - assetFloor by linarith), hbw, hwf] at hslope + have h2 : (P.contOf v z b - P.contOf v z w) * (θ * (b - assetFloor)) + ≤ G * ((1 - θ) * (b - assetFloor)) := by + nlinarith [hslope, hgap, mul_pos (show (0:ℝ) < 1 - θ by linarith) hbf] + rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] + nlinarith [h2, hbf] + -- the marginal gain, priced where it lands: the CRRA step read at `η + c` + have hmg : (η + c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) + ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by + rw [hu] + have hbound := haraUtility_marginal_bound_pos (γ := γ) (η := η) + (R := c + (1 - θ) * (b - assetFloor)) hγ0 (d := c) + (c := c + (1 - θ) * (b - assetFloor)) (by linarith) + (le_add_of_nonneg_right (mul_nonneg (by linarith) hbf.le)) le_rfl + have he : η + (c + (1 - θ) * (b - assetFloor)) = η + c + (1 - θ) * (b - assetFloor) := by ring + rw [he] at hbound + have he2 : c + (1 - θ) * (b - assetFloor) - c = (1 - θ) * (b - assetFloor) := by ring + rw [he2] at hbound + exact hbound + have hmid : P.u (c + (1 - θ) * (b - assetFloor)) - P.u c + ≤ (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) := by + have hd : (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) + = (P.discount : ℝ) * P.contOf v z b - (P.discount : ℝ) * P.contOf v z w := by ring + linarith [hopt, hd.le, hd.ge] + have hkey := le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) + rw [Real.rpow_neg hbase.le γ] at hkey + have h1θ : (0 : ℝ) < 1 - θ := by linarith + have hexp : ((η + c + (1 - θ) * (b - assetFloor)) ^ γ)⁻¹ * ((1 - θ) * (b - assetFloor)) + = (1 - θ) * ((b - assetFloor) / (η + c + (1 - θ) * (b - assetFloor)) ^ γ) := by field_simp + rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * G) + = (1 - θ) * ((P.discount : ℝ) * G / θ) from by ring] at hkey + have hdiv := le_of_mul_le_mul_left hkey h1θ + rw [div_le_iff₀ hrpow] at hdiv + linarith + +/-- **Saving never reaches the asset cap**, for shifted CRRA. -/ +theorem hara_policyOf_lt_assetCap {γ η θ G : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = haraUtility γ η) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) + (hosc : P.OscOn v G) (hcpos : ∀ z : Z, ∀ a ∈ Icc assetFloor assetCap, + 0 < P.consumptionFnOf v z a) + (hlt : ((P.discount : ℝ) * G / θ) + * (η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ + < assetCap - assetFloor) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.policyOf v (a, z) < assetCap := by + by_contra hcon + rw [not_lt] at hcon + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hkey := P.hara_policyOf_le_mul hγ0 hη hθ0 hθ1 hu hv hG hosc ha z (hcpos z a ha) + rw [← hbdef, ← hcdef] at hkey + have hcpos' : 0 < c := hcpos z a ha + have hres : P.resources (a, z) - assetFloor ≤ P.maxConsumption := + P.consumption_le_maxConsumption (s := (a, z)) ha le_rfl + have hsum : c + (1 - θ) * (b - assetFloor) + = (P.resources (a, z) - assetFloor) - θ * (b - assetFloor) := by + simp only [hcdef, consumptionFnOf, consumption, ← hbdef]; ring + have hle : η + c + (1 - θ) * (b - assetFloor) + ≤ η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor := by + have hθb : θ * (assetCap - assetFloor) ≤ θ * (b - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) hθ0.le + simp only [maxConsumption] at hres + nlinarith [hres, hθb, hsum] + have hnn : (0 : ℝ) ≤ η + c + (1 - θ) * (b - assetFloor) := by + have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) + (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) + linarith [hcpos'] + have hmono : (η + c + (1 - θ) * (b - assetFloor)) ^ γ + ≤ (η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ := + Real.rpow_le_rpow hnn hle hγ0.le + have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * G / θ := by positivity + nlinarith [hkey, mul_le_mul_of_nonneg_left hmono hcoef, hlt] + +/-! ### The assembly -/ + +theorem concaveOn_consumptionFnOf_iterates_of_hara {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = haraUtility γ η) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc assetFloor assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hslack : ∀ n : ℕ, ∀ a ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < P.maxSaving (a, z)) : + ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z) := by + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact P.concaveSlices_bellman ih + intro n + induction n with + | zero => exact P.concaveOn_consumptionFnOf_zero + | succ k ih => + intro z + have hnext : ∀ a ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf + (P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ))) z a := by + intro a ha + have := hpos (k + 1) z a ha + rwa [Function.iterate_succ_apply'] at this + have hnextslack : ∀ a ∈ Icc assetFloor assetCap, P.policyOf + (P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ))) (a, z) + < P.maxSaving (a, z) := by + intro a ha + have := hslack (k + 1) a ha z + rwa [Function.iterate_succ_apply'] at this + rw [Function.iterate_succ_apply'] + exact P.concaveOn_consumptionFnOf_bellman_of_hara_corner hγ hη hβ hu z + (fun z' A hA => hpos k z' A hA) ih + (fun z' x hx y hy hxy => P.consumptionFnOf_mono (hslices k) hx hy hxy) + hnext hnextslack (fun A hA z' => hslack k A hA z') + +/-- **Carroll and Kimball for shifted CRRA, from two inequalities.** The same two the CARA +assembly needs, and for the same reasons: marginal utility at zero is finite, so the consumption +floor has to be earned, and the cap has to sit above the saving the marginal bound allows. -/ +theorem concaveOn_consumptionFn_of_hara {γ η δ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hη : 0 ≤ η) + (hδ : 0 < δ) (hβ : 0 < (P.discount : ℝ)) (hb : P.Bounded) (hu : P.u = haraUtility γ η) + (hfloor : (P.discount : ℝ) * P.oscSlopeConst P.oscSpread < (η + δ) ^ (-γ)) + (hcap : assetFloor + 4 * (P.discount : ℝ) + * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount)) + / (η + P.maxConsumption) ^ (-γ) < assetCap) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact P.concaveSlices_bellman ih + have hM : MarginalBoundOn P.dom P.u ((η + δ) ^ (-γ)) := by + rw [hb, hu] + exact marginalBoundOn_haraUtility hγ0 hγ1 hη hδ + have hpos : ∀ n : ℕ, ∀ z' : Z, ∀ a ∈ Icc assetFloor assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z' a := + fun n z' a ha => P.consumptionFnOf_pos_of_marginal hM P.oscSpread_nonneg (hslices n) + (P.oscOn_iterate n) hfloor z' ha + have hmpos : (0 : ℝ) < (η + P.maxConsumption) ^ (-γ) := + Real.rpow_pos_of_pos (by linarith [P.maxConsumption_pos]) _ + have hslack : ∀ n : ℕ, ∀ a ∈ Icc assetFloor assetCap, ∀ z' : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z') < P.maxSaving (a, z') := by + intro n a ha z' + refine P.policyOf_lt_maxSaving_of_pos (hpos n z' a ha) ?_ + have hbound := P.policyOf_le_of_marginal_bound (hslices n) hmpos ?_ ha z' + · have hn := P.norm_iterate_le n + have h4 : (0 : ℝ) ≤ 4 * (P.discount : ℝ) := by positivity + have hstep := mul_le_mul_of_nonneg_left hn h4 + have hdiv : 4 * (P.discount : ℝ) * ‖(P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)‖ + / (η + P.maxConsumption) ^ (-γ) + ≤ 4 * (P.discount : ℝ) + * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount)) + / (η + P.maxConsumption) ^ (-γ) := by + rw [div_le_div_iff_of_pos_right hmpos] + linarith + linarith + · intro c d hd hdc hcmax + rw [hu] + exact haraUtility_marginal_bound hγ0 hγ1 hη (P.nonneg_of_mem_dom hd) hdc hcmax + exact P.concaveOn_consumptionFn_of_iterates + (P.concaveOn_consumptionFnOf_iterates_of_hara hγ0 hη hβ hu hpos hslack) z + + +/-! ### The two comparative-statics corollaries, for shifted CRRA + +Light's Theorem 1 and the impatience-driven decline both take the marginal-utility hypotheses +abstractly, so shifted CRRA discharges them the same way CRRA does — with `(η + c) ^ (-γ)` in +place of `c ^ (-γ)`. Relative risk aversion of the shifted family is `γc/(η + c) ≤ γ`, so a +NON-NEGATIVE subsistence level only helps Light's condition. -/ + +theorem hara_marginal_antitone {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) : + AntitoneOn (fun c => (η + c) ^ (-γ)) (Ioi (0 : ℝ)) := fun x hx y _ hxy => + Real.rpow_le_rpow_of_nonpos (by linarith [mem_Ioi.mp hx]) (by linarith) (by linarith) + +theorem hara_marginal_pos {γ η : ℝ} (hη : 0 ≤ η) {c : ℝ} (hc : 0 < c) : + 0 < (η + c) ^ (-γ) := Real.rpow_pos_of_pos (by linarith) _ + +/-- **Light (2018) Theorem 1 for shifted CRRA.** -/ +theorem policy_mono_withRate_hara {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + {γ η : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) + {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := + P.policy_mono_withRate' h₁ h₂ hr (du := fun c => (η + c) ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_haraUtility γ (by linarith)) + (hara_marginal_antitone hγ0 hη) + (by + have hrra := (relativeRiskAversionLeOne_haraUtility hγ0 hγ1 hη).monotoneOn_mul_deriv + (du := fun c => (η + c) ^ (-γ)) + (fun c hc => hasDerivAt_haraUtility γ (by have := mem_Ioi.mp hc; linarith)) + exact hrra) + (fun y hy => (hara_marginal_pos hη (mem_Ioi.mp hy)).le) + hposv hposw hslackv hslackw hcons ha z + +/-- **Açıkgöz Proposition 4 for shifted CRRA**: the decline condition is impatience. -/ +theorem hara_exists_exhaust_of_impatient {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + (hpos : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, 0 < P.consumptionFn z x) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a₀ : ℝ} (ha₀ : assetFloor < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc assetFloor a₀, P.policy (a, z₀) = assetFloor) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := + P.exists_exhaust_of_impatient hβR hiid (du := fun c => (η + c) ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_haraUtility γ (by linarith)) + (hara_marginal_antitone hγ0 hη) + (fun c hc => hara_marginal_pos hη hc) hpos hslack hz₀ ha₀ hle hzero + + +/-! ### The supply floor + +The household saves something, so aggregate capital is positive. The shape is the CRRA one: the +cost of saving a little is priced at the margin where consumption is lowest, the gain at the +margin in the bad successor state, and the comparison is between two marginal utilities. With a +subsistence level both are read at `η + c`. -/ + +theorem hara_cost_of_saving {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) {m h R b : ℝ} (hh0 : 0 < h) + (hhm : h < m) (hmR : m ≤ R) (hb0 : 0 ≤ b) (hbh : b ≤ h) : + haraUtility γ η (R - b) - haraUtility γ η (R - h) ≤ (η + m - h) ^ (-γ) * h := by + have hstep := crra_cost_of_saving (γ := γ) (m := η + m) (h := h) (R := η + R) (b := b) hγ hh0 + (by linarith) (by linarith) hb0 hbh + have e1 : η + R - b = η + (R - b) := by ring + have e2 : η + R - h = η + (R - h) := by ring + have e3 : η + m - h = η + m - h := rfl + rw [e1, e2] at hstep + simpa only [haraUtility] using hstep + +/-- **The continuation's gain, for shifted CRRA.** -/ +theorem hara_cont_sub_ge_gen {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + (z z₀ : Z) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (hxy : x ≤ y) : + P.transitionMatrix z z₀ * ((η + P.income z₀ + (1 + P.interest) * y) ^ (-γ) + * ((1 + P.interest) * (y - x))) + ≤ P.cont z y - P.cont z x := by + subst hfl + have hRh : 0 ≤ (1 + P.interest) * (y - x) := + mul_nonneg P.interest_gt_neg_one.le (by linarith) + have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + have hterm : P.transitionMatrix z z₀ * + (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) + ≤ P.cont z y - P.cont z x := by + rw [hsum] + refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) + (fun z' _ => ?_) (Finset.mem_univ z₀) + exact mul_nonneg (P.transitionMatrix_nonneg _ _) + (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) + have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx + have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by + simp only [resources, max_eq_right hx.1] + have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) + ≤ P.income z₀ + (1 + P.interest) * x := by + simp only [consumption, hresx] + linarith [(P.policy_mem_region (x, z₀)).1] + have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ + rw [hu] at hgain + have hbound : (η + P.income z₀ + (1 + P.interest) * y) ^ (-γ) * ((1 + P.interest) * (y - x)) + ≤ haraUtility γ η (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) + - haraUtility γ η (P.consumption (x, z₀) (P.policy (x, z₀))) := by + have hR : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) + ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith [hcle] + have hstep := haraUtility_marginal_bound_pos (γ := γ) (η := η) + (R := P.income z₀ + (1 + P.interest) * y) hγ0 (by linarith) + (le_add_of_nonneg_right hRh) hR + have he : η + (P.income z₀ + (1 + P.interest) * y) + = η + P.income z₀ + (1 + P.interest) * y := by ring + rw [he] at hstep + have he2 : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) + - P.consumption (x, z₀) (P.policy (x, z₀)) = (1 + P.interest) * (y - x) := by ring + rw [he2] at hstep + exact hstep + exact le_trans (mul_le_mul_of_nonneg_left (le_trans hbound hgain) + (P.transitionMatrix_nonneg z z₀)) hterm + +/-- **Saving is bounded below**, for shifted CRRA, when the gain beats the cost. -/ +theorem hara_policy_ge_of_gain {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) + (hpc : P.PositiveConsumption) (z₁ : Z) {h : ℝ} (hh0 : 0 < h) (hhcap : h ≤ assetCap) + (hhinc : h < P.income z₁) + (hgain : (η + P.income z₁ - h) ^ (-γ) * h + < P.discount * (P.cont z₁ h - P.cont z₁ (h / 2))) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + h / 2 ≤ P.policy (a, z₁) := by + refine P.policy_ge_of_cost hfl hpc z₁ hh0 hhcap hhinc (fun R b hm hb0 hbh => ?_) hgain ha + rw [hu] + exact hara_cost_of_saving hγ hη hh0 hhinc hm hb0 hbh + + +/-! ### The corner condition, in primitives + +The corner test of `policy_eq_zero_of_corner_at` is `β L < m`: the discounted slope of the value +function against the marginal value of consumption. Both sides have closed forms for shifted +CRRA, so the test becomes one inequality among the primitives. + +* The value function's slope is bounded by `slopeBoundU`, amplified by the return and the + discounting. Concavity prices `slopeBoundU` at the margin halfway down the minimum + consumption: `(η + minConsumption / 2) ^ (-γ)`. +* The marginal value at `(a, z₀)` for `a ≤ a₀` is at least `(η + income z₀ + R a₀) ^ (-γ)`. + +So the corner holds below `a₀` exactly when + + `β · (η + minConsumption/2) ^ (-γ) · R / (1 - β R) < (η + income z₀ + R a₀) ^ (-γ)`, + +which is checkable by `norm_num` at any calibration. -/ + +/-- The secant slope is at most the marginal utility at the left end. -/ +theorem haraUtility_marginal_bound_above {γ η : ℝ} (hγ0 : 0 < γ) {c d : ℝ} (hd : 0 < η + d) + (hdc : d ≤ c) : + haraUtility γ η c - haraUtility γ η d ≤ (η + d) ^ (-γ) * (c - d) := by + have hstep := crra_marginal_bound_above (γ := γ) (c := η + c) (d := η + d) hγ0 hd + (by linarith) + have he : η + c - (η + d) = c - d := by ring + rw [he] at hstep + simpa only [haraUtility] using hstep + +/-- **The slope constant in closed form.** -/ +theorem slopeBoundU_le_hara {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) : + P.slopeBoundU ≤ (η + P.minConsumption / 2) ^ (-γ) := by + have hmin := P.minConsumption_pos + simp only [IncomeFluctuation.slopeBoundU, slopeBound, hu] + rw [div_le_iff₀ (by linarith)] + have hstep := haraUtility_marginal_bound_above (γ := γ) (η := η) (c := P.minConsumption) + (d := P.minConsumption / 2) hγ0 (by linarith) (by linarith) + have he : P.minConsumption - P.minConsumption / 2 = P.minConsumption / 2 := by ring + rw [he] at hstep + exact hstep + +/-- The Lipschitz constant of the value function, in primitives. -/ +noncomputable def haraLipschitz (γ η : ℝ) : ℝ := + (η + P.minConsumption / 2) ^ (-γ) * (1 + P.interest) + / (1 - P.discount * (1 + P.interest)) + +theorem haraLipschitz_nonneg {γ η : ℝ} (hη : 0 ≤ η) + (hβR : P.discount * (1 + P.interest) < 1) : 0 ≤ P.haraLipschitz γ η := by + have hR := P.interest_gt_neg_one + have hmin := P.minConsumption_pos + exact div_nonneg (mul_nonneg (Real.rpow_nonneg (by linarith) _) hR.le) (by linarith) + +/-- **The value function is Lipschitz**, with the constant read off the primitives. -/ +theorem hara_valueFunction_lipschitz {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hβR : P.discount * (1 + P.interest) < 1) : + ∀ z : Z, ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| + ≤ P.haraLipschitz γ η * |x - y| := by + have hR := P.interest_gt_neg_one + have hne : (0 : ℝ) < 1 - P.discount * (1 + P.interest) := by linarith + refine P.valueFunction_lipschitz (P.haraLipschitz_nonneg hη hβR) ?_ + have hslope := P.slopeBoundU_le_hara hγ0 hη hu + have hkey : ((η + P.minConsumption / 2) ^ (-γ) + + P.discount * P.haraLipschitz γ η) * (1 + P.interest) = P.haraLipschitz γ η := by + simp only [haraLipschitz] + field_simp + ring + have hmul : (P.slopeBoundU + P.discount * P.haraLipschitz γ η) * (1 + P.interest) + ≤ ((η + P.minConsumption / 2) ^ (-γ) + P.discount * P.haraLipschitz γ η) + * (1 + P.interest) := + mul_le_mul_of_nonneg_right (by linarith) hR.le + rw [hkey] at hmul + exact hmul + +/-- **The corner condition from primitives.** One inequality, `norm_num`-checkable. -/ +theorem hara_policy_eq_zero_of_primitives {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 < η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) + (hβR : P.discount * (1 + P.interest) < 1) (z₀ : Z) {a₀ : ℝ} (_ha₀ : 0 ≤ a₀) + (ha₀cap : a₀ ≤ assetCap) + (hcond : P.discount * P.haraLipschitz γ η + < (η + P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) + {a : ℝ} (ha : a ∈ Icc assetFloor a₀) : P.policy (a, z₀) = assetFloor := by + subst hfl + have hR := P.interest_gt_neg_one + have hmem : a ∈ Icc (0 : ℝ) assetCap := ⟨ha.1, le_trans ha.2 ha₀cap⟩ + have hres : P.resources (a, z₀) - 0 ≤ P.income z₀ + (1 + P.interest) * a₀ := by + simp only [IncomeFluctuation.resources, max_eq_right ha.1, sub_zero] + have := mul_le_mul_of_nonneg_left ha.2 hR.le + linarith + have hres0 : (0 : ℝ) < P.resources (a, z₀) - 0 := by + have := P.assetFloor_lt_resources (a, z₀) + linarith + refine P.policy_eq_zero_of_corner_at (L := P.haraLipschitz γ η) + (m := (η + P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) + (P.hara_valueFunction_lipschitz hγ0 hη.le hu hβR) hmem ?_ hcond + intro c d hd hdc hc + have hd0 : (0 : ℝ) ≤ d := P.nonneg_of_mem_dom hd + have hbound := haraUtility_marginal_bound_pos (γ := γ) (η := η) + (R := P.resources (a, z₀) - 0) hγ0 (show (0 : ℝ) < η + d by linarith) hdc hc + rw [hu] + refine le_trans (mul_le_mul_of_nonneg_right ?_ (by linarith)) hbound + exact rpow_neg_antitone hγ0 (by linarith) (by linarith) + +section Measure + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- **Positive aggregate capital for shifted CRRA, from primitives.** -/ +theorem hara_le_aggregateCapital_of_gain {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ h : ℝ} + (hh0 : 0 < h) (hhcap : h ≤ assetCap) (hhinc : h < P.income z₁) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + (hgain : (η + P.income z₁ - h) ^ (-γ) * h + < P.discount * (P.transitionMatrix z₁ z₀ + * ((η + P.income z₀ + (1 + P.interest) * h) ^ (-γ) * ((1 + P.interest) * (h / 2))))) : + h / 2 * p₀ ≤ P.aggregateCapital μ := by + subst hfl + refine P.le_aggregateCapital hμ (by linarith) hp fun a ha => ?_ + refine P.hara_policy_ge_of_gain hγ0 hη hu rfl hpc z₁ hh0 hhcap hhinc ?_ ha + refine lt_of_lt_of_le hgain (mul_le_mul_of_nonneg_left ?_ P.discount.coe_nonneg) + have hhalf : h / 2 ∈ Icc (0 : ℝ) assetCap := ⟨by linarith, by linarith⟩ + have hfull : h ∈ Icc (0 : ℝ) assetCap := ⟨hh0.le, hhcap⟩ + have hgen := P.hara_cont_sub_ge_gen hγ0 hη hu rfl hpc z₁ z₀ hhalf hfull (by linarith) + rw [show h - h / 2 = h / 2 from by ring] at hgen + exact hgen + + +/-- **The supply floor with the two powers bounded separately.** The gain condition compares +`(η + y₁ - t)^(-γ)`, the marginal cost of saving `t` in the high state, against +`(η + y₀ + Rt)^(-γ)`, the marginal value of having it in the low state. Bounding the first from +above and the second from below leaves ONE inequality among the bounds — and when a base sits on +the right side of one, its bound needs no root at all (`rpow_neg_le_one_of_one_le`, +`rpow_neg_le_inv_of_le_one`). + +This is what makes a supply floor a calibration check rather than a proof. -/ +theorem hara_le_aggregateCapital_of_bounds {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) + {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ t U L : ℝ} + (ht0 : 0 < t) (htcap : t ≤ assetCap) (htinc : t < P.income z₁) + (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) + (hU : (η + P.income z₁ - t) ^ (-γ) ≤ U) + (hL : L ≤ (η + P.income z₀ + (1 + P.interest) * t) ^ (-γ)) + (hcond : U * t + < (P.discount : ℝ) * (P.transitionMatrix z₁ z₀ * (L * ((1 + P.interest) * (t / 2))))) : + t / 2 * p₀ ≤ P.aggregateCapital μ := by + refine P.hara_le_aggregateCapital_of_gain hγ0 hη hu hfl hpc hμ (z₀ := z₀) ht0 htcap htinc hp ?_ + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hπ : (0 : ℝ) ≤ P.transitionMatrix z₁ z₀ := P.transitionMatrix_nonneg _ _ + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hcost : (η + P.income z₁ - t) ^ (-γ) * t ≤ U * t := + mul_le_mul_of_nonneg_right hU ht0.le + have hgainL : (P.discount : ℝ) * (P.transitionMatrix z₁ z₀ * (L * ((1 + P.interest) * (t / 2)))) + ≤ (P.discount : ℝ) * (P.transitionMatrix z₁ z₀ + * ((η + P.income z₀ + (1 + P.interest) * t) ^ (-γ) * ((1 + P.interest) * (t / 2)))) := + mul_le_mul_of_nonneg_left + (mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_right hL (by positivity)) hπ) hβ + linarith + +end Measure + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean new file mode 100644 index 000000000..8541ad789 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -0,0 +1,1083 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler +public import LeanPool.AiyagariEquilibrium.Models.CRRAConstants + + +/-! +# Positivity and monotonicity along the iteration + +Carroll and Kimball's induction step needs the CONTINUATION's consumption function to be +positive, concave and nondecreasing. Concavity is the induction hypothesis; the other two are +facts, but they are proved in this development only at the FIXED POINT +(`positiveConsumption_of_unbounded`, `consumptionFn_mono`). This file restates them for an +arbitrary continuation, which is what the induction sees. + +Both proofs are the fixed-point proofs with `objR` replaced by `objROf`: the objective read +against an arbitrary continuation. The one structural fact they use is that the continuation +value depends on the saving chosen and the income state, never on current assets, so shifting +both households' plans by their resource gap leaves consumption alone and moves only the +continuation. That is true of `objROf` for exactly the same reason it is true of `objR`. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### The objective against an arbitrary continuation -/ + +/-- The continuation value of saving `x`, read against `v`. -/ +noncomputable def contOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : ℝ := + ∑ z' : Z, P.transitionMatrix z z' * v (x, z') + +/-- The objective, as a real number, read against `v`. -/ +noncomputable def objROf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) (x : ℝ) : ℝ := + P.u (P.consumption s x) + P.discount * P.contOf v s.2 x + +theorem concaveOn_contOf {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.contOf v z) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + simp only [contOf, smul_eq_mul, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hz' := (hv z').2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hz' + nlinarith [mul_le_mul_of_nonneg_left hz' (P.transitionMatrix_nonneg z z')] + +/-! ### Positivity -/ + +/-- **Consumption never vanishes at the optimum, whatever the continuation.** With utility +unbounded below the reward at zero consumption is `⊥`, and the optimum is a real number. -/ +theorem consumptionFnOf_pos (hd : P.Unbounded) (v : (ℝ × Z) →ᵇ ℝ) {z : Z} {a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) : 0 < P.consumptionFnOf v z a := by + have hopt := P.policyOf_optimal v (a, z) + have hne : P.toExtended.reward ((a, z), P.policyOf v (a, z)) ≠ ⊥ := by + intro hbot + rw [ExtendedStochasticProgram.objectiveE, hbot, EReal.bot_add] at hopt + exact (EReal.bot_ne_coe _) hopt + have hmem := P.consumption_mem_dom_of_ne_bot (s := (a, z)) ha (P.policyOf_mem v (a, z)) hne + rw [hd] at hmem + exact hmem + +/-- **Consumption at the optimum lies in the utility domain**, whatever the continuation. This +needs no positivity: the optimum's objective is a real number, so the reward is not `⊥`. -/ +theorem consumption_policyOf_mem_dom (v : (ℝ × Z) →ᵇ ℝ) {z : Z} {a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) : P.consumptionFnOf v z a ∈ P.dom := + (P.bellmanFn_eq_of_optimal (s := (a, z)) ha (P.policyOf_mem v (a, z)) + (P.policyOf_optimal v (a, z))).1 + +/-- **Consumption is positive at the optimum against every continuation with concave slices.** +The induction sees the iterates, not the value function, so this is what it needs. With utility +unbounded below it is automatic; for the bounded CES family it is earned from a marginal Inada +condition, +exactly as `PositiveConsumption` is. -/ +def PositiveConsumptionAll : Prop := + ∀ (v : (ℝ × Z) →ᵇ ℝ), ConcaveSlices assetFloor assetCap v → + ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, 0 < P.consumptionFnOf v z a + +theorem positiveConsumptionAll_of_unbounded (hd : P.Unbounded) : P.PositiveConsumptionAll := + fun v _ _ _ ha => P.consumptionFnOf_pos hd v ha + +/-- **Positive consumption for free, when the cap is narrow relative to income.** If the whole +asset range is worth less than one period's minimum consumption, no feasible saving can exhaust +resources, so consumption is positive whatever the continuation. Utilities with FINITE marginal +value at zero — CARA, shifted CRRA — have no Inada condition to appeal to, and this is the +alternative: keep the household away from zero consumption by the budget rather than by the +preferences. -/ +theorem positiveConsumptionAll_of_rich (hrich : assetCap - assetFloor < P.minConsumption) : + P.PositiveConsumptionAll := by + intro v hv z a ha + have h1 : P.minConsumption ≤ P.resources (a, z) - assetFloor := + P.minConsumption_le_consumption_floor (a, z) + have h2 : P.policyOf v (a, z) ≤ assetCap := + (P.feasible_subset_region (P.policyOf_mem v (a, z))).2 + simp only [consumptionFnOf, consumption] + linarith + +/-- The objective in the reals agrees with the extended objective where consumption is +positive. -/ +theorem objectiveE_eq_coe_of (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) + (hcx : P.consumption (a, z) x ∈ P.dom) : + P.toExtended.objectiveE v (a, z) x = ((P.objROf v (a, z) x : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom (s := (a, z)) ha hx hcx, + ← EReal.coe_add] + rfl + +/-- **Any feasible action with positive consumption is worth at most the optimum**, whatever +the continuation. -/ +theorem objROf_le_of_mem (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) + (hcx : P.consumption (a, z) x ∈ P.dom) : + P.objROf v (a, z) x ≤ P.objROf v (a, z) (P.policyOf v (a, z)) := by + have hc : P.consumptionFnOf v z a ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have h1 := P.lazyValue_le_dom v z ha hx hcx + have h2 := P.lazyValue_eq_dom v z ha (P.policyOf_mem v (a, z)) hc + (P.policyOf_optimal v (a, z)) + simp only [lazyValue] at h1 h2 + simp only [objROf, contOf, consumption] + linarith + +/-! ### Monotonicity of the saving policy -/ + +/-- **The optimal policy is increasing in assets, whatever the continuation.** The exchange +argument of `policy_mono`, run against `v`. -/ +theorem policyOf_mono {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.policyOf v (a, z) ≤ P.policyOf v (a', z) := by + by_contra hcon + rw [not_le] at hcon + have hR : P.resources (a, z) ≤ P.resources (a', z) := P.resources_mono hle + have hys : P.policyOf v (a, z) ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v _ + have hys' : P.policyOf v (a, z) ∈ P.toExtended.feasible (a', z) := P.feasible_mono hle hys + have hy's' : P.policyOf v (a', z) ∈ P.toExtended.feasible (a', z) := P.policyOf_mem v _ + have hy's : P.policyOf v (a', z) ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] at hys ⊢ + rw [P.feasible_eq] at hy's' + exact ⟨hy's'.1, le_trans hcon.le hys.2⟩ + have hcsy : P.consumption (a, z) (P.policyOf v (a, z)) ∈ P.dom := + P.consumption_policyOf_mem_dom v ha + have hcs'y' : P.consumption (a', z) (P.policyOf v (a', z)) ∈ P.dom := + P.consumption_policyOf_mem_dom v ha' + have hcsy' : P.consumption (a, z) (P.policyOf v (a', z)) ∈ P.dom := by + refine P.dom_upward hcsy ?_ + simp only [consumption]; linarith + have hcs'y : P.consumption (a', z) (P.policyOf v (a, z)) ∈ P.dom := by + refine P.dom_upward hcsy ?_ + simp only [consumption]; linarith + -- increasing differences, from concavity of `u` alone + have hshift := P.strictConcaveOn_u_dom.concaveOn.sub_le_sub_of_shift + (c₁ := P.resources (a, z) - P.policyOf v (a, z)) + (c₂ := P.resources (a, z) - P.policyOf v (a', z)) + (Δ := P.resources (a', z) - P.resources (a, z)) + (by simpa only [consumption] using hcsy) + (by + have he : P.resources (a, z) - P.policyOf v (a', z) + + (P.resources (a', z) - P.resources (a, z)) + = P.consumption (a', z) (P.policyOf v (a', z)) := by simp only [consumption]; ring + rw [he]; exact hcs'y') + (by linarith) (by linarith) + have hopt_s := P.objROf_le_of_mem v ha hy's hcsy' + have hge : P.objROf v (a', z) (P.policyOf v (a', z)) + ≤ P.objROf v (a', z) (P.policyOf v (a, z)) := by + simp only [objROf, consumption] at hopt_s ⊢ + have e₁ : P.resources (a, z) - P.policyOf v (a', z) + + (P.resources (a', z) - P.resources (a, z)) + = P.resources (a', z) - P.policyOf v (a', z) := by ring + have e₂ : P.resources (a, z) - P.policyOf v (a, z) + + (P.resources (a', z) - P.resources (a, z)) + = P.resources (a', z) - P.policyOf v (a, z) := by ring + rw [e₁, e₂] at hshift + linarith + have hopt' : P.objROf v (a', z) (P.policyOf v (a, z)) + = P.objROf v (a', z) (P.policyOf v (a', z)) := + le_antisymm (P.objROf_le_of_mem v ha' hys' hcs'y) hge + have hbell : P.toExtended.objectiveE v (a', z) (P.policyOf v (a, z)) + = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of v ha' hys' hcs'y, hopt', + ← P.objectiveE_eq_coe_of v ha' hy's' hcs'y'] + exact P.policyOf_optimal v _ + exact absurd (P.optimal_action_unique_of_concaveSlices hv (s := (a', z)) ha' hys' hy's' + hbell (P.policyOf_optimal v _)) (by linarith) + +/-! ### Monotonicity of the consumption function -/ + +/-- **Consumption rises with assets, whatever the continuation.** The exchange argument of +`consumptionFn_mono`, run against `v`. -/ +theorem consumptionFnOf_mono {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.consumptionFnOf v z a ≤ P.consumptionFnOf v z a' := by + set Δ : ℝ := P.resources (a', z) - P.resources (a, z) with hΔdef + have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith [P.resources_mono (z := z) hle] + set b : ℝ := P.policyOf v (a, z) with hbdef + set b' : ℝ := P.policyOf v (a', z) with hb'def + by_contra hcon + rw [not_le] at hcon + have hgap : b + Δ < b' := by + simp only [consumptionFnOf, consumption] at hcon + rw [hΔdef]; linarith + have hbmem : b ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v _ + have hb'mem : b' ∈ P.toExtended.feasible (a', z) := P.policyOf_mem v _ + rw [P.feasible_eq] at hbmem hb'mem + have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + refine ⟨by linarith [hbmem.1], ?_⟩ + have hms := P.maxSaving_le_add_sub (z := z) hle + rw [← hΔdef] at hms + linarith [hb'mem.2] + have hc1 : P.consumption (a, z) b ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hc2 : P.consumption (a', z) b' ∈ P.dom := P.consumption_policyOf_mem_dom v ha' + have he1 : P.consumption (a', z) (b + Δ) = P.consumption (a, z) b := by + simp only [consumption, hΔdef]; ring + have he2 : P.consumption (a, z) (b' - Δ) = P.consumption (a', z) b' := by + simp only [consumption, hΔdef]; ring + have hI := P.objROf_le_of_mem v ha hshiftdown (by rw [he2]; exact hc2) + have hII := P.objROf_le_of_mem v ha' hshiftup (by rw [he1]; exact hc1) + have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + have hb'reg : b' ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + have hshift := (P.concaveOn_contOf hv z).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) + hbreg (by simpa using hb'reg) (by linarith) hΔ + rw [sub_add_cancel] at hshift + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hscaled := mul_le_mul_of_nonneg_left hshift hβ + simp only [objROf, he1, he2] at hI hII + have hIIeq : P.objROf v (a', z) (b + Δ) = P.objROf v (a', z) b' := by + simp only [objROf, he1] + nlinarith [hI, hII, hscaled] + have hbell : P.toExtended.objectiveE v (a', z) (b + Δ) + = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, + ← P.objectiveE_eq_coe_of v ha' (P.policyOf_mem v _) hc2] + exact P.policyOf_optimal v _ + exact absurd (P.optimal_action_unique_of_concaveSlices hv (s := (a', z)) ha' hshiftup + (P.policyOf_mem v _) hbell (P.policyOf_optimal v _)) (by rw [← hb'def]; linarith) + +/-! ### Saving stays strictly inside the feasible set + +Positive consumption already keeps the saving below resources; what is left is the asset CAP, +and that is the single quantitative input the induction still needs. -/ + +theorem policyOf_lt_maxSaving (hpc : P.PositiveConsumptionAll) {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) {a : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) (hcap : P.policyOf v (a, z) < assetCap) : + P.policyOf v (a, z) < P.maxSaving (a, z) := by + rw [P.maxSaving_eq] + refine lt_min hcap ?_ + have hc := hpc v hv z a ha + simp only [consumptionFnOf, consumption] at hc + linarith + +/-- The same, with positivity supplied only at the continuation in hand. Utilities with finite +marginal value at zero have no `PositiveConsumptionAll`, but they do have positivity against the +continuations the iteration produces. -/ +theorem policyOf_lt_maxSaving_of_pos {v : (ℝ × Z) →ᵇ ℝ} {a : ℝ} {z : Z} + (hc : 0 < P.consumptionFnOf v z a) (hcap : P.policyOf v (a, z) < assetCap) : + P.policyOf v (a, z) < P.maxSaving (a, z) := by + rw [P.maxSaving_eq] + refine lt_min hcap ?_ + simp only [consumptionFnOf, consumption] at hc + linarith + +/-! ### The oscillation of a continuation + +`2 ‖v‖` is the wrong bound on the continuation's spread: it is not translation-invariant, and +for CES — where utility is bounded and the level of `v` is large compared with its variation — +it is far too crude. `oscGap` is the right one, and what it bounds is the OSCILLATION. This +section carries that along the iteration. -/ + +/-- `v` varies by at most `G` over the asset region. -/ +def OscOn (_P : IncomeFluctuation Z assetFloor assetCap) (v : (ℝ × Z) →ᵇ ℝ) (G : ℝ) : Prop := + ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, ∀ z z' : Z, + v (x, z) - v (y, z') ≤ G + +theorem OscOn.mono {v : (ℝ × Z) →ᵇ ℝ} {G G' : ℝ} (h : P.OscOn v G) (hG : G ≤ G') : + P.OscOn v G' := fun x hx y hy z z' => (h x hx y hy z z').trans hG + +/-- The spread the iteration can produce: the reward's spread, amplified by `(1 - β)⁻¹`. At a +zero borrowing limit this is definitionally `CRRAConstants.oscGap`, so the witnesses' bounds on +that transfer unchanged. -/ +noncomputable def oscSpread : ℝ := + (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) + +/-- At a zero borrowing limit the spread is `CRRAConstants.oscGap`. -/ +theorem oscSpread_eq_oscGap {assetCap : ℝ} (Q : IncomeFluctuation Z 0 assetCap) : + Q.oscSpread = Q.oscGap := rfl + +theorem oscSpread_nonneg : 0 ≤ P.oscSpread := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) + exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) + (P.mem_dom_of_pos P.maxConsumption_pos) P.minConsumption_le_maxConsumption + +/-- The continuation inherits the bound, even across different income states. -/ +theorem contOf_sub_le_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (h : P.OscOn v G) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (z z' : Z) : + P.contOf v z x - P.contOf v z' y ≤ G := by + classical + obtain ⟨z₁, -, hz₁⟩ := Finset.exists_max_image Finset.univ (fun w : Z => v (x, w)) + ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + obtain ⟨z₂, -, hz₂⟩ := Finset.exists_min_image Finset.univ (fun w : Z => v (y, w)) + ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + have h1 : P.contOf v z x ≤ v (x, z₁) := by + have hle : ∑ w : Z, P.transitionMatrix z w * v (x, w) + ≤ ∑ w : Z, P.transitionMatrix z w * v (x, z₁) := + Finset.sum_le_sum fun w _ => + mul_le_mul_of_nonneg_left (hz₁ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z w) + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle + exact hle + have h2 : v (y, z₂) ≤ P.contOf v z' y := by + have hle : ∑ w : Z, P.transitionMatrix z' w * v (y, z₂) + ≤ ∑ w : Z, P.transitionMatrix z' w * v (y, w) := + Finset.sum_le_sum fun w _ => + mul_le_mul_of_nonneg_left (hz₂ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z' w) + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle + exact hle + linarith [h x hx y hy z₁ z₂] + +theorem oscOn_zero : P.OscOn (0 : (ℝ × Z) →ᵇ ℝ) 0 := fun _ _ _ _ _ _ => by simp + +/-- **The Bellman operator adds the reward's spread and contracts the continuation's.** -/ +theorem oscOn_bellman {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (h : P.OscOn v G) : + P.OscOn (P.toExtended.bellman v) + (P.u P.maxConsumption - P.u P.minConsumption + P.discount * G) := by + intro x hx y hy z z' + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hba : ∀ (t : ℝ) (w : Z), (P.toExtended.bellman v) (t, w) + = P.toExtended.bellmanFn v (t, w) := fun t w => rfl + -- the optimum at `(x, z)`, priced above + obtain ⟨-, hup⟩ := P.bellmanFn_eq_of_optimal (v := v) (s := (x, z)) hx + (P.policyOf_mem v (x, z)) (P.policyOf_optimal v (x, z)) + have hAmem : P.policyOf v (x, z) ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (x, z)) + have hcle : P.u (P.consumptionFnOf v z x) ≤ P.u P.maxConsumption := + P.monotoneOn_u_dom (P.consumption_policyOf_mem_dom v hx) P.maxConsumption_mem_dom + (P.consumption_le_maxConsumption (s := (x, z)) hx (P.policyOf_mem v (x, z)).1) + -- saving the minimum at `(y, z')`, priced below + have hfl : assetFloor ∈ P.toExtended.feasible (y, z') := ⟨le_rfl, le_max_left _ _⟩ + have hmin : P.minConsumption ≤ P.consumption (y, z') assetFloor := + P.minConsumption_le_consumption_floor (y, z') + have hminpos : 0 < P.consumption (y, z') assetFloor := + lt_of_lt_of_le P.minConsumption_pos hmin + have hlow := P.lazyValue_le_dom v z' hy hfl (P.mem_dom_of_pos hminpos) + have hulow : P.u P.minConsumption ≤ P.u (P.consumption (y, z') assetFloor) := + P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) + (P.mem_dom_of_pos hminpos) hmin + simp only [lazyValue] at hlow + have hcont : P.contOf v z (P.policyOf v (x, z)) - P.contOf v z' assetFloor ≤ G := + P.contOf_sub_le_osc h hAmem ⟨le_rfl, P.assetFloor_le_assetCap⟩ z z' + have hscaled := mul_le_mul_of_nonneg_left hcont hβ + rw [hba x z, hba y z', hup] + simp only [contOf] at hscaled hlow ⊢ + have hcx : P.consumptionFnOf v z x = P.consumption (x, z) (P.policyOf v (x, z)) := rfl + rw [hcx] at hcle + have hres : P.resources (y, z') - assetFloor = P.consumption (y, z') assetFloor := rfl + rw [hres] at hlow + linarith + +/-- Every iterate from zero oscillates by at most `oscGap`. -/ +theorem oscOn_iterate (n : ℕ) : + P.OscOn ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) P.oscSpread := by + have hβ1 : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + have hfix : P.u P.maxConsumption - P.u P.minConsumption + (P.discount : ℝ) * P.oscSpread + = P.oscSpread := by + have hne : (1 : ℝ) - (P.discount : ℝ) ≠ 0 := by linarith + simp only [oscSpread] + field_simp + ring + induction n with + | zero => + intro x hx y hy z z' + simpa using P.oscSpread_nonneg + | succ k ih => + rw [Function.iterate_succ_apply'] + have := P.oscOn_bellman ih + rwa [hfix] at this + +/-! ### A uniform bound on saving, and hence on the cap + +The last input is quantitative: the household must not want to save all the way to the imposed +ceiling. `policy_le_of_marginal_bound` does this at the fixed point; the same deviation — save +half as far above the borrowing limit — works against any continuation, with `‖v‖` in place of +`‖V‖`. The iterates are uniformly bounded because the Bellman operator adds at most the reward +and contracts what it inherits, so one calibration inequality covers the whole iteration. -/ + +/-- **The fixed point oscillates by no more than the iterates do**, being their uniform limit. -/ +theorem oscOn_valueFunction : P.OscOn P.toExtended.valueFunction P.oscSpread := by + intro x hx y hy z z' + have hpt : ∀ p : ℝ × Z, Tendsto + (fun n => ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) p) atTop + (𝓝 (P.toExtended.valueFunction p)) := fun p => + (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp + (P.toExtended.tendsto_iterate_valueFunction 0)).tendsto_at p + exact le_of_tendsto_of_tendsto ((hpt (x, z)).sub (hpt (y, z'))) tendsto_const_nhds + (Eventually.of_forall fun n => P.oscOn_iterate n x hx y hy z z') + +theorem abs_contOf_le (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : |P.contOf v z x| ≤ ‖v‖ := by + have h1 : |∑ z' : Z, P.transitionMatrix z z' * v (x, z')| + ≤ ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| := Finset.abs_sum_le_sum_abs _ _ + have h2 : ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| + ≤ ∑ z' : Z, P.transitionMatrix z z' * ‖v‖ := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] + exact mul_le_mul_of_nonneg_left + (by simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (x, z')) + (P.transitionMatrix_nonneg z z') + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at h2 + simp only [contOf] + linarith + +/-- **A uniform bound on saving from a uniform marginal bound**, against any continuation. -/ +theorem policyOf_le_of_marginal_bound {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) {m : ℝ} (hm : 0 < m) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.policyOf v (a, z) ≤ assetFloor + 4 * (P.discount : ℝ) * ‖v‖ / m := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hnv : (0 : ℝ) ≤ ‖v‖ := norm_nonneg _ + have hbound : (0 : ℝ) ≤ 4 * (P.discount : ℝ) * ‖v‖ / m := by positivity + set b : ℝ := P.policyOf v (a, z) with hbdef + have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos + · rw [← hzero]; linarith + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hcdom : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hcpos : 0 ≤ c := P.nonneg_of_mem_dom hcdom + have hres : P.resources (a, z) = c + b := by + simp only [hcdef, consumptionFnOf, consumption]; ring + set w : ℝ := (b + assetFloor) / 2 with hwdef + have hw0 : assetFloor < w := by rw [hwdef]; linarith + have hwb : w < b := by rw [hwdef]; linarith + have heq : b - w = w - assetFloor := by rw [hwdef]; ring + have hfeas : w ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcons : P.consumption (a, z) w = c + (b - assetFloor) / 2 := by + simp only [consumption, hres, hwdef]; ring + have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) + simp only [objROf, hcons] at hopt + rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, + show P.policyOf v (a, z) = b from rfl] at hopt + -- concavity of the continuation caps what the forgone saving could have repaid + have hfloormem : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent hfloormem hbreg hw0 hwb + have hb1 := abs_le.mp (P.abs_contOf_le v z w) + have hb0 := abs_le.mp (P.abs_contOf_le v z assetFloor) + have hloss : P.contOf v z b - P.contOf v z w ≤ 2 * ‖v‖ := by + rw [div_le_div_iff₀ (by linarith) (by linarith), ← heq] at hslope + have := le_of_mul_le_mul_right hslope (by linarith : (0 : ℝ) < b - w) + linarith [hb1.2, hb0.1] + have hcmax : c + (b - assetFloor) / 2 ≤ P.maxConsumption := by + have hcm := P.consumption_le_maxConsumption (s := (a, z)) ha hw0.le + rwa [hcons] at hcm + have hmg := hmarg (c + (b - assetFloor) / 2) c hcdom (by linarith) hcmax + rw [show c + (b - assetFloor) / 2 - c = (b - assetFloor) / 2 from by ring] at hmg + rw [← sub_le_iff_le_add', le_div_iff₀ hm] + nlinarith [hopt, hmg, mul_le_mul_of_nonneg_left hloss hβ] + +/-- The Bellman operator adds at most the reward and contracts what it inherits. -/ +theorem norm_bellman_le (v : (ℝ × Z) →ᵇ ℝ) : + ‖P.toExtended.bellman v‖ + ≤ max |P.toExtended.rewardMin| |P.toExtended.rewardMax| + (P.discount : ℝ) * ‖v‖ := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hM : (0 : ℝ) ≤ max |P.toExtended.rewardMin| |P.toExtended.rewardMax| := + le_max_of_le_left (abs_nonneg _) + refine (BoundedContinuousFunction.norm_le (by positivity)).mpr fun s => ?_ + have hb := P.toExtended.bellmanFn_bounds v s + have hval : (P.toExtended.bellman v) s = P.toExtended.bellmanFn v s := rfl + rw [hval, Real.norm_eq_abs, abs_le] + simp only [ExtendedStochasticProgram.loBound, ExtendedStochasticProgram.hiBound] at hb + have h1 : -|P.toExtended.rewardMin| ≤ P.toExtended.rewardMin := neg_abs_le _ + have h2 : P.toExtended.rewardMax ≤ |P.toExtended.rewardMax| := le_abs_self _ + have h3 : -max |P.toExtended.rewardMin| |P.toExtended.rewardMax| + ≤ -|P.toExtended.rewardMin| := by + simp only [neg_le_neg_iff]; exact le_max_left _ _ + have h4 : |P.toExtended.rewardMax| + ≤ max |P.toExtended.rewardMin| |P.toExtended.rewardMax| := le_max_right _ _ + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + rw [hdx] at hb + constructor <;> linarith [hb.1, hb.2] + +/-- **The iterates are uniformly bounded**, so one calibration covers them all. -/ +theorem norm_iterate_le (n : ℕ) : + ‖(P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)‖ + ≤ max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount) := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hβ1 : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + have hM : (0 : ℝ) ≤ max |P.toExtended.rewardMin| |P.toExtended.rewardMax| := + le_max_of_le_left (abs_nonneg _) + have hfix : max |P.toExtended.rewardMin| |P.toExtended.rewardMax| + + (P.discount : ℝ) * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| + / (1 - P.discount)) + = max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount) := by + have hne : (1 : ℝ) - (P.discount : ℝ) ≠ 0 := by linarith + field_simp + ring + induction n with + | zero => simpa using by positivity + | succ k ih => + rw [Function.iterate_succ_apply'] + refine le_trans (P.norm_bellman_le _) ?_ + rw [← hfix] + have := mul_le_mul_of_nonneg_left ih hβ + linarith + +/-! ### Carroll and Kimball, assembled along the iteration -/ + +/-- The limit step, split off from `concaveOn_consumptionFn_of_preserves`: concavity of the +consumption functions of the iterates passes to the fixed point, because optimal actions survive +a uniform limit. -/ +theorem concaveOn_consumptionFn_of_iterates + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + set T := P.toExtended.bellman with hTdef + have hlim : Tendsto (fun n => T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) atTop + (𝓝 P.toExtended.valueFunction) := P.toExtended.tendsto_iterate_valueFunction 0 + have hpt : ∀ a ∈ Icc assetFloor assetCap, + Tendsto (fun n => P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (P.consumptionFn z a)) := by + intro a ha + have hp := P.tendsto_policyOf hlim P.concaveSlices_valueFunction (s := (a, z)) ha + have he : P.consumptionFn z a + = P.resources (a, z) - P.policyOf P.toExtended.valueFunction (a, z) := rfl + rw [he] + simpa only [consumptionFnOf, consumption] using tendsto_const_nhds.sub hp + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ + refine le_of_tendsto_of_tendsto + (((hpt x hx).const_smul θ).add ((hpt y hy).const_smul φ)) (hpt _ hm) + (Eventually.of_forall fun n => (hcons n z).2 hx hy hθ hφ hθφ) + +/-- **Carroll and Kimball (1996) for CRRA.** The consumption function is concave. + +Everything is now proved except the asset cap: `hslack` says the household never wants to save +all the way to the imposed ceiling, at any stage of the iteration. That is the artefact the cap +always was — `crra_policy_lt_assetCap` discharges it at the fixed point by calibration, and +`policy_assetCap_of_patient` shows it is exactly `β (1 + r) < 1` that makes it true. -/ +theorem concaveOn_consumptionFnOf_iterates_of_crra {γ : ℝ} (hpc : P.PositiveConsumptionAll) + (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) + (hslack : ∀ n : ℕ, ∀ a ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : + ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z) := by + set T := P.toExtended.bellman with hTdef + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + · intro n + induction n with + | zero => exact P.concaveOn_consumptionFnOf_zero + | succ k ih => + intro z + have hnext : ∀ a ∈ Icc assetFloor assetCap, + P.policyOf (T (T^[k] (0 : (ℝ × Z) →ᵇ ℝ))) (a, z) < P.maxSaving (a, z) := by + intro a ha + have hs := hslack (k + 1) a ha z + rw [Function.iterate_succ_apply'] at hs + exact P.policyOf_lt_maxSaving hpc (P.concaveSlices_bellman (hslices k)) ha hs + rw [Function.iterate_succ_apply'] + exact P.concaveOn_consumptionFnOf_bellman hγ0 hβ hu z + (fun z' A hA => hpc _ (hslices k) z' A hA) ih + (fun z' x hx y hy hxy => P.consumptionFnOf_mono (hslices k) hx hy hxy) + (fun a ha => hpc _ (P.concaveSlices_bellman (hslices k)) z a ha) hnext + fun A hA z' => P.policyOf_lt_maxSaving hpc (hslices k) hA (hslack k A hA z') + +/-- **Carroll and Kimball for CRRA**, at the fixed point. -/ +theorem concaveOn_consumptionFn_of_crra {γ : ℝ} (hpc : P.PositiveConsumptionAll) (hγ0 : 0 < γ) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) + (hslack : ∀ n : ℕ, ∀ a ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := + P.concaveOn_consumptionFn_of_iterates + (P.concaveOn_consumptionFnOf_iterates_of_crra hpc hγ0 hβ hu hslack) z + + +/-- **Carroll and Kimball (1996) for CRRA, from a calibration.** Beyond CRRA utility and +marginal utility unbounded at zero, the only hypothesis left is one inequality: the asset cap +must sit above the uniform bound on saving that the marginal bound `m` produces. Nothing about +the borrowing limit, and nothing about interiority. + +That inequality is the cap being an artefact, which is what it was always meant to be: by +`policy_assetCap_of_patient` it is exactly impatience that makes it true. -/ +theorem concaveOn_consumptionFn_of_marginal {γ m : ℝ} (hpc : P.PositiveConsumptionAll) + (hγ0 : 0 < γ) + (hβ : 0 < (P.discount : ℝ)) (hu : P.u = crraUtility γ) (hm : 0 < m) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + (hcap : assetFloor + 4 * (P.discount : ℝ) + * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount)) / m + < assetCap) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + refine P.concaveOn_consumptionFn_of_crra hpc hγ0 hβ hu (fun n a ha z' => ?_) z + have hb := P.policyOf_le_of_marginal_bound (hslices n) hm hmarg ha z' + have hn := P.norm_iterate_le n + have h4 : (0 : ℝ) ≤ 4 * (P.discount : ℝ) := by positivity + have hstep := mul_le_mul_of_nonneg_left hn h4 + have hmono : 4 * (P.discount : ℝ) * ‖(P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)‖ / m + ≤ 4 * (P.discount : ℝ) * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| + / (1 - P.discount)) / m := by + rw [div_le_div_iff₀ hm hm]; nlinarith [hstep, hm] + linarith + +/-! ### Positive consumption for the bounded family + +With utility unbounded below, consumption is positive because zero consumption is worth `⊥`. +With CES and `γ < 1` it is not: zero consumption is admissible and the floor has to be earned +from the MARGIN, which is what `ConsumptionFloor` does at the fixed point. The induction sees +the iterates, so the same argument is run against an arbitrary continuation — and it goes +through unchanged, because it only ever used concavity and boundedness of the continuation. -/ + +/-- The state-independent bound on the continuation's slope, read against `v`. -/ +noncomputable def contSlopeConstOf (v : (ℝ × Z) →ᵇ ℝ) : ℝ := 4 * ‖v‖ / P.minConsumption + +theorem contSlopeConstOf_nonneg (v : (ℝ × Z) →ᵇ ℝ) : 0 ≤ P.contSlopeConstOf v := + div_nonneg (by positivity) P.minConsumption_pos.le + +/-- **The continuation's slope is bounded by a state-independent constant**, above +`assetFloor + minConsumption / 2`, whatever the continuation. -/ +theorem contOf_sub_le {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (z : Z) + {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.contOf v z y - P.contOf v z x ≤ P.contSlopeConstOf v * (y - x) := by + have hmin := P.minConsumption_pos + have hx0 : assetFloor < x := by linarith + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy + have hb1 := abs_le.mp (P.abs_contOf_le v z x) + have hb2 := abs_le.mp (P.abs_contOf_le v z assetFloor) + have hhalf : P.contSlopeConstOf v * (P.minConsumption / 2) = 2 * ‖v‖ := by + rw [contSlopeConstOf]; field_simp; ring + have hbig : P.contOf v z x - P.contOf v z assetFloor + ≤ P.contSlopeConstOf v * (x - assetFloor) := by + have hstep : P.contSlopeConstOf v * (P.minConsumption / 2) + ≤ P.contSlopeConstOf v * (x - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) (P.contSlopeConstOf_nonneg v) + rw [hhalf] at hstep + linarith [hb1.2, hb2.1] + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) + ≤ (P.contSlopeConstOf v * (x - assetFloor)) * (y - x) := by + nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] + nlinarith [hprod, hx0] + +/-- **A consumption floor from the margin, against an arbitrary continuation.** -/ +theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) : + ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by + have hmin := P.minConsumption_pos + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + obtain ⟨δ₀, hδ₀, hδ⟩ := hu ((P.discount : ℝ) * P.contSlopeConstOf v + 1) + refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ + by_contra hlt + rw [not_le] at hlt + set d : ℝ := min δ₀ (P.minConsumption / 4) with hd + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hltc : c < d := hlt + have hdδ : d ≤ δ₀ := min_le_left _ _ + have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ + have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem + have hres : P.minConsumption ≤ P.resources (a, z) - assetFloor := + P.minConsumption_le_consumption_floor (a, z) + have hcb : c = P.resources (a, z) - b := rfl + have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith + set h : ℝ := d - c with hhdef + have hh0 : 0 < h := by rw [hhdef]; linarith + have hhle : h ≤ P.minConsumption / 4 := by rw [hhdef]; linarith + have hlow : assetFloor + P.minConsumption / 2 ≤ b - h := by linarith + have hbmem : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hfeas : b - h ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcd : P.consumption (a, z) (b - h) = d := by + simp only [consumption] at hcb ⊢; linarith + have hdpos : 0 < P.consumption (a, z) (b - h) := by rw [hcd]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) + simp only [objROf, hcd] at hopt + have hslope := P.contOf_sub_le hv z hlow (by linarith) hbmem + rw [show b - (b - h) = h from by ring] at hslope + have hscaled := mul_le_mul_of_nonneg_left hslope hβ + have hutil : P.u d - P.u c + ≤ ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by + have e : (P.discount : ℝ) * (P.contSlopeConstOf v * h) + = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by rw [hhdef]; ring + linarith [hopt, hscaled, e.le, e.ge] + have hmarg := hδ c d hcmem (by linarith) hdδ + rw [show ((P.discount : ℝ) * P.contSlopeConstOf v + 1) * (d - c) + = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) + (d - c) from by ring] at hmarg + rw [hhdef] at hh0 + linarith [hmarg, hutil, hh0] + +/-- **Positive consumption against every continuation, for the bounded family.** -/ +theorem positiveConsumptionAll_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : + P.PositiveConsumptionAll := by + intro v hv z a ha + obtain ⟨δ, hδ, hspec⟩ := P.exists_consumptionFnOf_floor_of_marginalInada hu hv + exact lt_of_lt_of_le hδ (hspec z a ha) + +/-- **Carroll and Kimball for the BOUNDED CES family**, `0 < γ < 1`, where utility does not fall +to `-∞` and positivity of consumption has to be earned. -/ +theorem concaveOn_consumptionFn_of_bounded_crra {γ m : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hβ : 0 < (P.discount : ℝ)) (hb : P.Bounded) (hu : P.u = crraUtility γ) (hm : 0 < m) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + (hcap : assetFloor + 4 * (P.discount : ℝ) + * (max |P.toExtended.rewardMin| |P.toExtended.rewardMax| / (1 - P.discount)) / m + < assetCap) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + refine P.concaveOn_consumptionFn_of_marginal (P.positiveConsumptionAll_of_marginalInada ?_) + hγ0 hβ hu hm hmarg hcap z + rw [hb, hu] + exact marginalInadaOn_Ici_crraUtility hγ0 hγ1 + +/-! ### The multiplicative CES bound on saving + +`policyOf_le_of_marginal_bound` prices the deviation against the LEVEL of the continuation, and +for CES that is hopeless: at the `cesWitness` parameters it gives a bound about six times the +asset cap. The fix is the one `crra_policy_le_mul` makes at the fixed point — deviate to a +FRACTION `θ` of the saving rather than half of it, and price the extra consumption at the margin +where it lands. The bound then involves the continuation's OSCILLATION, not its level, and it is +proportional to consumption rather than constant. -/ + +/-- **The proportional CES bound on saving, against an arbitrary continuation.** -/ +theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumptionAll) {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) (hosc : P.OscOn v G) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.policyOf v (a, z) - assetFloor + ≤ ((P.discount : ℝ) * G / θ) + * (P.consumptionFnOf v z a + + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hcpos : 0 < c := hpc v hv z a ha + have hbase : (0 : ℝ) < c + (1 - θ) * (b - assetFloor) := by + have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) + (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) + linarith + have hrpow : (0 : ℝ) < (c + (1 - θ) * (b - assetFloor)) ^ γ := Real.rpow_pos_of_pos hbase _ + rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos + · rw [← hzero, sub_self] + positivity + have hbf : (0 : ℝ) < b - assetFloor := by linarith + set w : ℝ := assetFloor + θ * (b - assetFloor) with hwdef + have hw0 : assetFloor < w := by rw [hwdef]; nlinarith + have hwb : w < b := by rw [hwdef]; nlinarith + have hwmem : w ∈ Icc assetFloor assetCap := ⟨hw0.le, by linarith [hbreg.2]⟩ + have hfeas : w ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcb : c = P.resources (a, z) - b := rfl + have hcons : P.consumption (a, z) w = c + (1 - θ) * (b - assetFloor) := by + simp only [consumption, hwdef]; rw [hcb]; ring + have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) + simp only [objROf, hcons] at hopt + rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, + show P.policyOf v (a, z) = b from rfl] at hopt + -- concavity of the continuation, priced against its oscillation + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hbreg hw0 hwb + have hgap := P.contOf_sub_le_osc hosc hwmem + (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z + have hbw : b - w = (1 - θ) * (b - assetFloor) := by rw [hwdef]; ring + have hwf : w - assetFloor = θ * (b - assetFloor) := by rw [hwdef]; ring + have hloss : P.contOf v z b - P.contOf v z w ≤ (1 - θ) / θ * G := by + rw [div_le_div_iff₀ (show (0 : ℝ) < b - w by linarith) + (show (0 : ℝ) < w - assetFloor by linarith), hbw, hwf] at hslope + have h2 : (P.contOf v z b - P.contOf v z w) * (θ * (b - assetFloor)) + ≤ G * ((1 - θ) * (b - assetFloor)) := by + nlinarith [hslope, hgap, mul_pos (show (0:ℝ) < 1 - θ by linarith) hbf] + rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] + nlinarith [h2, hbf] + -- the marginal gain, priced where it lands + have hmg : (c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) + ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by + rw [hu] + have hbound := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hcpos.le) + (le_add_of_nonneg_right (mul_nonneg (by linarith) hbf.le)) + (le_refl (c + (1 - θ) * (b - assetFloor))) + simpa using hbound + have hmid : P.u (c + (1 - θ) * (b - assetFloor)) - P.u c + ≤ (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) := by + have hd : (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) + = (P.discount : ℝ) * P.contOf v z b - (P.discount : ℝ) * P.contOf v z w := by ring + linarith [hopt, hd.le, hd.ge] + have hkey := le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) + rw [Real.rpow_neg hbase.le γ] at hkey + have h1θ : (0 : ℝ) < 1 - θ := by linarith + have hexp : ((c + (1 - θ) * (b - assetFloor)) ^ γ)⁻¹ * ((1 - θ) * (b - assetFloor)) + = (1 - θ) * ((b - assetFloor) / (c + (1 - θ) * (b - assetFloor)) ^ γ) := by field_simp + rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * G) + = (1 - θ) * ((P.discount : ℝ) * G / θ) from by ring] at hkey + have hdiv := le_of_mul_le_mul_left hkey h1θ + rw [div_le_iff₀ hrpow] at hdiv + linarith + +/-- **Saving never reaches the asset cap, against an arbitrary continuation.** The hypothesis is +the one `crra_policy_lt_assetCap` carries at the fixed point, so the calibrations already proved +for the CES witnesses discharge it. -/ +theorem crra_policyOf_lt_assetCap {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) + (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumptionAll) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) + (hosc : P.OscOn v G) + (hlt : ((P.discount : ℝ) * G / θ) + * (P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ + < assetCap - assetFloor) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.policyOf v (a, z) < assetCap := by + by_contra hcon + rw [not_lt] at hcon + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hkey := P.crra_policyOf_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc hv hG hosc ha z + rw [← hbdef, ← hcdef] at hkey + have hcpos : 0 < c := hpc v hv z a ha + have hres : P.resources (a, z) - assetFloor ≤ P.maxConsumption := + P.consumption_le_maxConsumption (s := (a, z)) ha le_rfl + have hsum : c + (1 - θ) * (b - assetFloor) + = (P.resources (a, z) - assetFloor) - θ * (b - assetFloor) := by + simp only [hcdef, consumptionFnOf, consumption, ← hbdef]; ring + have hle : c + (1 - θ) * (b - assetFloor) + ≤ P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor := by + rw [hsum] + have hθb : θ * (assetCap - assetFloor) ≤ θ * (b - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) hθ0.le + simp only [maxConsumption] at hres + nlinarith [hres, hθb] + have hnn : (0 : ℝ) ≤ c + (1 - θ) * (b - assetFloor) := by + have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) + (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) + linarith + have hmono : (c + (1 - θ) * (b - assetFloor)) ^ γ + ≤ (P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ := + Real.rpow_le_rpow hnn hle hγ0.le + have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * G / θ := by positivity + nlinarith [hkey, mul_le_mul_of_nonneg_left hmono hcoef, hlt] + +/-- **Carroll and Kimball for the bounded CES family, at the witnesses' own calibration.** The +cap hypothesis is exactly `crra_policy_lt_assetCap`'s, with `oscSpread` for `oscGap` — the same +number at a zero borrowing limit. -/ +theorem concaveOn_consumptionFn_of_oscSpread {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) + (hθ0 : 0 < θ) (hθ1 : θ < 1) (hβ : 0 < (P.discount : ℝ)) (hb : P.Bounded) + (hu : P.u = crraUtility γ) + (hlt : ((P.discount : ℝ) * P.oscSpread / θ) + * (P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ + < assetCap - assetFloor) + (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + have hpc : P.PositiveConsumptionAll := by + refine P.positiveConsumptionAll_of_marginalInada ?_ + rw [hb, hu] + exact marginalInadaOn_Ici_crraUtility hγ0 hγ1 + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + exact P.concaveOn_consumptionFn_of_iterates + (P.concaveOn_consumptionFnOf_iterates_of_crra hpc hγ0 hβ hu + (fun n a ha z' => P.crra_policyOf_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc (hslices n) + P.oscSpread_nonneg (P.oscOn_iterate n) hlt ha z')) z + + + +/-! ### A slope bound from the OSCILLATION rather than the level + +`contSlopeConstOf` prices the continuation's slope by `‖v‖`, which is what the CRRA arguments +had to hand. For utilities whose marginal value at zero is FINITE — CARA, and shifted CRRA with +a subsistence level — the consumption floor has to beat that slope, and the level is far too +crude a bound to beat. The oscillation is the right measure and it is the one the iterates +control (`oscOn_iterate`), so the same geometry is redone with `G` in place of `2‖v‖`. -/ + +/-- The slope constant an oscillation bound `G` gives. -/ +noncomputable def oscSlopeConst (G : ℝ) : ℝ := 2 * G / P.minConsumption + +theorem oscSlopeConst_nonneg {G : ℝ} (hG : 0 ≤ G) : 0 ≤ P.oscSlopeConst G := + div_nonneg (by linarith) P.minConsumption_pos.le + +/-- **The continuation's slope, priced by its oscillation**, above +`assetFloor + minConsumption / 2`. -/ +theorem contOf_sub_le_of_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (hG : 0 ≤ G) + (hv : ConcaveSlices assetFloor assetCap v) (hosc : P.OscOn v G) (z : Z) {x y : ℝ} + (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.contOf v z y - P.contOf v z x ≤ P.oscSlopeConst G * (y - x) := by + have hmin := P.minConsumption_pos + have hx0 : assetFloor < x := by linarith + have hxmem : x ∈ Icc assetFloor assetCap := ⟨hx0.le, by linarith [hy.2]⟩ + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy + have hgap := P.contOf_sub_le_osc hosc hxmem + (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z + have hhalf : P.oscSlopeConst G * (P.minConsumption / 2) = G := by + rw [oscSlopeConst]; field_simp + have hbig : P.contOf v z x - P.contOf v z assetFloor + ≤ P.oscSlopeConst G * (x - assetFloor) := by + have hstep : P.oscSlopeConst G * (P.minConsumption / 2) + ≤ P.oscSlopeConst G * (x - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) (P.oscSlopeConst_nonneg hG) + rw [hhalf] at hstep + linarith + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) + ≤ (P.oscSlopeConst G * (x - assetFloor)) * (y - x) := by + nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] + nlinarith [hprod, hx0] + +/-! ### A consumption floor from a FINITE marginal bound + +`exists_consumptionFnOf_floor_of_marginalInada` needs marginal utility to explode at zero, which +excludes every HARA member except pure CRRA. What the argument actually uses is that the +marginal value of consumption near zero beats the discounted slope of the continuation, and for +a finite marginal bound `M` that is an inequality to check rather than a triviality. -/ + +/-- **The consumption floor, from a finite marginal bound.** -/ +theorem exists_consumptionFnOf_floor_of_marginal {M G : ℝ} + (hM : MarginalBoundOn P.dom P.u M) (hG : 0 ≤ G) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hosc : P.OscOn v G) + (hlt : (P.discount : ℝ) * P.oscSlopeConst G < M) : + ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by + have hmin := P.minConsumption_pos + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + obtain ⟨δ₀, hδ₀, hδ⟩ := hM + refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ + by_contra hlt' + rw [not_le] at hlt' + set d : ℝ := min δ₀ (P.minConsumption / 4) with hd + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hltc : c < d := hlt' + have hdδ : d ≤ δ₀ := min_le_left _ _ + have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ + have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem + have hres : P.minConsumption ≤ P.resources (a, z) - assetFloor := + P.minConsumption_le_consumption_floor (a, z) + have hcb : c = P.resources (a, z) - b := rfl + have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith + set hgap : ℝ := d - c with hhdef + have hh0 : 0 < hgap := by rw [hhdef]; linarith + have hhle : hgap ≤ P.minConsumption / 4 := by rw [hhdef]; linarith + have hlow : assetFloor + P.minConsumption / 2 ≤ b - hgap := by linarith + have hbmem : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hfeas : b - hgap ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcd : P.consumption (a, z) (b - hgap) = d := by + simp only [consumption] at hcb ⊢; linarith + have hdpos : 0 < P.consumption (a, z) (b - hgap) := by rw [hcd]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) + simp only [objROf, hcd] at hopt + have hslope := P.contOf_sub_le_of_osc hG hv hosc z hlow (by linarith) hbmem + rw [show b - (b - hgap) = hgap from by ring] at hslope + have hscaled := mul_le_mul_of_nonneg_left hslope hβ + have hutil : P.u d - P.u c ≤ ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by + have e : (P.discount : ℝ) * (P.oscSlopeConst G * hgap) + = ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by rw [hhdef]; ring + linarith [hopt, hscaled, e.le, e.ge] + have hmarg := hδ c d hcmem (by linarith) hdδ + rw [hhdef] at hh0 + nlinarith [hmarg, hutil, hh0, hlt] + +/-- **Positive consumption from a finite marginal bound**, against every continuation with +concave slices and oscillation at most `G`. -/ +theorem consumptionFnOf_pos_of_marginal {M G : ℝ} + (hM : MarginalBoundOn P.dom P.u M) (hG : 0 ≤ G) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hosc : P.OscOn v G) + (hlt : (P.discount : ℝ) * P.oscSlopeConst G < M) + (z : Z) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : 0 < P.consumptionFnOf v z a := by + obtain ⟨δ, hδ, hfloor⟩ := P.exists_consumptionFnOf_floor_of_marginal hM hG hv hosc hlt + exact lt_of_lt_of_le hδ (hfloor z a ha) + +/-! ### Single crossing in the continuation + +Two continuations at the SAME state, ordered by increasing differences. This is +`policy_le_of_cont_increasingDifferences` of `IncomeFluctuationRateMonotone` read against +arbitrary continuations rather than at the fixed point, and it is what carries Light's step 5 +along the iteration: the induction hypothesis is exactly increasing differences of the two +iterates. -/ + +/-- **Increasing differences between two continuations**, in the same economy. -/ +def ContOfIncreasingDifferences (v w : (ℝ × Z) →ᵇ ℝ) : Prop := + ∀ (z : Z) (x y : ℝ), x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → + P.contOf v z y - P.contOf v z x ≤ P.contOf w z y - P.contOf w z x + +/-- **Increasing differences slice by slice**, between two bounded continuous functions on the +state space. This is the property the induction of Light's step 5 carries along the iteration: +it mentions no economy, so it makes sense between the iterates of two DIFFERENT economies, and +it is closed under pointwise limits. -/ +def IncDiffSlices (assetFloor assetCap : ℝ) (v w : (ℝ × Z) →ᵇ ℝ) : Prop := + ∀ (z : Z) (x y : ℝ), x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → + v (y, z) - v (x, z) ≤ w (y, z) - w (x, z) + +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] in +theorem incDiffSlices_zero (assetFloor assetCap : ℝ) : + IncDiffSlices (Z := Z) assetFloor assetCap 0 0 := by + intro z x y _ _ _ + simp + +/-- It suffices to have increasing differences slice by slice in the continuations themselves. -/ +theorem contOfIncreasingDifferences_of_slices {v w : (ℝ × Z) →ᵇ ℝ} + (h : IncDiffSlices assetFloor assetCap v w) : + P.ContOfIncreasingDifferences v w := by + intro z x y hx hy hxy + simp only [contOf, ← Finset.sum_sub_distrib, ← mul_sub] + exact Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (h z' x y hx hy hxy) (P.transitionMatrix_nonneg z z') + +/-- **Single crossing in the continuation.** A household whose continuation has increasing +differences over another's saves at least as much, at every state. No derivative: the exchange +argument turns the two optimality comparisons into a sandwich that increasing differences +closes. -/ +theorem policyOf_le_of_contOf_increasingDifferences {v w : (ℝ × Z) →ᵇ ℝ} + (hw : ConcaveSlices assetFloor assetCap w) (hid : P.ContOfIncreasingDifferences v w) + {a : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) : + P.policyOf v (a, z) ≤ P.policyOf w (a, z) := by + by_contra hcon + rw [not_le] at hcon + set bV : ℝ := P.policyOf v (a, z) with hbV + set bW : ℝ := P.policyOf w (a, z) with hbW + have hbVm : bV ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v (a, z) + have hbWm : bW ∈ P.toExtended.feasible (a, z) := P.policyOf_mem w (a, z) + have hcV : P.consumption (a, z) bV ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hcW : P.consumption (a, z) bW ∈ P.dom := P.consumption_policyOf_mem_dom w ha + have hoptV := P.objROf_le_of_mem v ha hbWm hcW + have hoptW := P.objROf_le_of_mem w ha hbVm hcV + have hidd := hid z bW bV (P.feasible_subset_region hbWm) (P.feasible_subset_region hbVm) hcon.le + have hβ0 : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + simp only [objROf] at hoptV hoptW + have hWeq : P.objROf w (a, z) bV = P.objROf w (a, z) bW := by + simp only [objROf] + nlinarith [hoptV, hoptW, mul_le_mul_of_nonneg_left hidd hβ0] + have hbell : P.toExtended.objectiveE w (a, z) bV + = ((P.toExtended.bellmanFn w (a, z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of w ha hbVm hcV, hWeq, ← P.objectiveE_eq_coe_of w ha hbWm hcW] + exact P.policyOf_optimal w (a, z) + exact absurd (P.optimal_action_unique_of_concaveSlices hw ha hbVm hbWm hbell + (P.policyOf_optimal w (a, z))) (by intro h; rw [h] at hcon; exact lt_irrefl _ hcon) + +/-- Consumption is correspondingly ordered the other way. -/ +theorem consumptionFnOf_le_of_contOf_increasingDifferences {v w : (ℝ × Z) →ᵇ ℝ} + (hw : ConcaveSlices assetFloor assetCap w) (hid : P.ContOfIncreasingDifferences v w) + {a : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) : + P.consumptionFnOf w z a ≤ P.consumptionFnOf v z a := by + simp only [consumptionFnOf, consumption] + have := P.policyOf_le_of_contOf_increasingDifferences hw hid (z := z) ha + linarith + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean new file mode 100644 index 000000000..282b50119 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean @@ -0,0 +1,406 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds +public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements + + +/-! +# The Bellman operator preserves Lipschitz continuity in assets + +The economics half of the Lipschitz argument. With `isClosed_forall_lipschitzOn` this makes +the value function Lipschitz in assets, which bounds the marginal value of wealth uniformly +and so lets a corner condition be checked without differentiating anything. + +## The action swap + +Comparing a richer state `x` with a poorer state `y ≤ x`, the resource shortfall is +`Δ = (1+r)(x-y)`. Given any action `a'` feasible at `x`, the action `max 0 (a' - Δ)` is +feasible at `y` and — away from the truncation — leaves consumption EXACTLY unchanged. So the +whole difference passes to the continuation value, where the hypothesis on `v` bounds it by +`L·Δ`, and the utility term contributes nothing. + +Where the truncation bites, the poorer household saves nothing and therefore consumes all its +resources. That is what keeps the utility term controllable: both consumptions are then at +least `minIncome`, where a concave `u` is Lipschitz with the explicit constant of +`slopeBound`. No appeal to the cutoff lemma is needed, and no derivative. + +Those two cases are where the constant `(K + βL)(1+r)` comes from, and solving +`(K + βL)(1+r) ≤ L` for `L` is exactly where impatience `β(1+r) < 1` is required. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The Lipschitz constant of `u` above the lowest possible consumption. -/ +noncomputable def slopeBoundU : ℝ := slopeBound P.u P.minConsumption + +theorem slopeBoundU_nonneg : 0 ≤ P.slopeBoundU := + slopeBound_nonneg P.monotoneOn_u P.minConsumption_pos + +theorem abs_u_sub_le {c d : ℝ} (hc : P.minConsumption ≤ c) (hd : P.minConsumption ≤ d) : + |P.u c - P.u d| ≤ P.slopeBoundU * |c - d| := + P.strictConcaveOn_u.concaveOn.abs_sub_le_slopeBound_mul P.monotoneOn_u P.minConsumption_pos hc hd + +/-- The expectation inherits a Lipschitz bound from the continuation value, since the two +states share the income draw and hence the transition weights. -/ +theorem abs_expect_sub_le_lipschitz {v : (ℝ × Z) →ᵇ ℝ} {L : ℝ} + (hv : ∀ z : Z, ∀ p ∈ Icc assetFloor assetCap, ∀ q ∈ Icc assetFloor assetCap, + |v (p, z) - v (q, z)| ≤ L * |p - q|) + (z : Z) {a b : ℝ} (ha : a ∈ Icc assetFloor assetCap) (hb : b ∈ Icc assetFloor assetCap) + (x y : ℝ) : + |P.toExtended.expect v ((x, z), a) - P.toExtended.expect v ((y, z), b)| ≤ L * |a - b| := by + have hp : ∀ (w : ℝ) (c : ℝ), P.toExtended.expect v ((w, z), c) + = ∑ z' : Z, P.transitionMatrix z z' * v (c, z') := fun _ _ => rfl + rw [hp, hp, ← Finset.sum_sub_distrib] + calc |∑ z' : Z, (P.transitionMatrix z z' * v (a, z') - P.transitionMatrix z z' * v (b, z'))| + ≤ ∑ z' : Z, |P.transitionMatrix z z' * v (a, z') - P.transitionMatrix z z' * v (b, z')| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z' : Z, P.transitionMatrix z z' * (L * |a - b|) := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [← mul_sub, abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] + exact mul_le_mul_of_nonneg_left (hv z' a ha b hb) (P.transitionMatrix_nonneg z z') + _ = L * |a - b| := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + +/-- **The richer state's value exceeds the poorer's by at most the swap cost.** -/ +theorem bellmanFn_sub_le_of_lipschitz {v : (ℝ × Z) →ᵇ ℝ} {L : ℝ} (hL : 0 ≤ L) + (hv : ∀ z : Z, ∀ p ∈ Icc assetFloor assetCap, ∀ q ∈ Icc assetFloor assetCap, + |v (p, z) - v (q, z)| ≤ L * |p - q|) + (z : Z) {x y : ℝ} (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : y ≤ x) : + P.toExtended.bellmanFn v (x, z) - P.toExtended.bellmanFn v (y, z) + ≤ (P.slopeBoundU + P.discount * L) * ((1 + P.interest) * (x - y)) := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hK := P.slopeBoundU_nonneg + set Δ : ℝ := (1 + P.interest) * (x - y) with hΔdef + have hΔ0 : 0 ≤ Δ := mul_nonneg P.interest_gt_neg_one.le (by linarith) + have hres : P.resources (x, z) = P.resources (y, z) + Δ := by + simp only [resources, hΔdef] + rw [max_eq_right hx.1, max_eq_right hy.1]; ring + rw [sub_le_iff_le_add'] + refine P.toExtended.bellmanFn_le v fun a ha => ?_ + rcases eq_or_ne (P.toExtended.reward ((x, z), a)) ⊥ with hbot | hne + · rw [ExtendedStochasticProgram.objectiveE, hbot, EReal.bot_add]; exact bot_le + -- consumption is positive at the richer state, so the objective is real there + have hc : P.consumption (x, z) a ∈ P.dom := P.consumption_mem_dom_of_ne_bot hx ha hne + have ha0 : assetFloor ≤ a := ha.1 + have hacap : a ≤ assetCap := le_trans ha.2 (P.maxSaving_le_assetCap _) + have haR : a ≤ P.resources (x, z) := le_trans ha.2 (P.maxSaving_le_resources _) + set b : ℝ := max assetFloor (a - Δ) with hbdef + have hb0 : assetFloor ≤ b := le_max_left _ _ + have hba : b ≤ a := by rw [hbdef]; exact max_le ha0 (by linarith) + have hbmem : b ∈ P.toExtended.feasible (y, z) := by + rw [P.feasible_eq, P.maxSaving_eq] + refine ⟨hb0, le_min (le_trans hba hacap) ?_⟩ + rw [hbdef] + exact max_le (P.assetFloor_lt_resources _).le (by linarith [hres]) + -- consumption at the poorer state under the swapped action + have hcy : P.consumption (y, z) b = P.resources (y, z) - b := rfl + have hcymem : P.consumption (y, z) b ∈ P.dom := by + rcases le_total (a - assetFloor) Δ with h | h + · refine P.mem_dom_of_pos ?_ + rw [hcy, hbdef, max_eq_left (by linarith)] + linarith [P.minConsumption_le_consumption_floor (y, z), P.minConsumption_pos] + · refine P.dom_upward hc (le_of_eq ?_) + rw [hcy, hbdef, max_eq_right (by linarith)] + simp only [consumption]; linarith [hres] + -- both objectives are real; compare them + have hrx := P.reward_eq_coe_dom hx ha hc + have hry := P.reward_eq_coe_dom hy hbmem hcymem + have hexp := P.abs_expect_sub_le_lipschitz hv z (a := a) (b := b) + ⟨ha0, hacap⟩ ⟨hb0, le_trans hba hacap⟩ x y + have hgapa : |a - b| ≤ Δ := by + rw [hbdef] + rcases le_total (a - assetFloor) Δ with h | h + · rw [max_eq_left (by linarith), abs_of_nonneg (by linarith)]; exact h + · rw [max_eq_right (by linarith)] + rw [show a - (a - Δ) = Δ by ring, abs_of_nonneg hΔ0] + -- the utility term: equal consumptions off the truncation, both above `minIncome` on it + have hutil : P.u (P.consumption (x, z) a) - P.u (P.consumption (y, z) b) + ≤ P.slopeBoundU * Δ := by + rcases le_total Δ (a - assetFloor) with h | h + · -- no truncation: consumption is unchanged + have : P.consumption (y, z) b = P.consumption (x, z) a := by + rw [hcy, hbdef, max_eq_right (by linarith)] + simp only [consumption]; linarith [hres] + rw [this, sub_self] + positivity + · -- truncation: the poorer household borrows to the limit and eats the rest + have hb0' : b = assetFloor := by rw [hbdef, max_eq_left (by linarith)] + have hcyval : P.consumption (y, z) b = P.resources (y, z) - assetFloor := by + rw [hcy, hb0'] + have h1 : P.minConsumption ≤ P.consumption (y, z) b := by + rw [hcyval]; exact P.minConsumption_le_consumption_floor _ + have h2 : P.minConsumption ≤ P.consumption (x, z) a := by + simp only [consumption] + rw [hcyval] at h1 + linarith [hres] + have := P.abs_u_sub_le h2 h1 + rw [abs_le] at this + refine le_trans this.2 ?_ + refine mul_le_mul_of_nonneg_left ?_ hK + rw [abs_le] + constructor + · simp only [consumption, hb0']; linarith + · simp only [consumption, hb0']; linarith + -- assemble + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + have hstep : P.toExtended.objectiveE v (x, z) a + ≤ P.toExtended.objectiveE v (y, z) b + + (((P.slopeBoundU + P.discount * L) * Δ : ℝ) : EReal) := by + simp only [ExtendedStochasticProgram.objectiveE, hrx, hry, hdx] + rw [← EReal.coe_add, add_assoc, ← EReal.coe_add, ← EReal.coe_add, EReal.coe_le_coe_iff] + rw [abs_le] at hexp + have h1 : (P.discount : ℝ) + * (P.toExtended.expect v ((x, z), a) - P.toExtended.expect v ((y, z), b)) + ≤ P.discount * (L * |a - b|) := mul_le_mul_of_nonneg_left hexp.2 hβ + have h2 : (P.discount : ℝ) * (L * |a - b|) ≤ P.discount * (L * Δ) := + mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hgapa hL) hβ + nlinarith [hutil, h1, h2] + refine le_trans hstep ?_ + rw [EReal.coe_add] + exact add_le_add (P.toExtended.le_bellmanFn v hbmem) (le_refl _) + +/-- **The poorer state's value falls short by at most the swap cost.** The reverse swap needs +no utility bound at all: where it truncates, the richer household consumes strictly more, so +the utility term has the helpful sign. -/ +theorem bellmanFn_sub_le_of_lipschitz' {v : (ℝ × Z) →ᵇ ℝ} {L : ℝ} (hL : 0 ≤ L) + (hv : ∀ z : Z, ∀ p ∈ Icc assetFloor assetCap, ∀ q ∈ Icc assetFloor assetCap, + |v (p, z) - v (q, z)| ≤ L * |p - q|) + (z : Z) {x y : ℝ} (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : y ≤ x) : + P.toExtended.bellmanFn v (y, z) - P.toExtended.bellmanFn v (x, z) + ≤ P.discount * L * ((1 + P.interest) * (x - y)) := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set Δ : ℝ := (1 + P.interest) * (x - y) with hΔdef + have hΔ0 : 0 ≤ Δ := mul_nonneg P.interest_gt_neg_one.le (by linarith) + have hres : P.resources (x, z) = P.resources (y, z) + Δ := by + simp only [resources, hΔdef] + rw [max_eq_right hx.1, max_eq_right hy.1]; ring + rw [sub_le_iff_le_add'] + refine P.toExtended.bellmanFn_le v fun b hb => ?_ + rcases eq_or_ne (P.toExtended.reward ((y, z), b)) ⊥ with hbot | hne + · rw [ExtendedStochasticProgram.objectiveE, hbot, EReal.bot_add]; exact bot_le + have hcy : P.consumption (y, z) b ∈ P.dom := P.consumption_mem_dom_of_ne_bot hy hb hne + have hb0 : assetFloor ≤ b := hb.1 + have hbcap : b ≤ assetCap := le_trans hb.2 (P.maxSaving_le_assetCap _) + have hbR : b ≤ P.resources (y, z) := le_trans hb.2 (P.maxSaving_le_resources _) + set a : ℝ := min (b + Δ) (P.maxSaving (x, z)) with hadef + have ha0 : assetFloor ≤ a := le_min (by linarith) (P.assetFloor_le_maxSaving _) + have hamem : a ∈ P.toExtended.feasible (x, z) := ⟨ha0, min_le_right _ _⟩ + have hacap : a ≤ assetCap := le_trans (min_le_right _ _) (P.maxSaving_le_assetCap _) + -- consumption at the richer state is at least what the poorer household had + have hcx : P.consumption (y, z) b ≤ P.consumption (x, z) a := by + simp only [consumption, hadef] + rcases le_total (b + Δ) (P.maxSaving (x, z)) with h | h + · rw [min_eq_left h]; linarith [hres] + · rw [min_eq_right h] + have hms : P.maxSaving (x, z) = min assetCap (P.resources (x, z)) := P.maxSaving_eq _ + rcases le_total (P.resources (x, z)) assetCap with hcap | hcap + · -- no truncation is possible here + rw [hms, min_eq_right hcap] at h ⊢ + linarith [hres] + · rw [hms, min_eq_left hcap] at h ⊢ + linarith [hres] + have hcxmem : P.consumption (x, z) a ∈ P.dom := P.dom_upward hcy hcx + have hgapa : |b - a| ≤ Δ := by + have hlow : b - Δ ≤ a := by + rw [hadef] + refine le_min (by linarith) ?_ + have hmono : P.maxSaving (y, z) ≤ P.maxSaving (x, z) := by + rw [P.maxSaving_eq, P.maxSaving_eq] + exact min_le_min le_rfl (by linarith [hres]) + linarith [hb.2] + have hhigh : a ≤ b + Δ := min_le_left _ _ + rw [abs_le] + constructor <;> linarith + have hrx := P.reward_eq_coe_dom hx hamem hcxmem + have hry := P.reward_eq_coe_dom hy hb hcy + have hexp := P.abs_expect_sub_le_lipschitz hv z (a := b) (b := a) + ⟨hb0, hbcap⟩ ⟨ha0, hacap⟩ y x + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + have hstep : P.toExtended.objectiveE v (y, z) b + ≤ P.toExtended.objectiveE v (x, z) a + (((P.discount * L) * Δ : ℝ) : EReal) := by + simp only [ExtendedStochasticProgram.objectiveE, hrx, hry, hdx] + rw [← EReal.coe_add, add_assoc, ← EReal.coe_add, ← EReal.coe_add, EReal.coe_le_coe_iff] + rw [abs_le] at hexp + have hu' : P.u (P.consumption (y, z) b) ≤ P.u (P.consumption (x, z) a) := + P.monotoneOn_u_dom hcy hcxmem hcx + have h1 : (P.discount : ℝ) + * (P.toExtended.expect v ((y, z), b) - P.toExtended.expect v ((x, z), a)) + ≤ P.discount * (L * |b - a|) := mul_le_mul_of_nonneg_left hexp.2 hβ + have h2 : (P.discount : ℝ) * (L * |b - a|) ≤ P.discount * (L * Δ) := + mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hgapa hL) hβ + nlinarith [hu', h1, h2] + refine le_trans hstep ?_ + rw [EReal.coe_add] + exact add_le_add (P.toExtended.le_bellmanFn v hamem) (le_refl _) + +/-- **The Bellman operator preserves Lipschitz continuity in assets.** -/ +theorem abs_bellmanFn_sub_le_of_lipschitz {v : (ℝ × Z) →ᵇ ℝ} {L : ℝ} (hL : 0 ≤ L) + (hv : ∀ z : Z, ∀ p ∈ Icc assetFloor assetCap, ∀ q ∈ Icc assetFloor assetCap, + |v (p, z) - v (q, z)| ≤ L * |p - q|) + (hbig : (P.slopeBoundU + P.discount * L) * (1 + P.interest) ≤ L) + (z : Z) {x y : ℝ} (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) : + |P.toExtended.bellmanFn v (x, z) - P.toExtended.bellmanFn v (y, z)| ≤ L * |x - y| := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hK := P.slopeBoundU_nonneg + have hone : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have key : ∀ p q : ℝ, p ∈ Icc assetFloor assetCap → q ∈ Icc assetFloor assetCap → q ≤ p → + |P.toExtended.bellmanFn v (p, z) - P.toExtended.bellmanFn v (q, z)| ≤ L * |p - q| := by + intro p q hp hq hqp + have h1 := P.bellmanFn_sub_le_of_lipschitz hL hv z hp hq hqp + have h2 := P.bellmanFn_sub_le_of_lipschitz' hL hv z hp hq hqp + have hgap : (P.discount : ℝ) * L * ((1 + P.interest) * (p - q)) + ≤ (P.slopeBoundU + P.discount * L) * ((1 + P.interest) * (p - q)) := by + refine mul_le_mul_of_nonneg_right (by linarith) (by nlinarith) + have hfinal : (P.slopeBoundU + P.discount * L) * ((1 + P.interest) * (p - q)) + ≤ L * (p - q) := by + have := mul_le_mul_of_nonneg_right hbig (by linarith : (0 : ℝ) ≤ p - q) + nlinarith + rw [abs_le, abs_of_nonneg (by linarith : (0 : ℝ) ≤ p - q)] + constructor <;> linarith + rcases le_total y x with h | h + · exact key x y hx hy h + · rw [abs_sub_comm, abs_sub_comm x y]; exact key y x hy hx h + +/-- **The value function is Lipschitz in assets**, with an explicit constant. Combining the +preservation proof with the closed class. Any `L` with `(K + βL)(1+r) ≤ L` works, and such an +`L` exists exactly when `β(1+r) < 1`. -/ +theorem valueFunction_lipschitz {L : ℝ} (hL : 0 ≤ L) + (hbig : (P.slopeBoundU + P.discount * L) * (1 + P.interest) ≤ L) : + ∀ z : Z, ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| ≤ L * |x - y| := + P.toExtended.blackwell.forall_lipschitzOn_valueFunction P.toExtended.discount_lt_one hL + (g := fun (z : Z) (a : ℝ) => (a, z)) + (fun v hvv z x hx y hy => by + simpa only [ExtendedStochasticProgram.bellman_apply] using + P.abs_bellmanFn_sub_le_of_lipschitz hL (fun z' p hp q hq => hvv z' p hp q hq) hbig z hx hy) + +/-! ### The corner + +With the marginal value of wealth bounded above by `L`, saving nothing beats saving anything +as soon as `β L` falls below a lower bound on the marginal utility of consumption. Both sides +of that comparison are now explicit constants, and no derivative appears in either. -/ + +/-- **The corner condition, state by state.** The marginal-utility bound is required only up +to THIS state's resources, so the threshold is local: a state with few resources has high +marginal utility and the constraint binds there, while a richer state may fail the test. + +That is what lets the borrowing constraint bind at a bad income draw without binding +everywhere, which the global version could not express. -/ +theorem policy_eq_zero_of_corner_at {L m : ℝ} + (hlip : ∀ z : Z, ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| ≤ L * |x - y|) + {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.resources s - assetFloor → + m * (c - d) ≤ P.u c - P.u d) + (hcond : P.discount * L < m) : + P.policy s = assetFloor := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set V := P.toExtended.valueFunction with hV + have hmem0 : assetFloor ∈ P.toExtended.feasible s := ⟨le_rfl, le_max_left _ _⟩ + have hc0 : 0 < P.consumption s assetFloor := by + have := P.minConsumption_le_consumption_floor s + have := P.minConsumption_pos + simp only [consumption]; linarith + have hrw0 := P.reward_eq_coe hs hmem0 hc0 + have hRmax : P.resources s - assetFloor ≤ P.maxConsumption := + P.consumption_le_maxConsumption hs (le_refl assetFloor) + -- nothing feasible beats borrowing to the limit + have hdom : ∀ a ∈ P.toExtended.feasible s, + P.toExtended.objectiveE V s a + ≤ ((P.u (P.consumption s assetFloor) + + P.discount * P.toExtended.expect V (s, assetFloor) : ℝ) : EReal) := by + intro a ha + rcases eq_or_ne (P.toExtended.reward (s, a)) ⊥ with hbot | hne + · rw [ExtendedStochasticProgram.objectiveE, hbot, EReal.bot_add]; exact bot_le + have hca : P.consumption s a ∈ P.dom := P.consumption_mem_dom_of_ne_bot hs ha hne + have ha0 : assetFloor ≤ a := ha.1 + have hacap : a ≤ assetCap := le_trans ha.2 (P.maxSaving_le_assetCap _) + have hrwa := P.reward_eq_coe_dom hs ha hca + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + -- the utility gain from consuming instead of saving + have hu : m * (a - assetFloor) + ≤ P.u (P.consumption s assetFloor) - P.u (P.consumption s a) := by + have hcc : P.consumption s a ≤ P.consumption s assetFloor := by + simp only [consumption]; linarith + have := hmarg (P.consumption s assetFloor) (P.consumption s a) hca hcc + (by simp only [consumption]; exact le_rfl) + have hdiff : P.consumption s assetFloor - P.consumption s a = a - assetFloor := by + simp only [consumption]; ring + rwa [hdiff] at this + -- the continuation loss from not saving + have hexp := P.abs_expect_sub_le_lipschitz hlip s.2 (a := a) (b := assetFloor) + ⟨ha0, hacap⟩ ⟨le_rfl, P.assetFloor_le_assetCap⟩ s.1 s.1 + rw [abs_le] at hexp + simp only [ExtendedStochasticProgram.objectiveE, hrwa, hdx, ← EReal.coe_add, + EReal.coe_le_coe_iff] + have hprod : (P.discount : ℝ) * (P.toExtended.expect V ((s.1, s.2), a) + - P.toExtended.expect V ((s.1, s.2), assetFloor)) + ≤ P.discount * (L * |a - assetFloor|) := + mul_le_mul_of_nonneg_left hexp.2 hβ + rw [abs_of_nonneg (by linarith : (0:ℝ) ≤ a - assetFloor)] at hprod + have hpp : (P.discount : ℝ) * (L * (a - assetFloor)) ≤ m * (a - assetFloor) := by + rcases eq_or_lt_of_le ha0 with h | h + · rw [← h]; simp + · nlinarith + have hfix : ∀ c : ℝ, P.toExtended.expect V ((s.1, s.2), c) + = P.toExtended.expect V (s, c) := fun _ => rfl + rw [hfix, hfix] at hprod + linarith + -- so borrowing to the limit attains the maximum, and the maximiser is unique + have hle : P.toExtended.bellmanFn V s + ≤ P.u (P.consumption s assetFloor) + + P.discount * P.toExtended.expect V (s, assetFloor) := + P.toExtended.bellmanFn_le V hdom + have hdx : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + have hge : ((P.u (P.consumption s assetFloor) + + P.discount * P.toExtended.expect V (s, assetFloor) : ℝ) : EReal) + ≤ ((P.toExtended.bellmanFn V s : ℝ) : EReal) := by + have h := P.toExtended.le_bellmanFn V hmem0 + rwa [ExtendedStochasticProgram.objectiveE, hrw0, hdx, ← EReal.coe_add] at h + rw [EReal.coe_le_coe_iff] at hge + refine (P.eq_policy_of_optimal hs hmem0 ?_).symm + rw [ExtendedStochasticProgram.objectiveE, hrw0, hdx, ← EReal.coe_add, EReal.coe_eq_coe_iff] + linarith + +/-- The global corner condition, as a corollary. -/ +theorem policy_eq_zero_of_corner {L m : ℝ} + (hlip : ∀ z : Z, ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| ≤ L * |x - y|) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + (hcond : P.discount * L < m) {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) : + P.policy s = assetFloor := + P.policy_eq_zero_of_corner_at hlip hs + (fun c d hd hdc hc => hmarg c d hd hdc + (le_trans hc (P.consumption_le_maxConsumption hs (le_refl assetFloor)))) + hcond + +/-- For `u c = -1/c`, the marginal-utility bound up to `R` is `1/R²`. So the state-dependent +threshold reads `β L < 1 / resources²`: the constraint binds exactly where resources are small +enough, and `resources = income + (1+r)·assets` rises with BOTH the income draw and the asset +position. -/ +theorem crra_marginal {R : ℝ} (hR : 0 < R) (c d : ℝ) (hd : 0 < d) (hdc : d ≤ c) (hcR : c ≤ R) : + (1 / R ^ 2) * (c - d) ≤ (-c⁻¹) - (-d⁻¹) := by + have hc : 0 < c := lt_of_lt_of_le hd hdc + have hkey : -c⁻¹ - -d⁻¹ = (c - d) / (c * d) := by field_simp; ring + rw [show (1 : ℝ) / R ^ 2 * (c - d) = (c - d) / R ^ 2 by ring, hkey, + div_le_div_iff₀ (by positivity) (by positivity)] + nlinarith [mul_nonneg (sub_nonneg.mpr hdc) (sub_nonneg.mpr (show c * d ≤ R ^ 2 by nlinarith))] + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean new file mode 100644 index 000000000..6f1d071f4 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -0,0 +1,159 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation +public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements + + +/-! +# The optimal policy is increasing in assets + +A household that starts richer saves at least as much, holding the income state fixed. + +The argument is the classical monotone-comparative-statics one, but it needs neither Topkis +nor any derivative. Two observations do all the work. + +First, the continuation value `β E[V(a', z')]` depends on the chosen saving `a'` and on the +*current* income state, but not on current assets. So when comparing two asset levels with +the same income state, the continuation terms cancel, and increasing differences of the +objective reduces to increasing differences of `u` composed with the budget — which is +exactly `ConcaveOn.sub_le_sub_of_shift`, concavity of `u`. + +Second, the usual Topkis conclusion (the argmax is an increasing *correspondence*) is not +needed, because uniqueness of the optimal action is already proved. If the richer household +saved strictly less, the exchange argument shows the poorer household's choice is *also* +optimal for the richer one, and uniqueness collapses the two. + +The feasible set grows with assets, which is what makes both exchanges legal. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### The budget grows with assets -/ + +theorem resources_mono {a a' : ℝ} {z : Z} (h : a ≤ a') : + P.resources (a, z) ≤ P.resources (a', z) := by + have hm : max assetFloor a ≤ max assetFloor a' := max_le_max le_rfl h + have hi := P.interest_gt_neg_one + simp only [resources] + nlinarith + +theorem maxSaving_mono {a a' : ℝ} {z : Z} (h : a ≤ a') : + P.maxSaving (a, z) ≤ P.maxSaving (a', z) := + max_le_max le_rfl (min_le_min le_rfl (P.resources_mono h)) + +theorem feasible_mono {a a' : ℝ} {z : Z} (h : a ≤ a') : + P.toExtended.feasible (a, z) ⊆ P.toExtended.feasible (a', z) := by + simp only [P.feasible_eq] + exact Icc_subset_Icc le_rfl (P.maxSaving_mono h) + +/-! ### The objective in the reals -/ + +/-- The continuation value of saving `x` when the current income state is `z`. It does not +depend on current assets, which is what makes the comparison below work. -/ +noncomputable def cont (z : Z) (x : ℝ) : ℝ := + ∑ z', P.transitionMatrix z z' * P.toExtended.valueFunction (x, z') + +/-- The objective, as a real number. Valid wherever consumption is positive. -/ +noncomputable def objR (s : ℝ × Z) (x : ℝ) : ℝ := + P.u (P.consumption s x) + P.discount * P.cont s.2 x + +theorem objectiveE_eq_coe {s : ℝ × Z} {x : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (hx : x ∈ P.toExtended.feasible s) (hc : P.consumption s x ∈ P.dom) : + P.toExtended.objectiveE P.toExtended.valueFunction s x = ((P.objR s x : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs hx hc, ← EReal.coe_add] + rfl + +/-- Any feasible action with positive consumption is worth at most the optimum. -/ +theorem objR_le_of_mem {s : ℝ × Z} {x : ℝ} (hs : s.1 ∈ Icc assetFloor assetCap) + (hx : x ∈ P.toExtended.feasible s) (hc : P.consumption s x ∈ P.dom) : + P.objR s x ≤ P.objR s (P.policy s) := by + have hpol : ((P.objR s (P.policy s) : ℝ) : EReal) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal) := by + rw [← P.objectiveE_eq_coe hs (P.policy_mem s) (P.consumption_policy_mem_dom hs)] + exact P.policy_optimal s + have h1 := P.toExtended.le_bellmanFn P.toExtended.valueFunction hx + rw [P.objectiveE_eq_coe hs hx hc, ← hpol] at h1 + exact_mod_cast h1 + +/-! ### Monotonicity -/ + +/-- **The optimal policy is increasing in assets.** Holding the income state fixed, a +household with more assets saves at least as much. -/ +theorem policy_mono {a a' : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) + (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.policy (a, z) ≤ P.policy (a', z) := by + by_contra hcon + rw [not_le] at hcon + have hR : P.resources (a, z) ≤ P.resources (a', z) := P.resources_mono hle + -- both exchanges are feasible + have hys : P.policy (a, z) ∈ P.toExtended.feasible (a, z) := P.policy_mem _ + have hys' : P.policy (a, z) ∈ P.toExtended.feasible (a', z) := P.feasible_mono hle hys + have hy's' : P.policy (a', z) ∈ P.toExtended.feasible (a', z) := P.policy_mem _ + have hy's : P.policy (a', z) ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] at hys ⊢ + rw [P.feasible_eq] at hy's' + exact ⟨hy's'.1, le_trans hcon.le hys.2⟩ + -- all four consumptions are positive + have hcsy : P.consumption (a, z) (P.policy (a, z)) ∈ P.dom := P.consumption_policy_mem_dom ha + have hcs'y' : P.consumption (a', z) (P.policy (a', z)) ∈ P.dom := + P.consumption_policy_mem_dom ha' + have hcsy' : P.consumption (a, z) (P.policy (a', z)) ∈ P.dom := by + refine P.dom_upward hcsy ?_ + simp only [consumption]; linarith + have hcs'y : P.consumption (a', z) (P.policy (a, z)) ∈ P.dom := by + refine P.dom_upward hcsy ?_ + simp only [consumption]; linarith + -- increasing differences, from concavity of u alone + have hshift := P.strictConcaveOn_u_dom.concaveOn.sub_le_sub_of_shift + (c₁ := P.resources (a, z) - P.policy (a, z)) + (c₂ := P.resources (a, z) - P.policy (a', z)) + (Δ := P.resources (a', z) - P.resources (a, z)) + (by simpa only [consumption] using hcsy) + (by + have : P.resources (a, z) - P.policy (a', z) + + (P.resources (a', z) - P.resources (a, z)) + = P.consumption (a', z) (P.policy (a', z)) := by simp only [consumption]; ring + rw [this]; exact hcs'y') + (by linarith) (by linarith) + -- the poorer household's choice beats its rival at its own state + have hopt_s := P.objR_le_of_mem ha hy's hcsy' + -- so it also beats it at the richer state + have hge : P.objR (a', z) (P.policy (a', z)) ≤ P.objR (a', z) (P.policy (a, z)) := by + simp only [objR, consumption] at hopt_s ⊢ + have e₁ : P.resources (a, z) - P.policy (a', z) + + (P.resources (a', z) - P.resources (a, z)) + = P.resources (a', z) - P.policy (a', z) := by ring + have e₂ : P.resources (a, z) - P.policy (a, z) + + (P.resources (a', z) - P.resources (a, z)) + = P.resources (a', z) - P.policy (a, z) := by ring + rw [e₁, e₂] at hshift + linarith + -- hence the poorer household's choice is optimal at the richer state too + have hopt' : P.objR (a', z) (P.policy (a, z)) = P.objR (a', z) (P.policy (a', z)) := + le_antisymm (P.objR_le_of_mem ha' hys' hcs'y) hge + have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z) (P.policy (a, z)) + = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe ha' hys' hcs'y, hopt', + ← P.objectiveE_eq_coe ha' hy's' hcs'y'] + exact P.policy_optimal _ + -- uniqueness collapses the two, contradicting the strict inequality + exact absurd (P.eq_policy_of_optimal ha' hys' hbell) (by linarith) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean new file mode 100644 index 000000000..2fa6ae8a7 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean @@ -0,0 +1,335 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRegion +public import LeanPool.AiyagariEquilibrium.Distribution.Feller + + +/-! +# Varying the interest rate + +The cross-rate estimates so far take two programmes plus a hypothesis for each field they +must share -- income, maximum income, minimum income, the transition matrix, the discount +factor, the utility function. Six hypotheses is a signal that the wrong object is being +quantified over: in the comparative statics only the INTEREST RATE varies. + +`withRate` makes that structural. Every field except `interest` is inherited, so every +sharing hypothesis becomes `rfl` and disappears from the statements, and +`fun r => P.withRate r _` is the family the equilibrium theorem wants. + +The other content here is the step that turns the two argument gaps -- in consumption and in +the continuation value -- into a comparison of objectives. It is stated with the gaps as +hypotheses, so the analytic work (uniform continuity) and the arithmetic (extended reals) +stay separate. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The same household problem at a different interest rate. -/ +def withRate (r : ℝ) (hr : P.RateOK r) : IncomeFluctuation Z assetFloor assetCap := + { P with interest := r, interest_gt_neg_one := hr.1, minConsumption_le_floor := hr.2 } + +@[simp] theorem withRate_interest (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).interest = r := rfl +@[simp] theorem withRate_income (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).income = P.income := rfl +@[simp] theorem withRate_maxIncome (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).maxIncome = P.maxIncome := rfl +@[simp] theorem withRate_minIncome (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).minIncome = P.minIncome := rfl +@[simp] theorem withRate_minConsumption (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).minConsumption = P.minConsumption := rfl +@[simp] theorem withRate_u (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).u = P.u := rfl +@[simp] theorem withRate_discount (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).discount = P.discount := rfl +@[simp] theorem withRate_transitionMatrix (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).transitionMatrix = P.transitionMatrix := rfl +@[simp] theorem withRate_region (r : ℝ) (hr : P.RateOK r) : + (P.withRate r hr).region = P.region := rfl +@[simp] theorem withRate_loBound (r : ℝ) (hr : P.RateOK r) (v : (ℝ × Z) →ᵇ ℝ) : + (P.withRate r hr).toExtended.loBound v = P.toExtended.loBound v := rfl + +/-- **From argument gaps to an objective comparison.** + +The reward is real at both actions because consumption is positive at both -- which is what +the cutoff delivers -- so the extended-real objective collapses to a real one and the +comparison is ordinary arithmetic. Keeping the two gaps as hypotheses separates the analytic +work from the arithmetic. -/ +theorem objectiveE_le_of_gaps (v : (ℝ × Z) →ᵇ ℝ) {r r' : ℝ} (hr : P.RateOK r) (hr' : P.RateOK r') + {s : ℝ × Z} (hs : s ∈ P.region) {a a' : ℝ} + (ha : a ∈ (P.withRate r hr).toExtended.feasible s) + (ha' : a' ∈ (P.withRate r' hr').toExtended.feasible s) + (hcA : 0 < (P.withRate r hr).consumption s a) + (hcB : 0 < (P.withRate r' hr').consumption s a') + {e₁ e₂ : ℝ} + (hu : P.u ((P.withRate r hr).consumption s a) + - P.u ((P.withRate r' hr').consumption s a') ≤ e₁) + (hv : (P.withRate r hr).toExtended.expect v (s, a) + - (P.withRate r' hr').toExtended.expect v (s, a') ≤ e₂) : + (P.withRate r hr).toExtended.objectiveE v s a + ≤ (P.withRate r' hr').toExtended.objectiveE v s a' + + ((e₁ + P.discount * e₂ : ℝ) : EReal) := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hrwA := (P.withRate r hr).reward_eq_coe hs ha hcA + have hrwB := (P.withRate r' hr').reward_eq_coe hs ha' hcB + have hdA : ((P.withRate r hr).toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + have hdB : ((P.withRate r' hr').toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + simp only [ExtendedStochasticProgram.objectiveE, hrwA, hrwB, hdA, hdB] + rw [← EReal.coe_add, add_assoc, ← EReal.coe_add, ← EReal.coe_add, EReal.coe_le_coe_iff] + have hu' : (P.withRate r hr).u = P.u := rfl + have hu'' : (P.withRate r' hr').u = P.u := rfl + rw [hu', hu''] at * + nlinarith [hu, hv, hβ] + +/-! ### The sup-norm estimate + +Everything assembled. Choosing the rate gap small enough makes the Bellman operator move by +less than any prescribed amount, uniformly over the region. + +Three separate smallness requirements are imposed on the rate gap, and the `+ 1` in each +constant is only to keep the divisions safe when the asset cap is zero: + +* the saving gap must fall under the continuation value's modulus, +* the consumption gap must fall under utility's modulus, +* the consumption gap must be at most `δ/2`, so that consumption at the SECOND rate stays + positive given that the cutoff bounds it at the first. Without this the reward at the + second rate could be `⊥` and the comparison would be vacuous. + +Actions below the cutoff need no comparison at all: they are already worth less than +`loBound v`, which is itself below the other operator's value. -/ + +theorem exists_rate_modulus (hd : P.Unbounded) (v : (ℝ × Z) →ᵇ ℝ) {rlo rhi : ℝ} + (hrlo : 0 < 1 + rlo) + {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (r r' : ℝ) (hr : P.RateOK r) (hr' : P.RateOK r'), + rlo ≤ r → r ≤ rhi → rlo ≤ r' → r' ≤ rhi → |r - r'| < η → + ∀ s ∈ P.region, + (P.withRate r hr).toExtended.bellmanFn v s + ≤ (P.withRate r' hr').toExtended.bellmanFn v s + ε := by + classical + have hcap := P.assetFloor_le_assetCap + have hmaxI : 0 ≤ P.maxIncome := le_trans P.minIncome_pos.le P.minIncome_le_maxIncome + obtain ⟨δ, hδ, hδspec⟩ := P.exists_cutoff hd v + have hB : 0 ≤ P.stateBoundCross rlo := P.stateBoundCross_nonneg hrlo + set K₁ : ℝ := P.stateBoundCross rlo + 1 with hK₁def + set K₂ : ℝ := 2 * P.stateBoundCross rlo + 1 with hK₂def + have hK₁ : 1 ≤ K₁ := by rw [hK₁def]; linarith + have hK₂ : 1 ≤ K₂ := by rw [hK₂def]; linarith + obtain ⟨η₁, hη₁, hmu⟩ := + P.exists_modulus_u (lo := δ / 2) (hi := P.maxIncome + (1 + rhi) * assetCap - assetFloor) + (by linarith) (half_pos hε) + obtain ⟨η₂, hη₂, hmv⟩ := exists_modulus_v (assetCap := assetCap) v (half_pos hε) + refine ⟨min (min (η₂ / K₁) (η₁ / K₂)) (δ / (2 * K₂)), by positivity, ?_⟩ + intro r r' hr hr' hlo hhi hlo' hhi' hclose s hs + set A := P.withRate r hr with hA + set B := P.withRate r' hr' with hB + refine A.toExtended.bellmanFn_le v fun a ha => ?_ + -- actions below the cutoff need no comparison + rcases le_or_gt ((P.toExtended.loBound v : ℝ) : EReal) + (A.toExtended.objectiveE v s a) with hcut | hbelow + · -- the matched action at the other rate + have hafe := ha + rw [A.feasible_eq_image] at hafe + obtain ⟨θ, hθ, rfl⟩ := hafe + set a' : ℝ := (assetFloor + θ * B.savingRoom s) with ha'def + have ha' : a' ∈ B.toExtended.feasible s := by rw [B.feasible_eq_image]; exact ⟨θ, hθ, rfl⟩ + -- consumption at the first rate is bounded below by the cutoff + have hcA : δ ≤ A.consumption s ((assetFloor + θ * A.savingRoom s)) := + A.le_consumption_of_cutoff v hδspec hs ha hcut + -- the two consumptions are close, so the second is positive too + have hgapc : |A.consumption s ((assetFloor + θ * A.savingRoom s)) - B.consumption s a'| + ≤ |r - r'| * K₂ := by + have hclamp := abs_clamped_consumption_sub_le (P := A) (Q := B) rfl rfl hrlo hlo hlo' hθ s + have hA' : min A.maxConsumption (A.consumption s ((assetFloor + θ * A.savingRoom s))) + = A.consumption s ((assetFloor + θ * A.savingRoom s)) := + min_eq_right (A.consumption_le_maxConsumption hs + (le_add_of_nonneg_right (mul_nonneg hθ.1 (A.savingRoom_nonneg s)))) + have hB' : min B.maxConsumption (B.consumption s a') = B.consumption s a' := + min_eq_right (B.consumption_le_maxConsumption hs + (le_add_of_nonneg_right (mul_nonneg hθ.1 (B.savingRoom_nonneg s)))) + rw [hA', hB'] at hclamp + rw [show A.stateBoundCross rlo = P.stateBoundCross rlo from rfl] at hclamp + refine le_trans hclamp ?_ + have hint : |A.interest - B.interest| = |r - r'| := rfl + rw [hint] + refine mul_le_mul_of_nonneg_left ?_ (abs_nonneg _) + rw [hK₂def] + linarith + have hsmall2 : |r - r'| * K₂ ≤ δ / 2 := by + have h := lt_of_lt_of_le hclose (min_le_right _ _) + rw [lt_div_iff₀ (by linarith)] at h + nlinarith [h] + have hcB : δ / 2 ≤ B.consumption s a' := by + rw [abs_le] at hgapc; linarith [hgapc.1, hgapc.2] + -- utility moves little + have hupper : ∀ {X : IncomeFluctuation Z assetFloor assetCap} {b : ℝ}, + X.consumption s b ≤ X.maxConsumption → X.maxIncome = P.maxIncome → + X.interest ≤ rhi → + X.consumption s b ≤ P.maxIncome + (1 + rhi) * assetCap - assetFloor := by + intro X b hb hmi hint + have : X.maxConsumption = X.maxIncome + (1 + X.interest) * assetCap - assetFloor := rfl + rw [this, hmi] at hb + nlinarith [P.assetCap_nonneg] + have hgu : |P.u (A.consumption s ((assetFloor + θ * A.savingRoom s))) + - P.u (B.consumption s a')| < ε / 2 := by + have hbA := A.consumption_le_maxConsumption hs + (le_add_of_nonneg_right (mul_nonneg hθ.1 (A.savingRoom_nonneg s))) + have hbB := B.consumption_le_maxConsumption hs + (le_add_of_nonneg_right (mul_nonneg hθ.1 (B.savingRoom_nonneg s))) + refine hmu _ ⟨by linarith, hupper hbA rfl hhi⟩ _ ⟨by linarith, hupper hbB rfl hhi'⟩ ?_ + have h := lt_of_lt_of_le (lt_of_lt_of_le hclose (min_le_left _ _)) (min_le_right _ _) + rw [lt_div_iff₀ (by linarith)] at h + rw [abs_le] at hgapc + rw [abs_lt]; constructor <;> linarith [hgapc.1, hgapc.2] + -- the continuation value moves little + have hgapa : |(assetFloor + θ * A.savingRoom s) - a'| < η₂ := by + have hms := abs_maxSaving_sub_le (P := A) (Q := B) rfl rfl hrlo hlo hlo' s + have hint : |A.interest - B.interest| = |r - r'| := rfl + rw [hint] at hms + have h := lt_of_lt_of_le (lt_of_lt_of_le hclose (min_le_left _ _)) (min_le_left _ _) + rw [lt_div_iff₀ (by linarith)] at h + rw [ha'def, show assetFloor + θ * A.savingRoom s - (assetFloor + θ * B.savingRoom s) + = θ * (A.maxSaving s - B.maxSaving s) by simp only [savingRoom]; ring, + abs_mul, abs_of_nonneg hθ.1] + calc θ * |A.maxSaving s - B.maxSaving s| ≤ 1 * (|r - r'| * P.stateBoundCross rlo) := by + refine mul_le_mul hθ.2 hms (abs_nonneg _) (by norm_num) + _ ≤ |r - r'| * K₁ := by rw [one_mul, hK₁def]; nlinarith [abs_nonneg (r - r')] + _ < η₂ := h + have hgv : |A.toExtended.expect v (s, (assetFloor + θ * A.savingRoom s)) + - B.toExtended.expect v (s, a')| ≤ ε / 2 := by + have hmem : ∀ x : ℝ, x ∈ A.toExtended.feasible s → x ∈ Icc assetFloor assetCap := fun x hx => + A.feasible_subset_region hx + have h1 : (assetFloor + θ * A.savingRoom s) ∈ Icc assetFloor assetCap := hmem _ ha + have h2 : a' ∈ Icc assetFloor assetCap := B.feasible_subset_region ha' + have hpA : ∀ z' : Z, A.toExtended.prob z' (s, (assetFloor + θ * A.savingRoom s)) + = P.transitionMatrix s.2 z' := fun _ => rfl + have hpB : ∀ z' : Z, B.toExtended.prob z' (s, a') = P.transitionMatrix s.2 z' := fun _ => rfl + have htA : ∀ z' : Z, A.toExtended.transition z' (s, (assetFloor + θ * A.savingRoom s)) + = ((assetFloor + θ * A.savingRoom s), z') := fun _ => rfl + have htB : ∀ z' : Z, B.toExtended.transition z' (s, a') = (a', z') := fun _ => rfl + simp only [ExtendedStochasticProgram.expect, hpA, hpB, htA, htB, ← Finset.sum_sub_distrib, + ← mul_sub] + calc |∑ z' : Z, P.transitionMatrix s.2 z' + * (v ((assetFloor + θ * A.savingRoom s), z') - v (a', z'))| + ≤ ∑ z' : Z, |P.transitionMatrix s.2 z' + * (v ((assetFloor + θ * A.savingRoom s), z') - v (a', z'))| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z' : Z, P.transitionMatrix s.2 z' * (ε / 2) := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg s.2 z')] + exact mul_le_mul_of_nonneg_left (hmv z' _ h1 _ h2 hgapa).le + (P.transitionMatrix_nonneg s.2 z') + _ = ε / 2 := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + -- combine + have hobj := P.objectiveE_le_of_gaps v hr hr' hs ha ha' + (by linarith) (by linarith) + (e₁ := ε / 2) (e₂ := ε / 2) + (by rw [abs_lt] at hgu; linarith [hgu.2]) + (by rw [abs_le] at hgv; linarith [hgv.2]) + refine le_trans hobj ?_ + have hbB := B.toExtended.le_bellmanFn v ha' + have hβ1 : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + have hβ0 : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + calc B.toExtended.objectiveE v s a' + ((ε / 2 + P.discount * (ε / 2) : ℝ) : EReal) + ≤ ((B.toExtended.bellmanFn v s : ℝ) : EReal) + + ((ε / 2 + P.discount * (ε / 2) : ℝ) : EReal) := by gcongr + _ ≤ ((B.toExtended.bellmanFn v s + ε : ℝ) : EReal) := by + rw [← EReal.coe_add, EReal.coe_le_coe_iff]; nlinarith + · -- below the cutoff: already worth less than the other operator's value + refine le_trans hbelow.le ?_ + rw [EReal.coe_le_coe_iff] + have := (B.toExtended.bellmanFn_bounds v s).1 + have hlb : B.toExtended.loBound v = P.toExtended.loBound v := rfl + rw [hlb] at this + linarith + +/-- **The value function moves continuously with the interest rate, on the region.** + +Stated AT a reference rate rather than between two arbitrary rates, and that is not a +weakening -- it is what makes the argument work. The modulus produced by +`exists_rate_modulus` depends on the continuation value fed to the operator, so comparing two +moving rates would need the family of value functions to be equicontinuous. Fixing the +reference rate makes the continuation value a single fixed function, and continuity at every +point is continuity. -/ +theorem exists_valueFunction_modulus (hd : P.Unbounded) {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) + {r₀ : ℝ} + (hr₀ : P.RateOK r₀) (hlo₀ : rlo ≤ r₀) (hhi₀ : r₀ ≤ rhi) {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ (r : ℝ) (hr : P.RateOK r), rlo ≤ r → r ≤ rhi → |r - r₀| < η → + ∀ s ∈ P.region, + |(P.withRate r hr).toExtended.valueFunction s + - (P.withRate r₀ hr₀).toExtended.valueFunction s| ≤ ε := by + have hβ1 : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + set V₀ := (P.withRate r₀ hr₀).toExtended.valueFunction with hV₀ + obtain ⟨η, hη, hmod⟩ := + P.exists_rate_modulus hd V₀ (rlo := rlo) (rhi := rhi) hrlo (ε := ε * (1 - P.discount)) + (by positivity) + refine ⟨η, hη, fun r hr hlo hhi hclose s hs => ?_⟩ + -- the operator gap at the fixed continuation value, both directions + have hfix : ∀ t : ℝ × Z, (P.withRate r₀ hr₀).toExtended.bellmanFn V₀ t = V₀ t := fun t => by + rw [hV₀, ← (P.withRate r₀ hr₀).toExtended.bellman_apply, + (P.withRate r₀ hr₀).toExtended.bellman_valueFunction] + have hD : ∀ t : ℝ × Z, t ∈ (P.withRate r hr).region → + |(P.withRate r hr).toExtended.bellmanFn V₀ t - V₀ t| ≤ ε * (1 - P.discount) := by + intro t ht + have h1 := hmod r r₀ hr hr₀ hlo hhi hlo₀ hhi₀ hclose t ht + have h2 := hmod r₀ r hr₀ hr hlo₀ hhi₀ hlo hhi (by rwa [abs_sub_comm]) t ht + rw [hfix t] at h1 h2 + rw [abs_le] + constructor <;> linarith + -- and the bridge turns it into a bound on the value functions + have hbr := abs_valueFunction_sub_le_region (P := P.withRate r hr) (Q := P.withRate r₀ hr₀) + hD hs + have hdisc : ((P.withRate r hr).discount : ℝ) = (P.discount : ℝ) := rfl + rw [hdisc] at hbr + refine le_trans hbr ?_ + rw [div_le_iff₀ (by linarith)] + +/-! ### What policy continuity is for + +The distribution side consumes the policy only through the Markov operator, and only through +its SUP norm: the closed-graph argument for `r ↦ μ r` needs + + ‖markovOp_r h - markovOp_r₀ h‖ → 0 + +because `∫ markovOp_r h dμ_r` splits into `∫ (markovOp_r h - markovOp_r₀ h) dμ_r`, bounded by +that norm, plus a term weak convergence handles. Pointwise convergence of the policy would +not do. + +The lemma below is the interface: it reduces the operator gap to the gap in the SUCCESSOR +STATES, which is where the policy enters. Combined with uniform continuity of `h` on the +compact state space, uniform convergence of the policy is exactly what is needed, and +nothing more. -/ + +theorem abs_markovFn_sub_le {A B : IncomeFluctuation Z assetFloor assetCap} + (hAB : A.transitionMatrix = B.transitionMatrix) (h : A.State →ᵇ ℝ) {ε : ℝ} + (hclose : ∀ s : A.State, ∀ z' : Z, |h (A.nextState s z') - h (B.nextState s z')| ≤ ε) + (s : A.State) : |A.markovFn h s - B.markovFn h s| ≤ ε := by + have hprob : ∀ z' : Z, B.prob s z' = A.prob s z' := fun z' => by + simp only [IncomeFluctuation.prob, hAB] + simp only [IncomeFluctuation.markovFn, hprob, ← Finset.sum_sub_distrib, ← mul_sub] + calc |∑ z' : Z, A.prob s z' * (h (A.nextState s z') - h (B.nextState s z'))| + ≤ ∑ z' : Z, |A.prob s z' * (h (A.nextState s z') - h (B.nextState s z'))| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z' : Z, A.prob s z' * ε := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (A.prob_nonneg s z')] + exact mul_le_mul_of_nonneg_left (hclose s z') (A.prob_nonneg s z') + _ = ε := by rw [← Finset.sum_mul, A.prob_sum, one_mul] + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean new file mode 100644 index 000000000..b8dcd595a --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean @@ -0,0 +1,209 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate + + +/-! +# Light (2018) Theorem 1: saving rises with the interest rate + +This is the last link in the uniqueness chain. `CapitalSupplyMonotone` turns a uniformly larger +policy into larger aggregate capital (Theorem 2), and `Equilibrium.Uniqueness` turns monotone +capital supply into a unique equilibrium rate (Theorem 3). What is still needed is that the +policy IS uniformly larger at the higher rate, which is Theorem 1. + +## The comparison is at a rescaled asset level + +Light's device is that two problems at different rates share a budget set. Cash on hand at +`(a, R₁)` is `y + R₁ a`, and at `(t a, R₂)` with `t = R₁ / R₂` it is `y + R₂ t a = y + R₁ a` — +the same. The saving cap is the same too, being `min assetCap (cash on hand)`. So the two +maximisations run over the SAME interval with the SAME current-consumption map, and differ only +in the continuation. + +That reduces the comparison to single crossing, and single crossing here is derivative-free. +`policy_le_of_cont_increasingDifferences` is the exchange argument of `policy_mono` run in the +other direction: there, one objective at two states; here, two objectives at one state. The +hypothesis is that the continuation has INCREASING DIFFERENCES across the two rates — the +integrated form of Light's `f'(·, R₂) ≥ f'(·, R₁)`, needing no derivative and closed under +pointwise limits. + +The theorem then chains + + `g(a, R₁) ≤ g(t a, R₂) ≤ g(a, R₂)`, + +the first step single crossing and the second `policy_mono`, since `t ≤ 1`. + +## The hypothesis `hid`, and where it is discharged + +The hypothesis `hid` is the property Light propagates through the operator, and propagating it +is his step 5. That step is not derivative-free and cannot be made so: it runs on the envelope +condition `(Tf)'(a, R) = R u'(σ_f(a, R))` as an EQUALITY, and the derivative-free substitute is a +sandwich between two utility increments which is strictly loose. The Clausen–Strub lemma in +`Analysis.DifferentiableSandwich` is exactly the statement that the sandwich closes only in the +derivative limit. + +It is now PROVED, in `IncomeFluctuationRateStep`: `contIncreasingDifferences_withRate`. The +envelope equality it needs is `hasDerivAt_bellman`, Clausen–Strub's lazy agent, and the other +input is the Carroll–Kimball concavity of the consumption function. Theorem 1 with `hid` +discharged is `policy_mono_withRate'`. + +Worth recording precisely: his step 5 applies Lemma 3 to `σ_f(·, R₂)` and needs it CONCAVE. That +is Light's Lemma 4, which is the Carroll and Kimball theorem — the hypothesis `hT` of +`IncomeFluctuationCarrollKimball`. So the residual hypothesis here and the residual hypothesis +there are not two gaps but one: concavity of the consumption function, which by Toda (2021) is +unavailable outside HARA. Closing it closes both. + +The pivotal inequality that step 5 turns on, `mul_marginal_le_of_scale`, is already proved. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +-- Light's rescaling `a ↦ ((1+r₁)/(1+r₂))·a` maps the asset region into itself only when the +-- borrowing limit is at zero (or non-positive), so this file is stated at a zero limit. +variable {assetCap : ℝ} +variable (P Q : IncomeFluctuation Z 0 assetCap) + +/-- **Increasing differences of the continuation across two rates**: the integrated form of +Light's `f'(·, R₂) ≥ f'(·, R₁)`. No derivative, and closed under pointwise limits. -/ +def ContIncreasingDifferences : Prop := + ∀ (z : Z) (x y : ℝ), x ∈ Icc (0 : ℝ) assetCap → y ∈ Icc (0 : ℝ) assetCap → x ≤ y → + P.cont z y - P.cont z x ≤ Q.cont z y - Q.cont z x + +/-- It suffices to have increasing differences slice by slice in the value function, when the +two economies share an income process. -/ +theorem contIncreasingDifferences_of_valueFunction + (hP : P.transitionMatrix = Q.transitionMatrix) + (hv : ∀ (z : Z) (x y : ℝ), x ∈ Icc (0 : ℝ) assetCap → y ∈ Icc (0 : ℝ) assetCap → x ≤ y → + P.toExtended.valueFunction (y, z) - P.toExtended.valueFunction (x, z) + ≤ Q.toExtended.valueFunction (y, z) - Q.toExtended.valueFunction (x, z)) : + ContIncreasingDifferences P Q := by + intro z x y hx hy hxy + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + refine Finset.sum_le_sum fun z' _ => ?_ + rw [show P.transitionMatrix z z' = Q.transitionMatrix z z' from by rw [hP]] + exact mul_le_mul_of_nonneg_left (hv z' x y hx hy hxy) (Q.transitionMatrix_nonneg z z') + +/-- **Single crossing across rates.** Two problems whose budget sets coincide and which differ +only in the continuation are ordered by the continuation: increasing differences make the second +save at least as much. + +This is the exchange argument of `policy_mono`, turned round — there one objective at two +states, here two objectives at one state. -/ +theorem policy_le_of_cont_increasingDifferences + (hu : P.u = Q.u) (hdom : P.dom = Q.dom) (hβ : (P.discount : ℝ) = (Q.discount : ℝ)) + (hid : ContIncreasingDifferences P Q) + {a₁ a₂ : ℝ} {z : Z} (h₁ : a₁ ∈ Icc (0 : ℝ) assetCap) (h₂ : a₂ ∈ Icc (0 : ℝ) assetCap) + (hres : P.resources (a₁, z) = Q.resources (a₂, z)) : + P.policy (a₁, z) ≤ Q.policy (a₂, z) := by + by_contra hcon + rw [not_le] at hcon + set bP : ℝ := P.policy (a₁, z) with hbP + set bQ : ℝ := Q.policy (a₂, z) with hbQ + -- the two budget sets coincide + have hms : P.maxSaving (a₁, z) = Q.maxSaving (a₂, z) := by simp only [maxSaving, hres] + have hcons : ∀ x : ℝ, P.consumption (a₁, z) x = Q.consumption (a₂, z) x := by + intro x; simp only [consumption, hres] + have hbPm : bP ∈ P.toExtended.feasible (a₁, z) := P.policy_mem _ + have hbQm : bQ ∈ Q.toExtended.feasible (a₂, z) := Q.policy_mem _ + rw [P.feasible_eq] at hbPm + rw [Q.feasible_eq] at hbQm + have hbPQ : bP ∈ Q.toExtended.feasible (a₂, z) := by + rw [Q.feasible_eq, ← hms]; exact hbPm + have hbQP : bQ ∈ P.toExtended.feasible (a₁, z) := by + rw [P.feasible_eq, hms]; exact hbQm + -- all four consumptions are positive + have hcP : P.consumption (a₁, z) bP ∈ P.dom := P.consumption_policy_mem_dom h₁ + have hcQ : Q.consumption (a₂, z) bQ ∈ Q.dom := Q.consumption_policy_mem_dom h₂ + have hcPQ : P.consumption (a₁, z) bQ ∈ P.dom := by rw [hcons, hdom]; exact hcQ + have hcQP : Q.consumption (a₂, z) bP ∈ Q.dom := by rw [← hcons, ← hdom]; exact hcP + -- optimality of each against the other's choice + have hoptP := P.objR_le_of_mem h₁ hbQP hcPQ + have hoptQ := Q.objR_le_of_mem h₂ hbPQ hcQP + simp only [objR, hcons, hu, hβ] at hoptP + simp only [objR] at hoptQ + -- increasing differences on the pair `bQ < bP` + have hreg : bP ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ + have hreg' : bQ ∈ Icc (0 : ℝ) assetCap := Q.policy_mem_region _ + have hidd := hid z bQ bP hreg' hreg hcon.le + -- both optimality comparisons are therefore equalities + have hβ0 : (0 : ℝ) ≤ Q.discount := Q.discount.coe_nonneg + have hQeq : Q.objR (a₂, z) bP = Q.objR (a₂, z) bQ := by + simp only [objR] + nlinarith [hoptP, hoptQ, mul_le_mul_of_nonneg_left hidd hβ0] + -- so the first economy's choice is optimal in the second, and uniqueness closes it + have hbell : Q.toExtended.objectiveE Q.toExtended.valueFunction (a₂, z) bP + = ((Q.toExtended.bellmanFn Q.toExtended.valueFunction (a₂, z) : ℝ) : EReal) := by + rw [Q.objectiveE_eq_coe h₂ hbPQ hcQP, hQeq, ← Q.objectiveE_eq_coe h₂ (Q.policy_mem _) hcQ] + exact Q.policy_optimal _ + exact absurd (Q.eq_policy_of_optimal h₂ hbPQ hbell) (by rw [← hbQ]; linarith) + +/-! ### The rescaled asset level -/ + +/-- The asset level at the higher rate that gives the same cash on hand. -/ +noncomputable def rateScale : ℝ := (1 + P.interest) / (1 + Q.interest) + +theorem rateScale_pos : 0 < rateScale P Q := + div_pos P.interest_gt_neg_one Q.interest_gt_neg_one + +theorem rateScale_le_one (hr : P.interest ≤ Q.interest) : rateScale P Q ≤ 1 := by + rw [rateScale, div_le_one Q.interest_gt_neg_one]; linarith + +theorem resources_rateScale (hinc : P.income = Q.income) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) + (z : Z) : + P.resources (a, z) = Q.resources (rateScale P Q * a, z) := by + have hscale : (0 : ℝ) ≤ rateScale P Q * a := mul_nonneg (rateScale_pos P Q).le ha.1 + have hmax : max 0 (rateScale P Q * a) = rateScale P Q * a := max_eq_right hscale + simp only [resources, hinc, max_eq_right ha.1, hmax] + simp only [rateScale] + have hne : (1 : ℝ) + Q.interest ≠ 0 := Q.interest_gt_neg_one.ne' + field_simp + +theorem rateScale_mem (hr : P.interest ≤ Q.interest) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + rateScale P Q * a ∈ Icc (0 : ℝ) assetCap := by + have h1 := rateScale_pos P Q + have h2 := rateScale_le_one P Q hr + exact ⟨mul_nonneg h1.le ha.1, by nlinarith [ha.1, ha.2]⟩ + +/-! ### Theorem 1 -/ + +/-- **Light (2018) Theorem 1.** A household facing a higher interest rate saves at least as much, +at every asset level and every income state. + +The hypothesis `hid` is Light's step 5; it is discharged in `IncomeFluctuationRateStep` by +`contIncreasingDifferences_withRate`, which needs the envelope condition together with concavity +of the consumption function. See `policy_mono_withRate'` for the form without it. -/ +theorem policy_mono_interest (hu : P.u = Q.u) (hdom : P.dom = Q.dom) + (hβ : (P.discount : ℝ) = (Q.discount : ℝ)) + (hinc : P.income = Q.income) (hr : P.interest ≤ Q.interest) + (hid : ContIncreasingDifferences P Q) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + P.policy (a, z) ≤ Q.policy (a, z) := by + have hmem := rateScale_mem P Q hr ha + refine le_trans (policy_le_of_cont_increasingDifferences P Q hu hdom hβ hid ha hmem + (resources_rateScale P Q hinc ha z)) ?_ + refine Q.policy_mono hmem ha ?_ + nlinarith [rateScale_le_one P Q hr, ha.1, rateScale_pos P Q] + +/-- **Theorem 1 for a rate family**, in the form `CapitalSupplyMonotone` consumes: the policy of +`P.withRate r` rises with `r`. -/ +theorem policy_mono_withRate {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hid : ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := + policy_mono_interest (P.withRate r₁ h₁) (P.withRate r₂ h₂) rfl rfl rfl rfl hr hid ha z + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean new file mode 100644 index 000000000..a922e151d --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean @@ -0,0 +1,752 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateMonotone + + +/-! +# Light (2018), step 5: increasing differences propagate across rates + +`IncomeFluctuationRateMonotone` reduces Theorem 1 to one hypothesis, `ContIncreasingDifferences`, +and records that propagating it needs two things this development did not have: the envelope +condition as an EQUALITY, and concavity of the consumption function. Both are now proved +(`hasDerivAt_bellman`, `concaveOn_consumptionFnOf_bellman`), so the step is available. + +## The identity the whole thing rests on + +Two rates share a budget set: cash on hand at `(a, R₁)` equals cash on hand at `(t a, R₂)` for +`t = R₁ / R₂`, and the saving cap is the same because it is `min assetCap (cash on hand)`. So the +two maximisations are literally the same maximisation, and `lazyValue_withRate` says so. Hence +`bellmanFn_withRate`, `policyOf_withRate` and `consumptionFnOf_withRate`: the whole solution at +`(a, R₁)` is the solution at `(t a, R₂)`. + +That turns the difference `(T_{R₂} f)(a) - (T_{R₁} f)(a)` into `(T_{R₂} f)(a) - (T_{R₂} f)(t a)`, +a statement about ONE economy, and increasing differences across rates becomes monotonicity of +that difference in `a`. The envelope makes its derivative `R₂ u'(c_w(a)) - R₁ u'(c_v(t a))`, +where `v` and `w` are the two continuations, and two moves sign it: single crossing in the +continuation at the scaled asset level, then `mul_marginal_le_of_scale` — concavity of the +consumption function for one factor, relative risk aversion at most one for the other. + +Doing it in that order matters. Applying the pivotal inequality directly to `c_v` would need +Carroll–Kimball for economy two's choice against economy ONE's continuation, a hybrid object no +induction produces; going through `c_v(t a) ≥ c_w(t a)` first puts the pivotal inequality on +`c_w`, economy two's own, which is exactly what `concaveOn_consumptionFn_of_iterates` delivers. + +## What is left + +`contIncreasingDifferences_withRate` discharges the `hid` of `policy_mono_interest`, so Light's +Theorem 1 holds outright. Its hypotheses are the standing ones of this development: utility +differentiable with decreasing, non-negative marginal utility and relative risk aversion at most +one; consumption positive at every optimum; the asset cap slack along the iteration; and the +Carroll–Kimball concavity of the iterates' consumption functions. `CESConcaveConsumption` shows +these hold together at concrete CES calibrations. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-! ### The two rates solve the same problem at rescaled assets -/ + +variable {r₁ r₂ : ℝ} + +/-- **The objective at `(a, r₁)` is the objective at `(t a, r₂)`**, action for action. -/ +theorem lazyValue_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (v : (ℝ × Z) →ᵇ ℝ) (z : Z) + (x : ℝ) {a : ℝ} (ha : 0 ≤ a) : + (P.withRate r₁ h₁).lazyValue v z x a + = (P.withRate r₂ h₂).lazyValue v z x ((1 + r₁) / (1 + r₂) * a) := by + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have hscale : (0 : ℝ) ≤ (1 + r₁) / (1 + r₂) * a := by + have : (0 : ℝ) < 1 + r₁ := h₁.1 + positivity + have harg : P.income z + (1 + r₂) * ((1 + r₁) / (1 + r₂) * a) + = P.income z + (1 + r₁) * a := by field_simp + simp only [lazyValue, resources, IncomeFluctuation.withRate_income, + IncomeFluctuation.withRate_interest, IncomeFluctuation.withRate_discount, + IncomeFluctuation.withRate_transitionMatrix, IncomeFluctuation.withRate_u, + max_eq_right ha, max_eq_right hscale, harg] + +/-- The feasible sets coincide. -/ +theorem feasible_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (z : Z) {a : ℝ} (ha : 0 ≤ a) : + (P.withRate r₁ h₁).toExtended.feasible (a, z) + = (P.withRate r₂ h₂).toExtended.feasible ((1 + r₁) / (1 + r₂) * a, z) := by + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have hscale : (0 : ℝ) ≤ (1 + r₁) / (1 + r₂) * a := by + have : (0 : ℝ) < 1 + r₁ := h₁.1 + positivity + have hres : (P.withRate r₁ h₁).resources (a, z) + = (P.withRate r₂ h₂).resources ((1 + r₁) / (1 + r₂) * a, z) := by + have harg : P.income z + (1 + r₂) * ((1 + r₁) / (1 + r₂) * a) + = P.income z + (1 + r₁) * a := by field_simp + simp only [resources, IncomeFluctuation.withRate_income, + IncomeFluctuation.withRate_interest, max_eq_right ha, max_eq_right hscale, harg] + rw [(P.withRate r₁ h₁).feasible_eq, (P.withRate r₂ h₂).feasible_eq, + (P.withRate r₁ h₁).maxSaving_eq, (P.withRate r₂ h₂).maxSaving_eq, hres] + +/-- Consumption at a given action coincides. -/ +theorem consumption_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (z : Z) (x : ℝ) {a : ℝ} + (ha : 0 ≤ a) : + (P.withRate r₁ h₁).consumption (a, z) x + = (P.withRate r₂ h₂).consumption ((1 + r₁) / (1 + r₂) * a, z) x := by + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have hscale : (0 : ℝ) ≤ (1 + r₁) / (1 + r₂) * a := by + have : (0 : ℝ) < 1 + r₁ := h₁.1 + positivity + have harg : P.income z + (1 + r₂) * ((1 + r₁) / (1 + r₂) * a) + = P.income z + (1 + r₁) * a := by field_simp + simp only [consumption, resources, IncomeFluctuation.withRate_income, + IncomeFluctuation.withRate_interest, max_eq_right ha, max_eq_right hscale, harg] + + +/-- **The whole solution at `(a, r₁)` is the solution at `(t a, r₂)`.** -/ +theorem bellmanFn_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + (P.withRate r₁ h₁).toExtended.bellmanFn v (a, z) + = (P.withRate r₂ h₂).toExtended.bellmanFn v ((1 + r₁) / (1 + r₂) * a, z) := by + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have ht1 : (1 + r₁) / (1 + r₂) ≤ 1 := by rw [div_le_one hR₂]; linarith + have hscale : (1 + r₁) / (1 + r₂) * a ∈ Icc (0 : ℝ) assetCap := by + refine ⟨mul_nonneg (le_of_lt (div_pos hR₁ hR₂)) ha.1, ?_⟩ + nlinarith [ha.1, ha.2, div_pos hR₁ hR₂] + have hback : (1 + r₂) / (1 + r₁) * ((1 + r₁) / (1 + r₂) * a) = a := by field_simp + refine le_antisymm ?_ ?_ + · set b := (P.withRate r₁ h₁).policyOf v (a, z) with hb + have heq := (P.withRate r₁ h₁).lazyValue_eq_dom v z ha + ((P.withRate r₁ h₁).policyOf_mem v (a, z)) + ((P.withRate r₁ h₁).consumption_policyOf_mem_dom v ha) + ((P.withRate r₁ h₁).policyOf_optimal v (a, z)) + have hfe : b ∈ (P.withRate r₂ h₂).toExtended.feasible ((1 + r₁) / (1 + r₂) * a, z) := by + rw [← P.feasible_withRate h₁ h₂ z ha.1] + exact (P.withRate r₁ h₁).policyOf_mem v (a, z) + have hcd : (P.withRate r₂ h₂).consumption ((1 + r₁) / (1 + r₂) * a, z) b + ∈ (P.withRate r₂ h₂).dom := by + rw [← P.consumption_withRate h₁ h₂ z b ha.1] + exact (P.withRate r₁ h₁).consumption_policyOf_mem_dom v ha + have hle := (P.withRate r₂ h₂).lazyValue_le_dom v z hscale hfe hcd + rw [← P.lazyValue_withRate h₁ h₂ v z b ha.1, heq] at hle + exact hle + · set b := (P.withRate r₂ h₂).policyOf v ((1 + r₁) / (1 + r₂) * a, z) with hb + have heq := (P.withRate r₂ h₂).lazyValue_eq_dom v z hscale + ((P.withRate r₂ h₂).policyOf_mem v _) + ((P.withRate r₂ h₂).consumption_policyOf_mem_dom v hscale) + ((P.withRate r₂ h₂).policyOf_optimal v _) + have hfe : b ∈ (P.withRate r₁ h₁).toExtended.feasible (a, z) := by + rw [P.feasible_withRate h₁ h₂ z ha.1] + exact (P.withRate r₂ h₂).policyOf_mem v _ + have hcd : (P.withRate r₁ h₁).consumption (a, z) b ∈ (P.withRate r₁ h₁).dom := by + rw [P.consumption_withRate h₁ h₂ z b ha.1] + exact (P.withRate r₂ h₂).consumption_policyOf_mem_dom v hscale + have hle := (P.withRate r₁ h₁).lazyValue_le_dom v z ha hfe hcd + rw [P.lazyValue_withRate h₁ h₂ v z b ha.1, heq] at hle + exact hle + +/-- **The saving chosen at `(a, r₁)` is the saving chosen at `(t a, r₂)`.** -/ +theorem policyOf_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices (0 : ℝ) assetCap v) (z : Z) {a : ℝ} + (ha : a ∈ Icc (0 : ℝ) assetCap) : + (P.withRate r₁ h₁).policyOf v (a, z) + = (P.withRate r₂ h₂).policyOf v ((1 + r₁) / (1 + r₂) * a, z) := by + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have ht1 : (1 + r₁) / (1 + r₂) ≤ 1 := by rw [div_le_one hR₂]; linarith + have hscale : (1 + r₁) / (1 + r₂) * a ∈ Icc (0 : ℝ) assetCap := by + refine ⟨mul_nonneg (le_of_lt (div_pos hR₁ hR₂)) ha.1, ?_⟩ + nlinarith [ha.1, ha.2, div_pos hR₁ hR₂] + set b := (P.withRate r₁ h₁).policyOf v (a, z) with hb + have hfe : b ∈ (P.withRate r₂ h₂).toExtended.feasible ((1 + r₁) / (1 + r₂) * a, z) := by + rw [← P.feasible_withRate h₁ h₂ z ha.1] + exact (P.withRate r₁ h₁).policyOf_mem v (a, z) + have hcd : (P.withRate r₂ h₂).consumption ((1 + r₁) / (1 + r₂) * a, z) b + ∈ (P.withRate r₂ h₂).dom := by + rw [← P.consumption_withRate h₁ h₂ z b ha.1] + exact (P.withRate r₁ h₁).consumption_policyOf_mem_dom v ha + refine (P.withRate r₂ h₂).optimal_action_unique_of_concaveSlices hv + (s := ((1 + r₁) / (1 + r₂) * a, z)) hscale hfe + ((P.withRate r₂ h₂).policyOf_mem v _) ?_ ((P.withRate r₂ h₂).policyOf_optimal v _) + rw [(P.withRate r₂ h₂).objectiveE_eq_coe_of v hscale hfe hcd, + ← P.bellmanFn_withRate h₁ h₂ hr v z ha] + have hobj : (P.withRate r₂ h₂).objROf v ((1 + r₁) / (1 + r₂) * a, z) b + = (P.withRate r₁ h₁).lazyValue v z b a := by + change (P.withRate r₂ h₂).lazyValue v z b ((1 + r₁) / (1 + r₂) * a) = _ + rw [P.lazyValue_withRate h₁ h₂ v z b ha.1] + rw [hobj] + exact congrArg _ ((P.withRate r₁ h₁).lazyValue_eq_dom v z ha + ((P.withRate r₁ h₁).policyOf_mem v (a, z)) + ((P.withRate r₁ h₁).consumption_policyOf_mem_dom v ha) + ((P.withRate r₁ h₁).policyOf_optimal v (a, z))) + +/-- **Consumption too.** This is the function `mul_marginal_le_of_scale` is applied to. -/ +theorem consumptionFnOf_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices (0 : ℝ) assetCap v) (z : Z) {a : ℝ} + (ha : a ∈ Icc (0 : ℝ) assetCap) : + (P.withRate r₁ h₁).consumptionFnOf v z a + = (P.withRate r₂ h₂).consumptionFnOf v z ((1 + r₁) / (1 + r₂) * a) := by + simp only [consumptionFnOf] + rw [P.policyOf_withRate h₁ h₂ hr hv z ha, P.consumption_withRate h₁ h₂ z _ ha.1] + + +/-! ### The derivative assembly + +With the budget coincidence in hand the whole comparison lives in ONE economy: the difference to +be signed is + + `F a = (T w)(a) - (T v)(t a)`, `t = R₁ / R₂ ≤ 1`. + +`hasDerivAt_bellman` — the Clausen–Strub lazy-agent envelope — differentiates both terms, giving + + `F' a = R₂ u'(c_w(a)) - t R₂ u'(c_v(t a)) = R₂ u'(c_w(a)) - R₁ u'(c_v(t a))`. + +The second term is signed in two moves. Single crossing in the continuation puts +`c_w(t a) ≤ c_v(t a)`, so `u'(c_v(t a)) ≤ u'(c_w(t a))`; and then `mul_marginal_le_of_scale`, +Light's pivotal inequality, carries `R₁ u'(c_w(t a)) ≤ R₂ u'(c_w(a))`. Note which consumption +function the pivotal inequality is applied to: `c_w`, economy TWO's own, whose concavity is what +Carroll–Kimball delivers. Routing the comparison through `c_w` rather than through `c_v` is what +keeps the argument inside one economy's Carroll–Kimball theorem — `c_v` is the hybrid object +(economy two's choice against economy one's continuation) and no concavity of it is needed. + +Note what the envelope is asked for: the state interior and the CHOICE strictly below the saving +cap. Nothing is asked at the borrowing constraint, so the argument survives where it binds. -/ + +/-- **The monotone difference, in one economy, from the pivotal inequality as a hypothesis.** If +`t ≤ 1`, the continuations `v` and `w` have increasing differences, and `t R u'(c_w(t a)) ≤ +R u'(c_w(a))` at every interior `a`, then `a ↦ (T w)(a) - (T v)(t a)` is nondecreasing. -/ +theorem monotoneOn_bellman_sub_scaled_of_piv {assetCap : ℝ} (Q : IncomeFluctuation Z 0 assetCap) + {t : ℝ} (ht0 : 0 < t) (ht1 : t ≤ 1) {v w : (ℝ × Z) →ᵇ ℝ} {z : Z} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt Q.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) + (hwc : ConcaveSlices (0 : ℝ) assetCap w) (hid : Q.ContOfIncreasingDifferences v w) + (hbv : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + fun x => (Q.toExtended.bellman v) (x, z)) + (hbw : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + fun x => (Q.toExtended.bellman w) (x, z)) + (hintv : ∀ x ∈ Icc (0 : ℝ) assetCap, Q.policyOf v (x, z) < Q.maxSaving (x, z)) + (hintw : ∀ x ∈ Icc (0 : ℝ) assetCap, Q.policyOf w (x, z) < Q.maxSaving (x, z)) + (hposv : ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < Q.consumptionFnOf v z x) + (hposw : ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < Q.consumptionFnOf w z x) + (hpiv : ∀ a ∈ Ioo (0 : ℝ) assetCap, + t * (1 + Q.interest) * du (Q.consumptionFnOf w z (t * a)) + ≤ (1 + Q.interest) * du (Q.consumptionFnOf w z a)) : + MonotoneOn (fun a => (Q.toExtended.bellman w) (a, z) - (Q.toExtended.bellman v) (t * a, z)) + (Icc (0 : ℝ) assetCap) := by + have hR : (0 : ℝ) < 1 + Q.interest := Q.interest_gt_neg_one + -- the derivative, and its sign, at every interior point + have key : ∀ a ∈ Ioo (0 : ℝ) assetCap, ∃ D : ℝ, 0 ≤ D ∧ + HasDerivAt (fun x => (Q.toExtended.bellman w) (x, z) - (Q.toExtended.bellman v) (t * x, z)) + D a := by + intro a ha + have ha0 : (0 : ℝ) < a := ha.1 + have hamem : a ∈ Icc (0 : ℝ) assetCap := ⟨ha.1.le, ha.2.le⟩ + have hta0 : (0 : ℝ) < t * a := mul_pos ht0 ha0 + have htale : t * a ≤ a := by nlinarith + have htamem : t * a ∈ Icc (0 : ℝ) assetCap := ⟨hta0.le, le_trans htale hamem.2⟩ + -- the two envelope derivatives + have hd1 : HasDerivAt (fun x => (Q.toExtended.bellman w) (x, z)) + ((1 + Q.interest) * du (Q.consumptionFnOf w z a)) a := + Q.hasDerivAt_bellman ha0 ha.2 hbw (hintw a hamem) (hposw a hamem) + (hderiv _ (hposw a hamem)) + have hbase : HasDerivAt (fun x => (Q.toExtended.bellman v) (x, z)) + ((1 + Q.interest) * du (Q.consumptionFnOf v z (t * a))) (t * a) := + Q.hasDerivAt_bellman hta0 (lt_of_le_of_lt htale ha.2) hbv (hintv _ htamem) + (hposv _ htamem) (hderiv _ (hposv _ htamem)) + have hlin : HasDerivAt (fun x : ℝ => t * x) t a := by + simpa using (hasDerivAt_id a).const_mul t + have hd2 : HasDerivAt (fun x : ℝ => (Q.toExtended.bellman v) (t * x, z)) + ((1 + Q.interest) * du (Q.consumptionFnOf v z (t * a)) * t) a := hbase.comp a hlin + refine ⟨_, ?_, hd1.sub hd2⟩ + have hpiv := hpiv a ha + -- single crossing in the continuation, at the SCALED asset level + have hcross : Q.consumptionFnOf w z (t * a) ≤ Q.consumptionFnOf v z (t * a) := + Q.consumptionFnOf_le_of_contOf_increasingDifferences hwc hid htamem + have hdu := hanti (mem_Ioi.mpr (hposw _ htamem)) (mem_Ioi.mpr (hposv _ htamem)) hcross + have ht1' : (0 : ℝ) < t * (1 + Q.interest) := by positivity + nlinarith [hpiv, hdu, hR, ht1'] + refine monotoneOn_of_deriv_nonneg (convex_Icc _ _) ?_ ?_ ?_ + · refine ContinuousOn.sub ?_ ?_ + · exact ((Q.toExtended.bellman w).continuous.comp + (continuous_id.prodMk continuous_const)).continuousOn + · exact ((Q.toExtended.bellman v).continuous.comp + ((continuous_const.mul continuous_id).prodMk continuous_const)).continuousOn + · rw [interior_Icc] + intro x hx + obtain ⟨D, _, hD⟩ := key x hx + exact hD.differentiableAt.differentiableWithinAt + · rw [interior_Icc] + intro x hx + obtain ⟨D, hD0, hD⟩ := key x hx + rw [hD.deriv] + exact hD0 + +/-- **The monotone difference, in one economy.** If `t ≤ 1` and the continuations `v` and `w` have +increasing differences, then `a ↦ (T w)(a) - (T v)(t a)` is nondecreasing. The pivotal inequality +is supplied by `mul_marginal_le_of_scale`: relative risk aversion at most one and a concave, +increasing consumption function. -/ +theorem monotoneOn_bellman_sub_scaled {assetCap : ℝ} (Q : IncomeFluctuation Z 0 assetCap) + {t : ℝ} (ht0 : 0 < t) (ht1 : t ≤ 1) {v w : (ℝ × Z) →ᵇ ℝ} {z : Z} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt Q.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) + (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hwc : ConcaveSlices (0 : ℝ) assetCap w) (hid : Q.ContOfIncreasingDifferences v w) + (hbv : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + fun x => (Q.toExtended.bellman v) (x, z)) + (hbw : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + fun x => (Q.toExtended.bellman w) (x, z)) + (hintv : ∀ x ∈ Icc (0 : ℝ) assetCap, Q.policyOf v (x, z) < Q.maxSaving (x, z)) + (hintw : ∀ x ∈ Icc (0 : ℝ) assetCap, Q.policyOf w (x, z) < Q.maxSaving (x, z)) + (hposv : ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < Q.consumptionFnOf v z x) + (hposw : ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < Q.consumptionFnOf w z x) + (hcw : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) (Q.consumptionFnOf w z)) + (hmw : MonotoneOn (Q.consumptionFnOf w z) (Icc (0 : ℝ) assetCap)) : + MonotoneOn (fun a => (Q.toExtended.bellman w) (a, z) - (Q.toExtended.bellman v) (t * a, z)) + (Icc (0 : ℝ) assetCap) := by + refine Q.monotoneOn_bellman_sub_scaled_of_piv ht0 ht1 hderiv hanti hwc hid hbv hbw hintv hintw + hposv hposw ?_ + intro a ha + have hR : (0 : ℝ) < 1 + Q.interest := Q.interest_gt_neg_one + have hcap : (0 : ℝ) ≤ assetCap := Q.assetFloor_le_assetCap + have hzero : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, hcap⟩ + have ha0 : (0 : ℝ) < a := ha.1 + have hamem : a ∈ Icc (0 : ℝ) assetCap := ⟨ha.1.le, ha.2.le⟩ + have hta0 : (0 : ℝ) < t * a := mul_pos ht0 ha0 + have htale : t * a ≤ a := by nlinarith + have htamem : t * a ∈ Icc (0 : ℝ) assetCap := ⟨hta0.le, le_trans htale hamem.2⟩ + -- Light's pivotal inequality, at `α₁ = t R`, `α₂ = R`, `x = a / R`, applied to economy + -- two's OWN consumption function + have e1 : t * (1 + Q.interest) * (a / (1 + Q.interest)) = t * a := by field_simp + have e2 : (1 + Q.interest) * (a / (1 + Q.interest)) = a := by field_simp + have hpiv := mul_marginal_le_of_scale (c := Q.consumptionFnOf w z) (du := du) hcw hzero + (hposw 0 hzero) hmw hrra hdunn (α₁ := t * (1 + Q.interest)) (α₂ := 1 + Q.interest) + (x := a / (1 + Q.interest)) (by positivity) (by nlinarith) (by positivity) + (by rw [e1]; exact htamem) (by rw [e2]; exact hamem) + rw [e1, e2] at hpiv + exact hpiv + +/-! ### The step, across rates + +Reading the difference back through the budget coincidence turns the previous theorem into the +statement Light's induction propagates: the Bellman images of two continuations with increasing +differences again have increasing differences, at the two rates. -/ + +/-- **Light (2018), step 5.** Increasing differences survive the Bellman operator. -/ +theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) + (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hvc : ConcaveSlices (0 : ℝ) assetCap v) (hwc : ConcaveSlices (0 : ℝ) assetCap w) + (hposv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf v z a) + (hposw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf w z a) + (hslackv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf v (a, z) < assetCap) + (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf w (a, z) < assetCap) + (hcw : ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) ((P.withRate r₂ h₂).consumptionFnOf w z)) + (hid : IncDiffSlices (0 : ℝ) assetCap v w) : + IncDiffSlices (0 : ℝ) assetCap ((P.withRate r₁ h₁).toExtended.bellman v) + ((P.withRate r₂ h₂).toExtended.bellman w) := by + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + intro z x y hx hy hxy + have hmono := (P.withRate r₂ h₂).monotoneOn_bellman_sub_scaled + (t := (1 + r₁) / (1 + r₂)) (div_pos hR₁ hR₂) (by rw [div_le_one hR₂]; linarith) + (v := v) (w := w) (z := z) (du := du) hderiv hanti hrra hdunn hwc + ((P.withRate r₂ h₂).contOfIncreasingDifferences_of_slices hid) + ((P.withRate r₂ h₂).concaveSlices_bellman hvc z) + ((P.withRate r₂ h₂).concaveSlices_bellman hwc z) + (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposv a ha z) + (hslackv a ha z)) + (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposw a ha z) + (hslackw a ha z)) + (fun a ha => hposv a ha z) (fun a ha => hposw a ha z) (hcw z) + (fun a ha a' ha' hle => (P.withRate r₂ h₂).consumptionFnOf_mono hwc ha ha' hle) + have hkey := hmono hx hy hxy + -- read the economy-one terms back through the budget coincidence + have hcoin : ∀ a ∈ Icc (0 : ℝ) assetCap, + ((P.withRate r₁ h₁).toExtended.bellman v) (a, z) + = ((P.withRate r₂ h₂).toExtended.bellman v) ((1 + r₁) / (1 + r₂) * a, z) := by + intro a ha + rw [(P.withRate r₁ h₁).toExtended.bellman_apply, + (P.withRate r₂ h₂).toExtended.bellman_apply] + exact P.bellmanFn_withRate h₁ h₂ hr v z ha + rw [hcoin x hx, hcoin y hy] + linarith + +/-! ### The induction, and the limit + +`IncDiffSlices` mentions no economy and is closed under pointwise limits, so the step iterates +from the common start `0` and passes to the two value functions. That discharges the hypothesis +`hid` of `policy_mono_interest`, and with it Light's Theorem 1. -/ + +/-- **Light (2018) Theorem 1, with step 5 discharged.** The value functions at two rates have +increasing differences in assets, so the continuations do. -/ +theorem contIncreasingDifferences_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) + (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) : + ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂) := by + have hvs : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact (P.withRate r₁ h₁).concaveSlices_bellman ih + have hws : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact (P.withRate r₂ h₂).concaveSlices_bellman ih + -- the induction + have hstep : ∀ n : ℕ, IncDiffSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact incDiffSlices_zero _ _ + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact P.incDiffSlices_bellman_withRate h₁ h₂ hr hderiv hanti hrra hdunn (hvs k) (hws k) + (hposv k) (hposw k) (hslackv k) (hslackw k) (hcons k) ih + -- and the limit + refine contIncreasingDifferences_of_valueFunction _ _ rfl ?_ + intro z x y hx hy hxy + have hlim₁ : Tendsto (fun n => ((P.withRate r₁ h₁).toExtended.bellman)^[n] + (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 (P.withRate r₁ h₁).toExtended.valueFunction) := + (P.withRate r₁ h₁).toExtended.tendsto_iterate_valueFunction 0 + have hlim₂ : Tendsto (fun n => ((P.withRate r₂ h₂).toExtended.bellman)^[n] + (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 (P.withRate r₂ h₂).toExtended.valueFunction) := + (P.withRate r₂ h₂).toExtended.tendsto_iterate_valueFunction 0 + have hpt : ∀ (F : ℕ → (ℝ × Z) →ᵇ ℝ) (G : (ℝ × Z) →ᵇ ℝ), Tendsto F atTop (𝓝 G) → + ∀ p : ℝ × Z, Tendsto (fun n => F n p) atTop (𝓝 (G p)) := fun F G hF p => + (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp hF).tendsto_at p + have hev₁ := hpt _ _ hlim₁ + have hev₂ := hpt _ _ hlim₂ + exact le_of_tendsto_of_tendsto ((hev₁ (y, z)).sub (hev₁ (x, z))) + ((hev₂ (y, z)).sub (hev₂ (x, z))) (Eventually.of_forall fun n => hstep n z x y hx hy hxy) + +/-- **Light (2018) Theorem 1, unconditional on the increasing-differences hypothesis.** A +household facing a higher interest rate saves at least as much, at every asset level and every +income state. -/ +theorem policy_mono_withRate' (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) + (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := + P.policy_mono_withRate h₁ h₂ hr + (P.contIncreasingDifferences_withRate h₁ h₂ hr hderiv hanti hrra hdunn hposv hposw hslackv + hslackw hcons) ha z + +/-! ### CRRA, with the marginal-utility hypotheses discharged + +For `u = crraUtility γ` marginal utility is `c ^ (-γ)`, which is decreasing and non-negative for +every `γ > 0`, and `c · u'(c) = c ^ (1 - γ)` is nondecreasing exactly when `γ ≤ 1` — relative +risk aversion at most one, the hypothesis Light needs and the one this development has carried +since `RelativeRiskAversion`. -/ + +theorem policy_mono_withRate_crra {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) (hu : P.u = crraUtility γ) + (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := by + have hpow : ∀ c : ℝ, 0 < c → c * c ^ (-γ) = c ^ (1 - γ) := by + intro c hc + rw [show (1 : ℝ) - γ = 1 + -γ by ring, Real.rpow_add hc, Real.rpow_one] + refine P.policy_mono_withRate' h₁ h₂ hr (du := fun c => c ^ (-γ)) ?_ ?_ ?_ ?_ + hposv hposw hslackv hslackw hcons ha z + · intro c hc + rw [hu] + exact hasDerivAt_crraUtility γ hc + · intro x hx y hy hxy + exact Real.rpow_le_rpow_of_nonpos hx hxy (by linarith) + · intro x hx y hy hxy + simp only [] + rw [hpow x hx, hpow y hy] + exact Real.rpow_le_rpow hx.le hxy (by linarith) + · intro y hy + exact Real.rpow_nonneg hy.le _ + +/-! ### Beyond relative risk aversion one + +Everything above used `γ ≤ 1` in one place: the pivotal inequality +`t R u'(c_w(t a)) ≤ R u'(c_w(a))` applied to economy two's consumption function against the +CURRENT continuation `w`. Two changes make that inequality a hypothesis one can hope to meet +when `γ > 1`. + +First, run the induction from economy two's own value function rather than from `0`. The +economy-two iterates are then constant — `T₂ V₂ = V₂` — so the pivotal inequality is asked of +economy two's TRUE consumption function alone, not of every iterate (whose consumption functions +start at "eat everything", where no such inequality holds). + +Second, `mul_rpow_neg_le_of_scale` gives the inequality for `u' = c^{-γ}`, any `γ`, wherever +`(1 - c₂(0)/c₂(t a))(ρ - 1) ≤ ρ^{1/γ} - 1` with `ρ = R₂/R₁` — a condition on the range of the +consumption function, roughly `c₂(a) ≤ (γ/(γ-1)) c₂(0)` for rates close together. It is a +restriction on wealth, not on preferences: for a small enough asset cap, or on an invariant +interval, Light's Theorem 1 holds for every `γ`. -/ + +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] in +theorem incDiffSlices_refl (v : (ℝ × Z) →ᵇ ℝ) : IncDiffSlices (0 : ℝ) assetCap v v := + fun _ _ _ _ _ _ => le_rfl + +/-- **Light (2018), step 5, from the pivotal inequality as a hypothesis.** -/ +theorem incDiffSlices_bellman_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) + (hr : r₁ ≤ r₂) {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) + (hvc : ConcaveSlices (0 : ℝ) assetCap v) (hwc : ConcaveSlices (0 : ℝ) assetCap w) + (hposv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf v z a) + (hposw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf w z a) + (hslackv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf v (a, z) < assetCap) + (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf w (a, z) < assetCap) + (hpiv : ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf w z ((1 + r₁) / (1 + r₂) * a)) + ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf w z a)) + (hid : IncDiffSlices (0 : ℝ) assetCap v w) : + IncDiffSlices (0 : ℝ) assetCap ((P.withRate r₁ h₁).toExtended.bellman v) + ((P.withRate r₂ h₂).toExtended.bellman w) := by + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + intro z x y hx hy hxy + have hmono := (P.withRate r₂ h₂).monotoneOn_bellman_sub_scaled_of_piv + (t := (1 + r₁) / (1 + r₂)) (div_pos hR₁ hR₂) (by rw [div_le_one hR₂]; linarith) + (v := v) (w := w) (z := z) (du := du) hderiv hanti hwc + ((P.withRate r₂ h₂).contOfIncreasingDifferences_of_slices hid) + ((P.withRate r₂ h₂).concaveSlices_bellman hvc z) + ((P.withRate r₂ h₂).concaveSlices_bellman hwc z) + (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposv a ha z) + (hslackv a ha z)) + (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposw a ha z) + (hslackw a ha z)) + (fun a ha => hposv a ha z) (fun a ha => hposw a ha z) + (fun a ha => by + rw [IncomeFluctuation.withRate_interest, + show (1 + r₁) / (1 + r₂) * (1 + r₂) = 1 + r₁ from div_mul_cancel₀ _ hR₂.ne'] + exact hpiv z a ha) + have hkey := hmono hx hy hxy + have hcoin : ∀ a ∈ Icc (0 : ℝ) assetCap, + ((P.withRate r₁ h₁).toExtended.bellman v) (a, z) + = ((P.withRate r₂ h₂).toExtended.bellman v) ((1 + r₁) / (1 + r₂) * a, z) := by + intro a ha + rw [(P.withRate r₁ h₁).toExtended.bellman_apply, + (P.withRate r₂ h₂).toExtended.bellman_apply] + exact P.bellmanFn_withRate h₁ h₂ hr v z ha + rw [hcoin x hx, hcoin y hy] + linarith + +/-- **Light (2018) Theorem 1 from the pivotal inequality, the induction started anywhere.** -/ +theorem contIncreasingDifferences_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) + (hr : r₁ ≤ r₂) {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (v₀ : (ℝ × Z) →ᵇ ℝ) + (hv₀ : ConcaveSlices (0 : ℝ) assetCap v₀) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) (a, z) < assetCap) + (hpiv : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z ((1 + r₁) / (1 + r₂) * a)) + ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a)) : + ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂) := by + have hvs : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) := by + intro n + induction n with + | zero => exact hv₀ + | succ k ih => + rw [Function.iterate_succ_apply'] + exact (P.withRate r₁ h₁).concaveSlices_bellman ih + have hws : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := by + intro n + induction n with + | zero => exact hv₀ + | succ k ih => + rw [Function.iterate_succ_apply'] + exact (P.withRate r₂ h₂).concaveSlices_bellman ih + have hstep : ∀ n : ℕ, IncDiffSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := by + intro n + induction n with + | zero => exact incDiffSlices_refl v₀ + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact P.incDiffSlices_bellman_withRate_of_piv h₁ h₂ hr hderiv hanti (hvs k) (hws k) + (hposv k) (hposw k) (hslackv k) (hslackw k) (hpiv k) ih + refine contIncreasingDifferences_of_valueFunction _ _ rfl ?_ + intro z x y hx hy hxy + have hlim₁ : Tendsto (fun n => ((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) atTop + (𝓝 (P.withRate r₁ h₁).toExtended.valueFunction) := + (P.withRate r₁ h₁).toExtended.tendsto_iterate_valueFunction v₀ + have hlim₂ : Tendsto (fun n => ((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) atTop + (𝓝 (P.withRate r₂ h₂).toExtended.valueFunction) := + (P.withRate r₂ h₂).toExtended.tendsto_iterate_valueFunction v₀ + have hpt : ∀ (F : ℕ → (ℝ × Z) →ᵇ ℝ) (G : (ℝ × Z) →ᵇ ℝ), Tendsto F atTop (𝓝 G) → + ∀ p : ℝ × Z, Tendsto (fun n => F n p) atTop (𝓝 (G p)) := fun F G hF p => + (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp hF).tendsto_at p + have hev₁ := hpt _ _ hlim₁ + have hev₂ := hpt _ _ hlim₂ + exact le_of_tendsto_of_tendsto ((hev₁ (y, z)).sub (hev₁ (x, z))) + ((hev₂ (y, z)).sub (hev₂ (x, z))) (Eventually.of_forall fun n => hstep n z x y hx hy hxy) + +/-- **Light (2018) Theorem 1 from the pivotal inequality on economy two's OWN consumption +function.** The induction is started at economy two's value function, which its Bellman +operator fixes, so the only consumption function the inequality is asked of is the true one at +the higher rate. -/ +theorem policy_mono_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₁ h₁).toExtended.bellman)^[n] + (P.withRate r₂ h₂).toExtended.valueFunction) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (P.withRate r₂ h₂).toExtended.valueFunction) + (a, z) < assetCap) + (hposw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < (P.withRate r₂ h₂).consumptionFn z a) + (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policy (a, z) < assetCap) + (hpiv : ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFn z ((1 + r₁) / (1 + r₂) * a)) + ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFn z a)) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := by + have hfix : ∀ n : ℕ, ((P.withRate r₂ h₂).toExtended.bellman)^[n] + (P.withRate r₂ h₂).toExtended.valueFunction = (P.withRate r₂ h₂).toExtended.valueFunction := + fun n => Function.iterate_fixed (P.withRate r₂ h₂).toExtended.bellman_valueFunction n + refine P.policy_mono_withRate h₁ h₂ hr ?_ ha z + refine P.contIncreasingDifferences_withRate_of_piv h₁ h₂ hr hderiv hanti + (P.withRate r₂ h₂).toExtended.valueFunction (P.withRate r₂ h₂).concaveSlices_valueFunction + hposv (fun n a ha' z' => by rw [hfix n]; exact hposw a ha' z') hslackv + (fun n a ha' z' => by rw [hfix n]; exact hslackw a ha' z') + (fun n z' a ha' => by rw [hfix n]; exact hpiv z' a ha') + +/-- **Light (2018) Theorem 1 for CRRA with ANY `γ > 0`, on a range condition.** Saving rises with +the rate at every state provided economy two's consumption function satisfies +`(1 - c₂(0,z)/c₂(t a, z)) (ρ - 1) ≤ ρ^{1/γ} - 1` for `ρ = R₂/R₁`, `t = 1/ρ`, at every interior +`a` — for rates close together, consumption at most about `γ/(γ-1)` times its value at zero +assets. Concavity of that consumption function is Carroll--Kimball at the fixed point +(`concaveOn_consumptionFn_of_crra`). -/ +theorem policy_mono_withRate_crra_of_range {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hβ : 0 < (P.discount : ℝ)) (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hpc₂ : (P.withRate r₂ h₂).PositiveConsumptionAll) + (hslackIt : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (P.withRate r₂ h₂).toExtended.valueFunction) + (a, z) < assetCap) + (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policy (a, z) < assetCap) + (hrange : ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 - (P.withRate r₂ h₂).consumptionFn z 0 + / (P.withRate r₂ h₂).consumptionFn z ((1 + r₁) / (1 + r₂) * a)) + * ((1 + r₂) / (1 + r₁) - 1) ≤ ((1 + r₂) / (1 + r₁)) ^ (1 / γ) - 1) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := by + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have hcap : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg + have hzero : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, hcap⟩ + have hconc : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] + (P.withRate r₂ h₂).toExtended.valueFunction) := by + intro n + induction n with + | zero => exact (P.withRate r₂ h₂).concaveSlices_valueFunction + | succ k ih => + rw [Function.iterate_succ_apply'] + exact (P.withRate r₁ h₁).concaveSlices_bellman ih + refine P.policy_mono_withRate_of_piv h₁ h₂ hr (du := fun c => c ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_crraUtility γ hc) + (fun x hx y _ hxy => Real.rpow_le_rpow_of_nonpos hx hxy (by linarith)) + (fun n a' ha' z' => hpc₂ _ (hconc n) z' a' ha') hslackv + (fun a' ha' z' => hpc₂ _ (P.withRate r₂ h₂).concaveSlices_valueFunction z' a' ha') + hslackw ?_ ha z + intro z' a' ha' + have hcc : ConcaveOn ℝ (Icc (0 : ℝ) assetCap) ((P.withRate r₂ h₂).consumptionFn z') := + (P.withRate r₂ h₂).concaveOn_consumptionFn_of_crra hpc₂ hγ0 hβ hu hslackIt z' + have hmono : MonotoneOn ((P.withRate r₂ h₂).consumptionFn z') (Icc (0 : ℝ) assetCap) := + fun x hx y hy hxy => (P.withRate r₂ h₂).consumptionFn_mono hx hy hxy + have hc0 : 0 < (P.withRate r₂ h₂).consumptionFn z' 0 := + hpc₂ _ (P.withRate r₂ h₂).concaveSlices_valueFunction z' 0 hzero + have hta : (1 + r₁) / (1 + r₂) * a' ∈ Icc (0 : ℝ) assetCap := by + have ht1 : (1 + r₁) / (1 + r₂) ≤ 1 := by rw [div_le_one hR₂]; linarith + have ht0 : 0 < (1 + r₁) / (1 + r₂) := div_pos hR₁ hR₂ + constructor + · exact mul_nonneg ht0.le ha'.1.le + · nlinarith [ha'.2] + have e1 : (1 + r₁) * (a' / (1 + r₂)) = (1 + r₁) / (1 + r₂) * a' := by field_simp + have e2 : (1 + r₂) / (1 + r₁) * (1 + r₁) * (a' / (1 + r₂)) = a' := by field_simp + have e3 : (1 + r₂) / (1 + r₁) * (1 + r₁) = 1 + r₂ := div_mul_cancel₀ _ hR₁.ne' + have hpiv := mul_rpow_neg_le_of_scale hγ0 hcc hzero hc0 hmono (α₁ := 1 + r₁) + (ρ := (1 + r₂) / (1 + r₁)) (x := a' / (1 + r₂)) hR₁ (by rw [le_div_iff₀ hR₁]; linarith) + (div_pos ha'.1 hR₂) (by rw [e1]; exact hta) (by rw [e2]; exact ⟨ha'.1.le, ha'.2.le⟩) + (by rw [e1]; exact hrange z' a' ha') + rw [e1, e2, e3] at hpiv + exact hpiv + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean new file mode 100644 index 000000000..f350d860e --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean @@ -0,0 +1,296 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCrossRate + + +/-! +# The contraction, run on the compact region alone + +The value function is a bounded continuous function on all of `ℝ × Z`, but the model only +ever lives on `[0, assetCap] × Z`, which is forward invariant: every feasible choice lands +back in it (`feasible_subset_region`). So the Bellman operator evaluated at a state of the +region never consults the continuation value anywhere else. + +That has a consequence worth exploiting. The whole contraction estimate can be run with +suprema taken over the REGION rather than globally: + + sup_region |V_P - V_Q| ≤ (1 - β)⁻¹ * sup_region |bellman_P V_Q - V_Q| + +so comparative statics in the interest rate never needs a global sup-norm estimate. Since +the agent distribution also lives on the region, this is all the equilibrium argument +requires. It needs no second programme on the subtype, and no transport: it is a direct +estimate on the objects already built. + +The mechanism is that the reward cancels. Two continuation values fed to the same programme +give objectives differing only in the discounted expectation, and the expectation samples +the next asset level, which feasibility confines to the region. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The region the model lives on. -/ +def region (_P : IncomeFluctuation Z assetFloor assetCap) : Set (ℝ × Z) := + {s | s.1 ∈ Icc assetFloor assetCap} + +theorem isCompact_region : IsCompact P.region := by + have himg : P.region = (Icc assetFloor assetCap) ×ˢ (univ : Set Z) := by + ext s; simp [region, Set.mem_prod] + rw [himg] + exact isCompact_Icc.prod isCompact_univ + +theorem region_nonempty : P.region.Nonempty := + ⟨(assetFloor, Classical.ofNonempty), by simp [region, P.assetFloor_le_assetCap]⟩ + +/-- **The expectation only samples the region.** This is forward invariance in the form the +estimate needs. -/ +theorem abs_expect_sub_le {v w : (ℝ × Z) →ᵇ ℝ} {c : ℝ} + (hvw : ∀ t : ℝ × Z, t ∈ P.region → |v t - w t| ≤ c) {s : ℝ × Z} {a : ℝ} + (ha : a ∈ P.toExtended.feasible s) : + |P.toExtended.expect v (s, a) - P.toExtended.expect w (s, a)| ≤ c := by + have htr : ∀ z' : Z, P.toExtended.transition z' (s, a) = (a, z') := fun _ => rfl + have hreg : ∀ z' : Z, P.toExtended.transition z' (s, a) ∈ P.region := fun z' => by + rw [htr z']; exact P.feasible_subset_region ha + simp only [ExtendedStochasticProgram.expect, ← Finset.sum_sub_distrib, ← mul_sub] + calc |∑ z', P.toExtended.prob z' (s, a) + * (v (P.toExtended.transition z' (s, a)) - w (P.toExtended.transition z' (s, a)))| + ≤ ∑ z', |P.toExtended.prob z' (s, a) + * (v (P.toExtended.transition z' (s, a)) - w (P.toExtended.transition z' (s, a)))| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z' : Z, P.toExtended.prob z' (s, a) * c := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.toExtended.prob_nonneg z' _)] + exact mul_le_mul_of_nonneg_left (hvw _ (hreg z')) (P.toExtended.prob_nonneg z' _) + _ = c := by rw [← Finset.sum_mul, P.toExtended.prob_sum, one_mul] + +/-- **The operator contracts, measured on the region alone.** The reward cancels, so the two +objectives differ only in the discounted expectation, and the expectation samples the next +asset level, which feasibility confines to the region. -/ +theorem abs_bellmanFn_sub_le_region {v w : (ℝ × Z) →ᵇ ℝ} {c : ℝ} + (hvw : ∀ t : ℝ × Z, t ∈ P.region → |v t - w t| ≤ c) (s : ℝ × Z) : + |P.toExtended.bellmanFn v s - P.toExtended.bellmanFn w s| ≤ P.discount * c := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have key : ∀ v w : (ℝ × Z) →ᵇ ℝ, (∀ t : ℝ × Z, t ∈ P.region → |v t - w t| ≤ c) → + P.toExtended.bellmanFn v s ≤ P.toExtended.bellmanFn w s + P.discount * c := by + intro v w hvw + refine P.toExtended.bellmanFn_le v fun a ha => ?_ + have hstep : P.toExtended.objectiveE v s a + ≤ P.toExtended.objectiveE w s a + ((P.discount * c : ℝ) : EReal) := by + simp only [ExtendedStochasticProgram.objectiveE] + rw [add_assoc, ← EReal.coe_add] + refine add_le_add (le_refl _) ?_ + rw [EReal.coe_le_coe_iff] + have hexp := P.abs_expect_sub_le hvw ha + rw [abs_le] at hexp + -- `toExtended.discount` and `discount` are defeq but distinct atoms to `linarith` + rw [show (P.toExtended.discount : ℝ) = (P.discount : ℝ) from rfl] + nlinarith [hexp.2, hβ] + refine le_trans hstep ?_ + rw [EReal.coe_add] + exact add_le_add (P.toExtended.le_bellmanFn w ha) (le_refl _) + have hc : 0 ≤ c := + le_trans (abs_nonneg _) + (hvw (assetFloor, Classical.ofNonempty) (by simp [region, P.assetFloor_le_assetCap])) + have k1 := key v w hvw + have k2 := key w v fun t ht => by rw [abs_sub_comm]; exact hvw t ht + rw [abs_le] + constructor <;> linarith + +/-- **The bridge.** Two value functions differ on the region by at most the operator gap on +the region, amplified by `(1 - β)⁻¹`. Every norm here is taken over the COMPACT REGION, so +comparative statics in the interest rate never needs a global sup-norm estimate. + +Since the agent distribution also lives on the region, this is all the equilibrium argument +requires. Note it needs no second programme on the compact state space and no transport -- +it is a direct estimate on the objects already built. -/ +theorem abs_valueFunction_sub_le_region {P Q : IncomeFluctuation Z assetFloor assetCap} {D : ℝ} + (hD : ∀ t : ℝ × Z, t ∈ P.region → + |P.toExtended.bellmanFn Q.toExtended.valueFunction t + - Q.toExtended.valueFunction t| ≤ D) + {s : ℝ × Z} (hs : s ∈ P.region) : + |P.toExtended.valueFunction s - Q.toExtended.valueFunction s| + ≤ D / (1 - P.discount) := by + have hβ1 : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + have hβ0 : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set f : ℝ × Z → ℝ := + fun t => |P.toExtended.valueFunction t - Q.toExtended.valueFunction t| with hf + have hcont : ContinuousOn f P.region := + ((P.toExtended.valueFunction.continuous.sub + Q.toExtended.valueFunction.continuous).abs).continuousOn + obtain ⟨t, htmem, htmax⟩ := + P.isCompact_region.exists_isMaxOn P.region_nonempty hcont + have hCle : ∀ u : ℝ × Z, u ∈ P.region → f u ≤ f t := fun u hu => htmax hu + -- the contraction, measured on the region + have hstep : f t ≤ P.discount * f t + D := by + have h1 : P.toExtended.valueFunction t + = P.toExtended.bellmanFn P.toExtended.valueFunction t := by + rw [← P.toExtended.bellman_apply, P.toExtended.bellman_valueFunction] + have h2 := P.abs_bellmanFn_sub_le_region + (v := P.toExtended.valueFunction) (w := Q.toExtended.valueFunction) (c := f t) + (fun u hu => hCle u hu) t + have h3 := hD t htmem + have htri : |P.toExtended.bellmanFn P.toExtended.valueFunction t + - Q.toExtended.valueFunction t| + ≤ |P.toExtended.bellmanFn P.toExtended.valueFunction t + - P.toExtended.bellmanFn Q.toExtended.valueFunction t| + + |P.toExtended.bellmanFn Q.toExtended.valueFunction t + - Q.toExtended.valueFunction t| := abs_sub_le _ _ _ + have h4 : f t = |P.toExtended.bellmanFn P.toExtended.valueFunction t + - Q.toExtended.valueFunction t| := by rw [hf]; simp only [← h1] + calc f t = |P.toExtended.bellmanFn P.toExtended.valueFunction t + - Q.toExtended.valueFunction t| := h4 + _ ≤ |P.toExtended.bellmanFn P.toExtended.valueFunction t + - P.toExtended.bellmanFn Q.toExtended.valueFunction t| + + |P.toExtended.bellmanFn Q.toExtended.valueFunction t + - Q.toExtended.valueFunction t| := htri + _ ≤ P.discount * f t + D := add_le_add h2 h3 + have hC : f t ≤ D / (1 - P.discount) := by + rw [le_div_iff₀ (by linarith)] + nlinarith [hstep] + exact le_trans (hCle s hs) hC + +/-! ### The cutoff: actions worth keeping consume a bounded amount + +The pointwise comparison the sup-norm estimate needs is false at actions that consume almost +nothing, because `u` falls to `-∞` there and two nearby rates can give utilities an arbitrary +distance apart. Such actions are dominated: saving nothing is always feasible and is worth at +least `loBound v = u minIncome - β‖v‖`, which the framework already places below the +supremum (`le_maxE`). + +So it is enough to compare actions worth at least `loBound v`, and this section shows those +actions consume at least a fixed `δ > 0`. The `δ` depends on `u` and on `‖v‖` only — not on +the state and not on the interest rate, which is what the parametric argument needs. -/ + +theorem abs_expect_le (v : (ℝ × Z) →ᵇ ℝ) (p : (ℝ × Z) × ℝ) : + |P.toExtended.expect v p| ≤ ‖v‖ := by + calc |P.toExtended.expect v p| + ≤ ∑ z', |P.toExtended.prob z' p * v (P.toExtended.transition z' p)| := + Finset.abs_sum_le_sum_abs _ _ + _ ≤ ∑ z' : Z, P.toExtended.prob z' p * ‖v‖ := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.toExtended.prob_nonneg z' _)] + exact mul_le_mul_of_nonneg_left (v.norm_coe_le_norm _) (P.toExtended.prob_nonneg z' _) + _ = ‖v‖ := by rw [← Finset.sum_mul, P.toExtended.prob_sum, one_mul] + +/-- **Actions worth keeping consume a bounded amount**, for a cutoff `δ` given in advance. + +Separating the choice of `δ` from its use is what makes the bound uniform across interest +rates: the hypothesis on `δ` mentions only `u`, `loBound v` and the discount factor, none of +which move with the rate, so a single `δ` serves every rate. -/ +theorem le_consumption_of_cutoff (v : (ℝ × Z) →ᵇ ℝ) {δ : ℝ} + (hspec : ∀ c ∈ P.dom, c < δ → P.u c < P.toExtended.loBound v - P.discount * ‖v‖) + {s : ℝ × Z} (hs : s ∈ P.region) {a : ℝ} (ha : a ∈ P.toExtended.feasible s) + (hL : ((P.toExtended.loBound v : ℝ) : EReal) ≤ P.toExtended.objectiveE v s a) : + δ ≤ P.consumption s a := by + set R : ℝ := P.toExtended.loBound v - P.discount * ‖v‖ with hR + by_contra hlt + rw [not_le] at hlt + -- a reward of `⊥` would make the whole objective `⊥`, which is below the cutoff + have hne : P.toExtended.reward (s, a) ≠ ⊥ := by + intro hbot + rw [ExtendedStochasticProgram.objectiveE, hbot, EReal.bot_add] at hL + exact EReal.coe_ne_bot _ (le_bot_iff.mp hL) + have hc : P.consumption s a ∈ P.dom := P.consumption_mem_dom_of_ne_bot hs ha hne + -- so the reward is real, and the objective is a real inequality + have hrw := P.reward_eq_coe_dom hs ha hc + rw [ExtendedStochasticProgram.objectiveE, hrw, ← EReal.coe_add, EReal.coe_le_coe_iff] at hL + have hexp := P.abs_expect_le v (s, a) + rw [abs_le] at hexp + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hdisc : (P.toExtended.discount : ℝ) = (P.discount : ℝ) := rfl + rw [hdisc] at hL + have hge : R ≤ P.u (P.consumption s a) := by + rw [hR] + nlinarith [hexp.2, hL] + exact absurd (hspec _ hc hlt) (not_lt.mpr hge) + +/-- A cutoff exists, and its defining property mentions only data that does not move with +the interest rate. -/ +theorem exists_cutoff (hd : P.Unbounded) (v : (ℝ × Z) →ᵇ ℝ) : + ∃ δ > 0, ∀ c ∈ P.dom, c < δ → + P.u c < P.toExtended.loBound v - P.discount * ‖v‖ := by + set R : ℝ := P.toExtended.loBound v - P.discount * ‖v‖ with hR + have hev : ∀ᶠ c in 𝓝[>] (0 : ℝ), P.u c < R := P.tendsto_atBot_u hd (eventually_lt_atBot R) + obtain ⟨δ, hδ, hsub⟩ := (nhdsGT_basis (0 : ℝ)).eventually_iff.mp hev + refine ⟨δ, hδ, fun c hc hcδ => hsub ⟨?_, hcδ⟩⟩ + rw [hd] at hc + exact hc + +/-- **Actions worth keeping consume a bounded amount.** -/ +theorem exists_cutoff_consumption (hd : P.Unbounded) (v : (ℝ × Z) →ᵇ ℝ) : + ∃ δ > 0, ∀ s : ℝ × Z, s ∈ P.region → ∀ a ∈ P.toExtended.feasible s, + ((P.toExtended.loBound v : ℝ) : EReal) ≤ P.toExtended.objectiveE v s a → + δ ≤ P.consumption s a := by + obtain ⟨δ, hδ, hspec⟩ := P.exists_cutoff hd v + exact ⟨δ, hδ, fun s hs a ha hL => P.le_consumption_of_cutoff v hspec hs ha hL⟩ + +/-! ### Uniform continuity + +The two moduli the sup-norm estimate consumes. Both are uniform in the state, which is what +matters; neither is uniform in the interest rate, and neither needs to be, since the rate +enters only through the ARGUMENTS fed to `u` and to the continuation value, and those gaps +are already controlled by the cross-rate lemmas. + +Note `Z` carries only a discrete topology, with no metric, so `ℝ × Z` is not a metric space +and uniform continuity of the continuation value cannot be taken there directly. It is taken +in the asset coordinate for each income state separately and the finitely many moduli +combined, which is legitimate exactly because `Z` is a `Fintype`. -/ + +/-- **Uniform continuity of utility**, away from zero consumption. The interval is compact and +sits inside `Ioi 0`, where `u` is assumed continuous; the cutoff is what puts consumption +there. -/ +theorem exists_modulus_u {lo hi ε : ℝ} (hlo : 0 < lo) (hε : 0 < ε) : + ∃ η > 0, ∀ c₁ ∈ Icc lo hi, ∀ c₂ ∈ Icc lo hi, |c₁ - c₂| < η → |P.u c₁ - P.u c₂| < ε := by + have hsub : Icc lo hi ⊆ Ioi 0 := fun c hc => lt_of_lt_of_le hlo hc.1 + have hcont : ContinuousOn P.u (Icc lo hi) := P.continuousOn_u.mono hsub + have huc : UniformContinuousOn P.u (Icc lo hi) := + isCompact_Icc.uniformContinuousOn_of_continuous hcont + obtain ⟨η, hη, hspec⟩ := Metric.uniformContinuousOn_iff.mp huc ε hε + refine ⟨η, hη, fun c₁ h₁ c₂ h₂ hd => ?_⟩ + have := hspec c₁ h₁ c₂ h₂ (by rwa [Real.dist_eq]) + rwa [Real.dist_eq] at this + +omit [Fintype Z] [DiscreteTopology Z] in +/-- **Uniform continuity of the continuation value** in the asset coordinate, uniformly over +income states. The finiteness of `Z` is used in the PROOF, to combine the per-state moduli, +though it does not appear in the statement. -/ +theorem exists_modulus_v [Finite Z] (v : (ℝ × Z) →ᵇ ℝ) {ε : ℝ} (hε : 0 < ε) : + ∃ η > 0, ∀ z' : Z, ∀ a₁ ∈ Icc assetFloor assetCap, ∀ a₂ ∈ Icc assetFloor assetCap, + |a₁ - a₂| < η → |v (a₁, z') - v (a₂, z')| < ε := by + let _ := Fintype.ofFinite Z + -- one modulus per income state + have hslice : ∀ z' : Z, ∃ η > 0, ∀ a₁ ∈ Icc assetFloor assetCap, ∀ a₂ ∈ Icc assetFloor assetCap, + |a₁ - a₂| < η → |v (a₁, z') - v (a₂, z')| < ε := by + intro z' + have hcont : ContinuousOn (fun a : ℝ => v (a, z')) (Icc assetFloor assetCap) := + (v.continuous.comp (continuous_id.prodMk continuous_const)).continuousOn + have huc : UniformContinuousOn (fun a : ℝ => v (a, z')) (Icc assetFloor assetCap) := + isCompact_Icc.uniformContinuousOn_of_continuous hcont + obtain ⟨η, hη, hspec⟩ := Metric.uniformContinuousOn_iff.mp huc ε hε + refine ⟨η, hη, fun a₁ h₁ a₂ h₂ hd => ?_⟩ + have := hspec a₁ h₁ a₂ h₂ (by rwa [Real.dist_eq]) + rwa [Real.dist_eq] at this + choose η hη hspec using hslice + -- `Z` is finite, so the moduli have a positive minimum + have hne : (Finset.univ : Finset Z).Nonempty := ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + refine ⟨Finset.univ.inf' hne η, ?_, fun z' a₁ h₁ a₂ h₂ hd => ?_⟩ + · exact (Finset.lt_inf'_iff _).2 fun z' _ => hη z' + · exact hspec z' a₁ h₁ a₂ h₂ + (lt_of_lt_of_le hd (Finset.inf'_le _ (Finset.mem_univ z'))) + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean new file mode 100644 index 000000000..696465da6 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean @@ -0,0 +1,216 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.MinimalMPC +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateStep +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationHARA + + +/-! +# Light's Theorem 1 at large `β` + +`policy_mono_withRate_hara` — a household facing a higher rate saves at least as much — needs +the saving cap slack along BOTH iterations, and in particular for the higher-rate economy +choosing against the lower-rate economy's iterates. `Calibrated` supplies that from an oscillation +bound uniform in the rate, which at Aiyagari's `β = 0.96` is off by orders of magnitude. + +This file supplies it from the minimal MPC instead, and the price is that the two rates have to +be CLOSE: within `1 - Þ₁` of each other, where `Þ₁ = (βR₁)^(1/γ)` is the patience factor at the +lower rate. That is not a defect of the estimate but of the comparison — a household facing a +much higher rate against an old continuation can save more than its own cap — and it costs +nothing, because monotonicity on overlapping short intervals is monotonicity on their union +(`monotoneOn_of_local`). + +## The hybrid bound + +Economy 2 choosing against economy 1's iterate `v` at assets `a` is economy 1 choosing against +`v` at `t a`, `t = R₁/R₂ ≤ 1` (`consumptionFnOf_withRate`). Consumption rises with assets, so +economy 2's consumption at `a` is at least economy 1's at `a` itself, which the minimal MPC bounds +by `κ₁ (y + R₁ a)`. Saving is then at most `(1-κ₁) y + (R₂ - κ₁ R₁) a`, and `R₂ - κ₁ R₁ = Þ₁ + +(r₂ - r₁)`: the coefficient on assets exceeds `Þ₁` by exactly the rate gap. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +/-! ### Local monotonicity is monotonicity -/ + +/-- **Monotone on short steps, monotone on the interval.** If `f x ≤ f y` whenever `x ≤ y` are at +most `ε` apart, then `f` is monotone on the interval: walk from `x` to `y` in steps of `ε`. -/ +theorem monotoneOn_of_local {f : ℝ → ℝ} {lo hi ε : ℝ} (hε : 0 < ε) + (h : ∀ x ∈ Icc lo hi, ∀ y ∈ Icc lo hi, x ≤ y → y - x ≤ ε → f x ≤ f y) : + MonotoneOn f (Icc lo hi) := by + have key : ∀ n : ℕ, ∀ x ∈ Icc lo hi, ∀ y ∈ Icc lo hi, x ≤ y → y ≤ x + n * ε → f x ≤ f y := by + intro n + induction n with + | zero => + intro x hx y hy hxy hle + simp only [Nat.cast_zero, zero_mul, add_zero] at hle + exact h x hx y hy hxy (by linarith) + | succ k ih => + intro x hx y hy hxy hle + by_cases hshort : y - x ≤ ε + · exact h x hx y hy hxy hshort + · push Not at hshort + set m : ℝ := y - ε with hm + have hxm : x ≤ m := by rw [hm]; linarith + have hmy : m ≤ y := by rw [hm]; linarith + have hmmem : m ∈ Icc lo hi := ⟨le_trans hx.1 hxm, le_trans hmy hy.2⟩ + have hmle : m ≤ x + k * ε := by + rw [hm]; push_cast at hle; linarith + exact le_trans (ih x hx m hmmem hxm hmle) (h m hmmem y hy hmy (by rw [hm]; linarith)) + intro x hx y hy hxy + obtain ⟨n, hn⟩ := exists_nat_ge ((y - x) / ε) + refine key n x hx y hy hxy ?_ + have := (div_le_iff₀ hε).mp hn + linarith + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] + +section AnyFloor + +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- Every iterate from `0` has concave slices. -/ +theorem concaveSlices_iterate_zero (n : ℕ) : + ConcaveSlices assetFloor assetCap ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact P.concaveSlices_bellman ih + +end AnyFloor + +variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) + +/-! ### The hybrid cap slack -/ + +/-- **Cap slack for the higher-rate economy against the lower-rate economy's iterates**, from the +minimal MPC at the lower rate. The condition is one inequality in which the rate gap appears +explicitly. -/ +theorem crra_policyOf_withRate_lt_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hκ₁ : 0 < (P.withRate r₁ h₁).minMPC γ) + (hβ : 0 < (P.discount : ℝ)) (hβR₁ : (P.discount : ℝ) * (1 + r₁) < 1) + (hpos₁ : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < + (P.withRate r₁ h₁).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hthr₁ : (1 - (P.withRate r₁ h₁).minMPC γ) * (P.maxIncome + (1 + r₁) * assetCap) < assetCap) + (hgap : (1 - (P.withRate r₁ h₁).minMPC γ) * P.maxIncome + + ((1 - (P.withRate r₁ h₁).minMPC γ) * (1 + r₁) + (r₂ - r₁)) * assetCap < assetCap) + (n : ℕ) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap := by + set Q₁ := P.withRate r₁ h₁ with hQ₁ + set Q₂ := P.withRate r₂ h₂ with hQ₂ + set v : (ℝ × Z) →ᵇ ℝ := (Q₁.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ) with hv + set κ : ℝ := Q₁.minMPC γ with hκ + have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 + have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 + have hslices : ConcaveSlices (0 : ℝ) assetCap v := Q₁.concaveSlices_iterate_zero n + have hκ1 : κ ≤ 1 := minMPC_le_one (P := Q₁) hγ0 hβ + -- economy 1's minimal-MPC bound at this iterate + have hmpc : κ * Q₁.resources (a, z) ≤ Q₁.consumptionFnOf v z a := + (Q₁.crra_minMPC_of_cap hγ0 (show Q₁.u = crraUtility γ from hu) rfl + hκ₁ hβ hβR₁ hpos₁ hthr₁ n).1 z a ha + -- the rescale, and monotonicity of consumption in assets + set t : ℝ := (1 + r₁) / (1 + r₂) with ht + have ht0 : 0 < t := div_pos hR₁ hR₂ + have ht1 : t ≤ 1 := by rw [ht, div_le_one hR₂]; linarith + have hta : t * a ∈ Icc (0 : ℝ) assetCap := + ⟨mul_nonneg ht0.le ha.1, le_trans (by nlinarith [ha.1, ha.2]) ha.2⟩ + have htale : t * a ≤ a := by nlinarith [ha.1] + have hrescale : Q₁.consumptionFnOf v z a = Q₂.consumptionFnOf v z (t * a) := + P.consumptionFnOf_withRate h₁ h₂ hr hslices z ha + have hmono : Q₂.consumptionFnOf v z (t * a) ≤ Q₂.consumptionFnOf v z a := + Q₂.consumptionFnOf_mono hslices hta ha htale + -- so economy 2 consumes at least `κ (y + R₁ a)` here + have hres₁ : Q₁.resources (a, z) = P.income z + (1 + r₁) * a := by + simp only [hQ₁, resources, withRate_income, withRate_interest, max_eq_right ha.1] + have hres₂ : Q₂.resources (a, z) = P.income z + (1 + r₂) * a := by + simp only [hQ₂, resources, withRate_income, withRate_interest, max_eq_right ha.1] + have hpol : Q₂.policyOf v (a, z) = Q₂.resources (a, z) - Q₂.consumptionFnOf v z a := by + simp only [consumptionFnOf, consumption]; ring + have hy : P.income z ≤ P.maxIncome := P.le_maxIncome z + have hcoef : (0 : ℝ) ≤ (1 - κ) * (1 + r₁) + (r₂ - r₁) := by nlinarith + rw [hpol, hres₂] + -- saving ≤ (1-κ) y + ((1-κ)(1+r₁) + (r₂-r₁)) a, and that is < cap at a = cap + have hbound : P.income z + (1 + r₂) * a - Q₂.consumptionFnOf v z a + ≤ (1 - κ) * P.income z + ((1 - κ) * (1 + r₁) + (r₂ - r₁)) * a := by + rw [hres₁] at hmpc + nlinarith [hmpc, hrescale, hmono] + have hmax : (1 - κ) * P.income z + ((1 - κ) * (1 + r₁) + (r₂ - r₁)) * a + ≤ (1 - κ) * P.maxIncome + ((1 - κ) * (1 + r₁) + (r₂ - r₁)) * assetCap := by + have h1 : (1 - κ) * P.income z ≤ (1 - κ) * P.maxIncome := + mul_le_mul_of_nonneg_left hy (by linarith) + have h2 : ((1 - κ) * (1 + r₁) + (r₂ - r₁)) * a + ≤ ((1 - κ) * (1 + r₁) + (r₂ - r₁)) * assetCap := + mul_le_mul_of_nonneg_left ha.2 hcoef + linarith + linarith [hbound, hmax, hgap] + +/-! ### Light's Theorem 1, locally -/ + +/-- **A household facing a slightly higher rate saves at least as much**, for CRRA with `γ ≤ 1`, +with every cap-slack hypothesis discharged from the minimal MPC. Positivity against arbitrary +concave continuations is the one hypothesis left, and it is free for CRRA (Inada). -/ +theorem crra_policy_mono_withRate_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) + (hu : P.u = crraUtility γ) + {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + (hκ₁ : 0 < (P.withRate r₁ h₁).minMPC γ) (hκ₂ : 0 < (P.withRate r₂ h₂).minMPC γ) + (hβ : 0 < (P.discount : ℝ)) (hβR₂ : (P.discount : ℝ) * (1 + r₂) < 1) + (hpc₁ : (P.withRate r₁ h₁).PositiveConsumptionAll) + (hpc₂ : (P.withRate r₂ h₂).PositiveConsumptionAll) + (hthr₁ : (1 - (P.withRate r₁ h₁).minMPC γ) * (P.maxIncome + (1 + r₁) * assetCap) < assetCap) + (hthr₂ : (1 - (P.withRate r₂ h₂).minMPC γ) * (P.maxIncome + (1 + r₂) * assetCap) < assetCap) + (hgap : (1 - (P.withRate r₁ h₁).minMPC γ) * P.maxIncome + + ((1 - (P.withRate r₁ h₁).minMPC γ) * (1 + r₁) + (r₂ - r₁)) * assetCap < assetCap) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := by + have hβR₁ : (P.discount : ℝ) * (1 + r₁) < 1 := by nlinarith [hβ] + -- positivity at the iterates, read in either economy + have hpos₁ : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < + (P.withRate r₁ h₁).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc₁ _ ((P.withRate r₁ h₁).concaveSlices_iterate_zero n) z a ha + have hpos₂ : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc₂ _ ((P.withRate r₂ h₂).concaveSlices_iterate_zero n) z a ha + have hposv : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < + (P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc₂ _ ((P.withRate r₁ h₁).concaveSlices_iterate_zero n) z a ha + -- cap slack along economy 2's own iteration, from its minimal MPC + have hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap := + fun n a ha z => ((P.withRate r₂ h₂).crra_minMPC_of_cap hγ0 hu rfl + hκ₂ hβ hβR₂ hpos₂ hthr₂ n).2 a ha z + -- Carroll--Kimball along economy 2's iteration + have hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) + ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z) := + (P.withRate r₂ h₂).concaveOn_consumptionFnOf_iterates_of_hara hγ0 le_rfl hβ + (by rw [haraUtility_zero_shift]; exact hu) hpos₂ + (fun n a ha z => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hpos₂ n z a ha) + (hslackw n a ha z)) + exact P.policy_mono_withRate_hara hγ0 hγ1 le_rfl (by rw [haraUtility_zero_shift]; exact hu) + h₁ h₂ hr (fun n a ha z => hposv n z a ha) (fun n a ha z => hpos₂ n z a ha) + (fun n a ha z => P.crra_policyOf_withRate_lt_cap hγ0 hu h₁ h₂ hr hκ₁ hβ hβR₁ hpos₁ hthr₁ + hgap n ha z) + hslackw hcons ha z + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean new file mode 100644 index 000000000..45f2e8552 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean @@ -0,0 +1,218 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.LargeBetaLight +public import LeanPool.AiyagariEquilibrium.Models.EulerCorner +public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness +public import LeanPool.AiyagariEquilibrium.Models.MonotoneTransitions + + +/-! +# The Doeblin chain at large `β` + +At each rate, the uniqueness of the stationary distribution and the convergence to it come from +the same three facts as always — a corner at the worst income state, decline above it, and the +worst state reachable from everywhere — but at Aiyagari's `β = 0.96` each has to come from a +route that does not carry `1/(1-β)` or `1/(1-βR)` in a constant. + +* The corner from the Euler inequality (`crra_policy_eq_zero_of_euler`), whose test is + `βR · minIncome^(-γ) < (income z₀ + R a₀)^(-γ)` — one inequality, no Lipschitz constant. +* Decline from Açıkgöz Proposition 4 (`policy_lt_self_of_impatient_iid`), which holds at every + asset level above the constraint given `βR < 1` and iid income. +* Cap slack at the fixed point from the minimal MPC (`crra_policy_lt_cap_of_minMPC`). + +Nothing in this file mentions an oscillation bound. +-/ + +@[expose] public section + +open Set Filter Topology MeasureTheory BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **The exhaustion data at large `β`**, for CRRA, given that consumption is lowest at `z₀`. +The two ways of supplying that are iid income (`crra_exists_exhaust_of_euler_corner`) and +monotone transitions (`crra_exists_exhaust_of_euler_corner_monotone`). -/ +theorem crra_exists_exhaust_of_euler_corner_of_lowest {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} + (hlow : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.consumptionFn z₀ a ≤ P.consumptionFn z a) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + -- positivity, at the iterates and at the fixed point + have hposIt : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc _ (P.concaveSlices_iterate_zero n) z a ha + have hpos : ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFn z x := + fun z x hx => hpc _ P.concaveSlices_valueFunction z x hx + -- cap slack at the fixed point, from the minimal MPC + have hslack : ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, P.policy (x, z) < P.maxSaving (x, z) := + fun z x hx => P.policyOf_lt_maxSaving_of_pos (hpos z x hx) + (P.crra_policy_lt_cap_of_minMPC hγ0 hu rfl hκ hβ hβR hposIt hthr hx z) + -- the corner below `a₀`, from the Euler inequality + have hzero : ∀ a ∈ Icc (0 : ℝ) a₀, P.policy (a, z₀) = 0 := by + intro a ha + have hmem : a ∈ Icc (0 : ℝ) assetCap := ⟨ha.1, le_trans ha.2 hle⟩ + refine P.crra_policy_eq_zero_of_euler hγ0 hu rfl hβR hpos hslack hmem z₀ ?_ + have hres : P.resources (a, z₀) = P.income z₀ + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + have hres0 : 0 < P.income z₀ + (1 + P.interest) * a := by + have := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z₀) + nlinarith [ha.1] + have hle' : P.income z₀ + (1 + P.interest) * a ≤ P.income z₀ + (1 + P.interest) * a₀ := by + nlinarith [ha.2] + rw [hres] + exact lt_of_lt_of_le hcorner (rpow_neg_antitone hγ0 hres0 hle') + -- and decline above it, from impatience at the lowest-consumption state + refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ + have hamem : a ∈ Icc (0 : ℝ) assetCap := ⟨le_trans ha₀.le ha.1, ha.2⟩ + exact P.policy_lt_self_of_impatient hβR (du := fun c => c ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_crraUtility γ hc) + (fun _ hx _ _ hxy => Real.rpow_le_rpow_of_nonpos hx hxy (by linarith)) + (fun c hc => Real.rpow_pos_of_pos hc _) hpos hslack hamem (lt_of_lt_of_le ha₀ ha.1) + (fun z' => hlow a hamem z') + +/-- **The exhaustion data with iid income.** -/ +theorem crra_exists_exhaust_of_euler_corner {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) + (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + subst hfl + exact P.crra_exists_exhaust_of_euler_corner_of_lowest hγ0 hu rfl hκ hβ hβR hpc hthr + (fun a ha z => P.consumptionFn_le_of_income_le hiid (hz₀ z) ha) ha₀ hle hcorner + +/-- **The exhaustion data with persistent income**: monotone transitions in place of iid, via +Huggett's Lemma 1 (`consumptionFn_le_of_income_le_monotone`). The extra iterate-level +hypotheses that lemma needs are supplied by the minimal MPC. -/ +theorem crra_exists_exhaust_of_euler_corner_monotone {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hmono : P.MonotoneTransitions) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + subst hfl + have hposIt : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := + fun n z a ha => hpc _ (P.concaveSlices_iterate_zero n) z a ha + have hslackIt : ∀ n : ℕ, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap := + fun n x hx z => (P.crra_minMPC_of_cap hγ0 hu rfl hκ hβ hβR hposIt hthr n).2 x hx z + refine P.crra_exists_exhaust_of_euler_corner_of_lowest hγ0 hu rfl hκ hβ hβR hpc hthr + (fun a ha z => ?_) ha₀ hle hcorner + exact P.consumptionFn_le_of_income_le_monotone hmono (du := fun c => c ^ (-γ)) + (fun c hc => by rw [hu]; exact hasDerivAt_crraUtility γ hc) + (fun _ hx _ _ hxy => Real.rpow_le_rpow_of_nonpos hx hxy (by linarith)) + hpc hslackIt (hz₀ z) ha + +section Measure + +variable [MeasurableSpace Z] [BorelSpace Z] + +/-- **A unique stationary distribution at large `β`.** -/ +theorem crra_existsUnique_isStationary_of_euler_corner {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) : + ∃! μ : ProbabilityMeasure P.State, P.IsStationary μ := by + obtain ⟨N, hN⟩ := P.crra_exists_exhaust_of_euler_corner hγ0 hu hfl hκ hβ hβR hiid hpc hthr + hz₀ ha₀ hle hcorner + exact P.existsUnique_isStationary hreach hN + +/-- **Convergence to it from any start**, Doeblin with the atom at the constraint. -/ +theorem crra_tendsto_pushProb_of_euler_corner {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) + (μ₀ : ProbabilityMeasure P.State) {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) : + Tendsto (fun m => P.pushProb^[m] μ₀) atTop (𝓝 μ) := by + classical + obtain ⟨N, hN⟩ := P.crra_exists_exhaust_of_euler_corner hγ0 hu hfl hκ hβ hβR hiid hpc hthr + hz₀ ha₀ hle hcorner + obtain ⟨z₁, -, hmin⟩ := Finset.exists_min_image Finset.univ + (fun z => P.transitionMatrix z z₀) ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + exact P.tendsto_pushProb_iterate (z₀ := z₀) (p₀ := P.transitionMatrix z₁ z₀) (N := N + 1) + (hreach z₁) (fun s => hmin _ (Finset.mem_univ _)) (P.badStep_iterate_eq hN) μ₀ hμ + +/-- **A unique stationary distribution at large `β`, with persistent income.** -/ +theorem crra_existsUnique_isStationary_of_euler_corner_monotone {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hmono : P.MonotoneTransitions) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) : + ∃! μ : ProbabilityMeasure P.State, P.IsStationary μ := by + obtain ⟨N, hN⟩ := P.crra_exists_exhaust_of_euler_corner_monotone hγ0 hu hfl hκ hβ hβR hmono + hpc hthr hz₀ ha₀ hle hcorner + exact P.existsUnique_isStationary hreach hN + +/-- **Convergence to it from any start, with persistent income.** -/ +theorem crra_tendsto_pushProb_of_euler_corner_monotone {γ : ℝ} (hγ0 : 0 < γ) + (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) + (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hmono : P.MonotoneTransitions) + (hpc : P.PositiveConsumptionAll) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hcorner : (P.discount : ℝ) * (1 + P.interest) * P.minIncome ^ (-γ) + < (P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) + (μ₀ : ProbabilityMeasure P.State) {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) : + Tendsto (fun m => P.pushProb^[m] μ₀) atTop (𝓝 μ) := by + classical + obtain ⟨N, hN⟩ := P.crra_exists_exhaust_of_euler_corner_monotone hγ0 hu hfl hκ hβ hβR hmono + hpc hthr hz₀ ha₀ hle hcorner + obtain ⟨z₁, -, hmin⟩ := Finset.exists_min_image Finset.univ + (fun z => P.transitionMatrix z z₀) ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + exact P.tendsto_pushProb_iterate (z₀ := z₀) (p₀ := P.transitionMatrix z₁ z₀) (N := N + 1) + (hreach z₁) (fun s => hmin _ (Finset.mem_univ _)) (P.badStep_iterate_eq hN) μ₀ hμ + +end Measure + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean new file mode 100644 index 000000000..df93109b2 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -0,0 +1,687 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate +public import LeanPool.AiyagariEquilibrium.Analysis.HARA + + +/-! +# The minimal marginal propensity to consume + +Carroll and Shanker (2026), Lemma 1, bound the consumption SHARE of cash on hand between two +constants — the maximal and minimal marginal propensities to consume. This file proves the +minimal one, + + `κ · m ≤ c(m)`, `κ = 1 - Þ/R`, `Þ = (βR)^(1/γ)`, + +for CRRA utility at a zero borrowing limit. `Þ` is Carroll's absolute patience factor, and `κ` +is the MPC of a perfect-foresight consumer with no human wealth. + +## Why this one and not the other + +The MAXIMAL MPC is `1 - ℘^(1/γ)Þ/R`, where `℘` is the probability of a zero-income event. This +model has `minIncome_pos`, so `℘ = 0`, the maximal MPC is `1`, and the bound it gives — +`c(m) ≤ m` — is the budget constraint and nothing more. That is not a defect of the estimate: a +liquidity-constrained household at the borrowing limit really does consume everything, and our +own corner condition says so. So only the minimal MPC has content here. + +## The proof is one Euler step, and the bound is self-reproducing + +Suppose the continuation's consumption function already satisfies `c ≥ κ m`. At an interior +choice `A > 0` the Euler inequality `u'(c) ≤ βR E[u'(c'(m'))]` applies, and `m' ≥ R A` because +income is positive, so + + `c^(-γ) ≤ βR (κ R A)^(-γ)`, hence `c ≥ (κR/Þ) A = (κR/Þ)(m - c)`, + +and `(κR/Þ)/(1 + κR/Þ) = κ` precisely because `1 - κ = Þ/R`. At the corner `A = 0` the household +consumes everything, so `c = m ≥ κ m` outright. The bound therefore survives the Bellman +operator, and holds at the zero continuation because the household with no future eats +everything. + +No limit of a sequence of MPCs is taken: `κ` is a fixed point of the recursion +`κ⁻¹ ↦ 1 + (Þ/R)κ⁻¹`, so the single constant reproduces itself. + +## The sign of the rate + +Everything below is stated with `0 < κ` as the hypothesis, not `0 ≤ r`. Return impatience +`βR < 1` at a non-negative rate gives `0 < κ` (`minMPC_pos`), but at `γ = 1` one has +`κ = 1 - β` (`minMPC_one`) at EVERY rate `r > -1`, so the log bounds — and with them the +capital ceiling — run into negative rates, where Aiyagari's `K(r)` has to be excluded too. + +## What it is for + +Two consequences the development already had homes for. `policy_lt_self_of_consumption_lower_bound` +turns it into the DECLINE condition, which was a raw assumption; and +`aggregateCapital_le_of_consumption_bound` turns it into a quantitative ceiling on capital +supply, in place of the asset cap. Both conditions ask for `(1-κ)R < 1`, and `(1-κ)R = Þ`, so +both are exactly `βR < 1`. +-/ + +@[expose] public section + +open Set Finset Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-- **The minimal marginal propensity to consume**, `κ = 1 - Þ/R` with `Þ = (βR)^(1/γ)`. -/ +noncomputable def minMPC (γ : ℝ) : ℝ := + 1 - ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) / (1 + P.interest) + +variable {P} + +/-- The absolute patience factor is positive when the household discounts at all. -/ +theorem patience_pos {γ : ℝ} (_hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) : + 0 < ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) := + Real.rpow_pos_of_pos (mul_pos hβ P.interest_gt_neg_one) _ + +/-- **Impatience is exactly `Þ < 1`.** -/ +theorem patience_lt_one {γ : ℝ} (hγ0 : 0 < γ) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) : + ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) < 1 := by + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + exact Real.rpow_lt_one (by positivity) hβR (by positivity) + +/-- `(1 - κ)R = Þ`: the factor by which assets grow under the bound is the patience factor. -/ +theorem one_sub_minMPC_mul {γ : ℝ} : + (1 - P.minMPC γ) * (1 + P.interest) + = ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) := by + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + simp only [minMPC] + field_simp + ring + +theorem minMPC_le_one {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) : + P.minMPC γ ≤ 1 := by + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have := patience_pos (P := P) hγ0 hβ + simp only [minMPC] + have : (0 : ℝ) < ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) / (1 + P.interest) := by + positivity + linarith + +/-- **At `γ = 1` the patience factor is `βR` itself**, so `κ = 1 - β`: log and log Stone--Geary +need no root at all. -/ +theorem minMPC_one : P.minMPC 1 = 1 - (P.discount : ℝ) := by + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + simp only [minMPC, div_one, Real.rpow_one] + rw [mul_div_assoc, div_self hR.ne', mul_one] + +/-- **The minimal MPC is positive** exactly when the household is impatient and the rate is +non-negative — Carroll's return impatience, which `βR < 1` implies once `R ≥ 1`. -/ +theorem minMPC_pos {γ : ℝ} (hγ0 : 0 < γ) (hint : 0 ≤ P.interest) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) : 0 < P.minMPC γ := by + have hR : (1 : ℝ) ≤ 1 + P.interest := by linarith + have hT := patience_lt_one (P := P) hγ0 hβR + simp only [minMPC, sub_pos] + rw [div_lt_one (by linarith)] + linarith + +/-- Negative powers reverse the order: a smaller `x ^ (-γ)` means a larger `x`. -/ +theorem le_of_rpow_neg_le {x y γ : ℝ} (hx : 0 < x) (hy : 0 < y) (hγ : 0 < γ) + (h : x ^ (-γ) ≤ y ^ (-γ)) : y ≤ x := by + have hxp : (0 : ℝ) < x ^ γ := Real.rpow_pos_of_pos hx _ + have hyp : (0 : ℝ) < y ^ γ := Real.rpow_pos_of_pos hy _ + rw [Real.rpow_neg hx.le, Real.rpow_neg hy.le, inv_le_inv₀ hxp hyp] at h + by_contra hcon + exact absurd h (not_le.mpr (Real.rpow_lt_rpow hx.le (not_le.mp hcon) hγ)) + +/-! ### The bound along the iteration -/ + +/-- **One Euler step of the minimal-MPC bound.** If a continuation `v` satisfies +`κ m ≤ c_v(m)` everywhere, with consumption positive and the cap slack against it, then so does +`bellman v`. Extracted from the induction so that it can also drive a SIMULTANEOUS induction on +cap slack (`crra_minMPC_of_cap`), which is what large `β` needs. -/ +theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) {v : (ℝ × Z) →ᵇ ℝ} + (hposv : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf v z a) + (hcapv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf v (a, z) < assetCap) + (hposW : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (ih : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf v z a) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf (P.toExtended.bellman v) z a := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef + have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ + have hκ0 : 0 < P.minMPC γ := hκ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) + have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by + rw [hTdef, ← Real.rpow_mul hβR0.le, + show (1 / γ) * (-γ) = -1 from by field_simp, Real.rpow_neg_one] + set W : (ℝ × Z) →ᵇ ℝ := P.toExtended.bellman v with hWdef + set A : ℝ := P.policyOf W (a, z) with hAdef + have hAmem : A ∈ Icc (0 : ℝ) assetCap := P.feasible_subset_region (P.policyOf_mem W (a, z)) + set c : ℝ := P.consumptionFnOf W z a with hcdef + have hcA : c = P.resources (a, z) - A := rfl + have hc0 : 0 < c := hposW z a ha + rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 + · nlinarith [hκ1, hc0, hcA, hA0] + · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => + P.policyOf_lt_maxSaving_of_pos (hposv z' A hAmem) (hcapv A hAmem z') + have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hposv z' A hAmem + have hder : ∀ z' : Z, HasDerivAt P.u ((P.consumptionFnOf v z' A) ^ (-γ)) + (P.consumptionFnOf v z' A) := by + intro z'; rw [hu]; exact hasDerivAt_crraUtility γ (hc' z') + have heuler := P.euler_ge (v := v) (z := z) (a := a) (A := A) ha rfl hA0 hslack hc0 + (by rw [hu]; exact hasDerivAt_crraUtility γ hc0) hc' hder + set X : ℝ := P.minMPC γ * ((1 + P.interest) * A) with hXdef + have hX0 : 0 < X := by rw [hXdef]; positivity + have hstep : ∀ z' : Z, X ≤ P.consumptionFnOf v z' A := by + intro z' + refine le_trans ?_ (ih z' A hAmem) + have hres : P.resources (A, z') = P.income z' + (1 + P.interest) * A := by + simp only [resources, max_eq_right hAmem.1] + have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') + rw [hres, hXdef] + nlinarith [hκ0] + have hsum : ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) + ≤ X ^ (-γ) := by + calc ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) + ≤ ∑ z' : Z, P.transitionMatrix z z' * X ^ (-γ) := + Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (rpow_neg_antitone hγ0 hX0 (hstep z')) + (P.transitionMatrix_nonneg z z') + _ = X ^ (-γ) := by rw [← Finset.sum_mul, P.transitionMatrix_sum z, one_mul] + have hchain : c ^ (-γ) ≤ (P.discount : ℝ) * (1 + P.interest) * X ^ (-γ) := by + have := le_trans heuler + (mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hsum hR.le) hβ.le) + linarith [this] + have hTc : (T * c) ^ (-γ) ≤ X ^ (-γ) := by + rw [Real.mul_rpow hT0.le hc0.le, hTneg, inv_mul_le_iff₀ hβR0] + linarith [hchain] + have hXTc : X ≤ T * c := le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc + rw [hXdef] at hXTc + nlinarith [hXTc, hkey, hR, hcA] + +/-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, +uniformly in the horizon — so it passes to the value function. + +The hypotheses beyond CRRA are the two the Euler inequality needs and nothing else: consumption +positive at each iterate, and the saving cap slack there. Both are fields (or immediate +consequences of fields) of `Calibrated`. -/ +theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : + ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) + ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef + have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ + have hκ0 : 0 < P.minMPC γ := hκ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) + -- `Þ ^ (-γ) = 1/(βR)`, the one rpow identity the step needs + have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by + rw [hTdef, ← Real.rpow_mul hβR0.le, + show (1 / γ) * (-γ) = -1 from by field_simp, Real.rpow_neg_one] + intro n + induction n with + | zero => + intro z a ha + have hz : P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z) = 0 := P.policyOf_zero (s := (a, z)) ha + have hres : (0 : ℝ) ≤ P.resources (a, z) := (P.assetFloor_lt_resources (a, z)).le + have heq : P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a + = P.resources (a, z) := by + change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) + rw [hz]; simp [consumption] + rw [heq] + nlinarith [hres, hκ1] + | succ k ih => + intro z a ha + have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) + = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := + Function.iterate_succ_apply' _ _ _ + rw [hiter] + exact P.crra_minMPC_step hγ0 hu rfl hκ hβ hβR (hpos k) (hcap k) + (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ih z ha + +/-! ### At the fixed point, and what it buys + +The bound is uniform in the horizon, so it survives the limit: `tendsto_policyOf` says argmaxes +follow uniform limits when the limit has concave slices, which the value function does. -/ + +/-- **The minimal-MPC bound at the value function.** -/ +theorem crra_minMPC_mul_le_consumptionFn {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := by + have hiter := P.crra_minMPC_mul_le_consumptionFnOf hγ0 hu hfl hκ hβ hβR hpos hcap + have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha + have htend : Tendsto + (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop + (𝓝 (P.policy (a, z))) := + P.tendsto_policyOf (P.toExtended.tendsto_iterate_valueFunction 0) + P.concaveSlices_valueFunction hmem + have htendc : Tendsto + (fun n => P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (P.consumptionFn z a)) := by + have hcf : P.consumptionFn z a = P.resources (a, z) - P.policy (a, z) := rfl + rw [hcf] + simpa only [consumptionFnOf, consumption] using (tendsto_const_nhds (x := P.resources (a, z)) + (f := atTop (α := ℕ))).sub htend + exact ge_of_tendsto htendc (Filter.Eventually.of_forall fun n => hiter n z a ha) + +/-- **The decline condition, from primitives.** Above an explicit asset level the household runs +its assets down, because it consumes at least the share `κ` of cash on hand and +`(1-κ)R = Þ < 1`. This is the hypothesis `hdecl` of `exists_exhaust_of_decline`, which the +development has carried as an assumption. -/ +theorem crra_policy_lt_self_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) + (hgt : (1 - P.minMPC γ) * P.income z + < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a) : + P.policy (a, z) < a := by + subst hfl + refine P.policy_lt_self_of_consumption_lower_bound (ε := P.minMPC γ) ha ?_ (by linarith) + simpa using P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hβR hpos hcap z ha + +/-! ### Cap slack from the bound itself + +The induction above ASSUMES the cap is slack at every iterate. At small `β` that is supplied by +an oscillation bound; at Aiyagari's `β = 0.96` no such bound is available. But the minimal-MPC +bound itself implies cap slack — saving is at most `(1-κ) m`, and `(1-κ)(maxIncome + R·cap) +< cap` once the cap is large enough — so the two can be proved TOGETHER, one induction carrying +both. The cap is then bookkeeping again: any `cap > (1-κ)maxIncome / (1 - (1-κ)R)` will do, and +`1 - (1-κ)R = 1 - Þ > 0`. -/ + +/-- Saving under the minimal-MPC bound is at most `(1-κ) m`, hence below the cap once the cap +clears `(1-κ)(maxIncome + R·cap)`. -/ +theorem crra_policyOf_lt_cap_of_minMPC_bound {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) + (hfl : assetFloor = 0) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf v z a) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policyOf v (a, z) < assetCap := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right ha.1] + have hbd : P.income z + (1 + P.interest) * a ≤ P.maxIncome + (1 + P.interest) * assetCap := by + have := P.le_maxIncome z + nlinarith [ha.2, hR] + have hpol : P.policyOf v (a, z) = P.resources (a, z) - P.consumptionFnOf v z a := by + simp only [consumptionFnOf, consumption]; ring + have hmpc := hv z a ha + have hmono : (1 - P.minMPC γ) * P.resources (a, z) + ≤ (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) := by + rw [hres]; exact mul_le_mul_of_nonneg_left hbd (by linarith) + rw [hpol] + linarith + +/-- **The minimal-MPC bound and cap slack, by one induction.** Nothing is assumed about the cap +beyond one inequality among the primitives. -/ +theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) : + ∀ n : ℕ, + (∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) + ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + ∧ (∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) := by + intro n + induction n with + | zero => + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hm : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) + ≤ P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + intro z a ha + subst hfl + have hz : P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z) = 0 := P.policyOf_zero (s := (a, z)) ha + have hres : (0 : ℝ) ≤ P.resources (a, z) := (P.assetFloor_lt_resources (a, z)).le + have heq : P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a + = P.resources (a, z) := by + change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) + rw [hz]; simp [consumption] + rw [heq] + nlinarith [hres, hκ1] + exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ + | succ k ih => + obtain ⟨ihm, ihc⟩ := ih + have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) + = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := + Function.iterate_succ_apply' _ _ _ + have hm : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) + ≤ P.consumptionFnOf ((P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + intro z a ha + rw [hiter] + exact P.crra_minMPC_step hγ0 hu hfl hκ hβ hβR (hpos k) ihc + (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ihm z ha + exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ + +/-- **The minimal-MPC bound at the value function, with the cap from primitives.** -/ +theorem crra_minMPC_mul_le_consumptionFn_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := + P.crra_minMPC_mul_le_consumptionFn hγ0 hu hfl hκ hβ hβR hpos + (fun n => (P.crra_minMPC_of_cap hγ0 hu hfl hκ hβ hβR hpos hthr n).2) z ha + +/-- **The cap is slack at the fixed point**, from primitives. -/ +theorem crra_policy_lt_cap_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) < assetCap := + P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr + (fun z' _b hb => + P.crra_minMPC_mul_le_consumptionFn_of_cap hγ0 hu hfl hκ hβ hβR hpos hthr z' hb) + ha z + +/-! ### The shifted-CRRA version + +With `u'(c) = (η + c)^(-γ)` the same argument runs on `η + c` against `η + m`: + + `κ (η + m) ≤ η + c(m)`, + +with the SAME `κ`. The step produces `c ≥ κ m + η(κ - Þ)/R`, and what has to be checked is that +this is at least `κ m - η(1-κ)`, which holds because `κ ≥ 0`. So the subsistence level costs +nothing in the constant; it only shifts what the constant multiplies. + +Read as a bound on saving this is `policy ≤ (1-κ)(η + m)`, so the household behaves like a CRRA +household with income raised by `η` — which is what a subsistence level is. -/ + +/-- **The minimal-MPC bound for shifted CRRA, at every iterate.** -/ +theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : + ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * (η + P.resources (a, z)) + ≤ η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR + set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef + have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ + have hκ0 : 0 < P.minMPC γ := hκ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) + have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by + rw [hTdef, ← Real.rpow_mul hβR0.le, + show (1 / γ) * (-γ) = -1 from by field_simp, Real.rpow_neg_one] + intro n + induction n with + | zero => + intro z a ha + have hz : P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z) = 0 := P.policyOf_zero (s := (a, z)) ha + have hres : (0 : ℝ) ≤ P.resources (a, z) := (P.assetFloor_lt_resources (a, z)).le + have heq : P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a + = P.resources (a, z) := by + change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) + rw [hz]; simp [consumption] + rw [heq] + nlinarith [hres, hκ1, hη] + | succ k ih => + intro z a ha + have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) + = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := + Function.iterate_succ_apply' _ _ _ + set v : (ℝ × Z) →ᵇ ℝ := (P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ) with hvdef + rw [hiter] + set W : (ℝ × Z) →ᵇ ℝ := P.toExtended.bellman v with hWdef + set A : ℝ := P.policyOf W (a, z) with hAdef + have hAmem : A ∈ Icc (0 : ℝ) assetCap := P.feasible_subset_region (P.policyOf_mem W (a, z)) + set c : ℝ := P.consumptionFnOf W z a with hcdef + have hcA : c = P.resources (a, z) - A := rfl + have hc0 : 0 < c := by + have := hpos (k + 1) z a ha + rwa [hiter] at this + rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 + · nlinarith [hκ1, hc0, hcA, hA0, hη] + · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => + P.policyOf_lt_maxSaving_of_pos (hpos k z' A hAmem) (hcap k A hAmem z') + have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hpos k z' A hAmem + have hder : ∀ z' : Z, HasDerivAt P.u ((η + P.consumptionFnOf v z' A) ^ (-γ)) + (P.consumptionFnOf v z' A) := by + intro z'; rw [hu] + exact hasDerivAt_haraUtility γ (by linarith [hc' z']) + have heuler := P.euler_ge (v := v) (z := z) (a := a) (A := A) ha rfl hA0 hslack hc0 + (by rw [hu]; exact hasDerivAt_haraUtility γ (by linarith)) hc' hder + -- the continuation's shifted consumption is at least `κ (η + R A)` + set X : ℝ := P.minMPC γ * (η + (1 + P.interest) * A) with hXdef + have hX0 : 0 < X := by + have : (0 : ℝ) < (1 + P.interest) * A := by positivity + rw [hXdef]; nlinarith [hκ0, hη] + have hstep : ∀ z' : Z, X ≤ η + P.consumptionFnOf v z' A := by + intro z' + refine le_trans ?_ (ih z' A hAmem) + have hres : P.resources (A, z') = P.income z' + (1 + P.interest) * A := by + simp only [resources, max_eq_right hAmem.1] + have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') + rw [hres, hXdef] + nlinarith [hκ0] + have hsum : ∑ z' : Z, P.transitionMatrix z z' + * (η + P.consumptionFnOf v z' A) ^ (-γ) ≤ X ^ (-γ) := by + calc ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) + ≤ ∑ z' : Z, P.transitionMatrix z z' * X ^ (-γ) := + Finset.sum_le_sum fun z' _ => + mul_le_mul_of_nonneg_left (rpow_neg_antitone hγ0 hX0 (hstep z')) + (P.transitionMatrix_nonneg z z') + _ = X ^ (-γ) := by rw [← Finset.sum_mul, P.transitionMatrix_sum z, one_mul] + have hchain : (η + c) ^ (-γ) ≤ (P.discount : ℝ) * (1 + P.interest) * X ^ (-γ) := by + have := le_trans heuler + (mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hsum hR.le) hβ.le) + linarith [this] + have hTc : (T * (η + c)) ^ (-γ) ≤ X ^ (-γ) := by + rw [Real.mul_rpow hT0.le (by linarith), hTneg, inv_mul_le_iff₀ hβR0] + linarith [hchain] + have hXTc : X ≤ T * (η + c) := + le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc + rw [hXdef] at hXTc + -- `κR + Þ = R`, and the extra `η` term has the right sign because `κ ≥ 0` + nlinarith [hXTc, hkey, hR, hcA, hκ0, hη, hT0] + +/-- **The shifted-CRRA bound at the value function.** -/ +theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFn z a := by + have hiter := P.hara_minMPC_mul_le_consumptionFnOf hγ0 hη hu hfl hκ hβ hβR hpos hcap + have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha + have htend : Tendsto + (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop + (𝓝 (P.policy (a, z))) := + P.tendsto_policyOf (P.toExtended.tendsto_iterate_valueFunction 0) + P.concaveSlices_valueFunction hmem + have htendc : Tendsto + (fun n => η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (η + P.consumptionFn z a)) := by + have hcf : P.consumptionFn z a = P.resources (a, z) - P.policy (a, z) := rfl + rw [hcf, ← add_sub_assoc] + simpa only [consumptionFnOf, consumption, ← add_sub_assoc] using + (tendsto_const_nhds (x := η + P.resources (a, z)) (f := atTop (α := ℕ))).sub htend + exact ge_of_tendsto htendc (Filter.Eventually.of_forall fun n => hiter n z a ha) + +/-- **The share form.** `κ(η + m) ≤ η + c` is not a bound on the SHARE `c/m`, because of the +constant. Charging the constant against the smallest cash on hand there can be gives one: +`1 - (1-κ)(1 + η/minIncome)` is a share the household always consumes. It is what the capital +ceiling needs, and it degrades gracefully — at `η = 0` it is `κ` itself. -/ +noncomputable def minMPCShare (γ η : ℝ) : ℝ := + P.minMPC γ - (1 - P.minMPC γ) * η / P.minIncome + +theorem hara_minMPCShare_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPCShare γ η * P.resources (a, z) ≤ P.consumptionFn z a := by + have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hβR hpos hcap z ha + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hy : 0 < P.minIncome := P.minIncome_pos + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right (hfl ▸ ha.1 : assetFloor ≤ a)] + have hmin : P.minIncome ≤ P.resources (a, z) := by + have h1 : P.minIncome ≤ P.income z := P.minIncome_le z + have h2 : (0 : ℝ) ≤ (1 + P.interest) * a := by + have : (0 : ℝ) ≤ a := hfl ▸ ha.1 + positivity + rw [hres]; linarith + set κ : ℝ := P.minMPC γ with hκdef + set m : ℝ := P.resources (a, z) with hmdef + set q : ℝ := (1 - κ) * η / P.minIncome with hqdef + have hnn : (0 : ℝ) ≤ (1 - κ) * η := mul_nonneg (by linarith) hη + have hgap : η - κ * η ≤ m * q := by + rw [hqdef, ← mul_div_assoc, le_div_iff₀ hy] + nlinarith [mul_le_mul_of_nonneg_left hmin hnn] + have hbound' : κ * η + κ * m ≤ η + P.consumptionFn z a := by nlinarith [hbound] + simp only [minMPCShare, ← hκdef, ← hqdef] + nlinarith [hbound', hgap] + +/-- **The decline condition for shifted CRRA.** Saving is at most `(1-κ)(η + m)`, so the +household behaves like a CRRA household whose income is raised by the subsistence level. -/ +theorem hara_policy_lt_self_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) + (hgt : (1 - P.minMPC γ) * (η + P.income z) + < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a) : + P.policy (a, z) < a := by + have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hβR hpos hcap z ha + have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by + simp only [resources, max_eq_right (hfl ▸ ha.1 : assetFloor ≤ a)] + have hpol : P.policy (a, z) = P.resources (a, z) - P.consumptionFn z a := by + simp only [consumptionFn, consumption]; ring + rw [hpol, hres] + rw [hres] at hbound + nlinarith [hbound, hgt] + +/-- **The Doeblin exhaustion data for shifted CRRA**, with no assumption on the income process. -/ +theorem hara_exists_exhaust_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + {z₀ : Z} {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc (0 : ℝ) a₀, P.policy (a, z₀) = 0) + (hthr : (1 - P.minMPC γ) * (η + P.income z₀) + < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + subst hfl + have hslope : (0 : ℝ) < 1 - (1 - P.minMPC γ) * (1 + P.interest) := by + rw [one_sub_minMPC_mul (P := P)] + have := patience_lt_one (P := P) hγ0 hβR + linarith + refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ + refine P.hara_policy_lt_self_of_minMPC hγ0 hη hu rfl hκ hβ hβR hpos hcap z₀ + ⟨le_trans ha₀.le ha.1, ha.2⟩ ?_ + have hmono : (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀ + ≤ (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a := + mul_le_mul_of_nonneg_left ha.1 hslope.le + linarith + +/-! ### The exhaustion data, without `IidIncome` + +`exists_exhaust_of_impatient` (Açıkgöz Proposition 4) also supplies the decline half from +impatience, but it gets there by comparing income states: consumption is lowest where income is +lowest, which needs `IidIncome` so that the continuation does not depend on today's state. The +minimal-MPC route compares nothing. It is a statement about the budget and the Euler inequality +at ONE state, so it holds whatever the income process does. + +What it costs instead is the two iterate-level hypotheses, which the Açıkgöz route asks for only +at the fixed point. -/ + +/-- **The Doeblin exhaustion data from the minimal MPC.** Only the corner condition and one +threshold inequality are left to the calibration — and no assumption is made about the income +process. -/ +theorem crra_exists_exhaust_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + {z₀ : Z} {a₀ : ℝ} (ha₀ : 0 < a₀) (hle : a₀ ≤ assetCap) + (hzero : ∀ a ∈ Icc (0 : ℝ) a₀, P.policy (a, z₀) = 0) + (hthr : (1 - P.minMPC γ) * P.income z₀ + < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀) : + ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by + subst hfl + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + -- the decline threshold only has to be cleared once: the gap grows with assets + have hslope : (0 : ℝ) < 1 - (1 - P.minMPC γ) * (1 + P.interest) := by + rw [one_sub_minMPC_mul (P := P)] + have := patience_lt_one (P := P) hγ0 hβR + linarith + refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ + refine P.crra_policy_lt_self_of_minMPC hγ0 hu rfl hκ hβ hβR hpos hcap z₀ + ⟨le_trans ha₀.le ha.1, ha.2⟩ ?_ + have hmono : (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀ + ≤ (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a := + mul_le_mul_of_nonneg_left ha.1 hslope.le + linarith + +end IncomeFluctuation + + + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean new file mode 100644 index 000000000..81b68abbf --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean @@ -0,0 +1,322 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler +public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate + + +/-! +# Persistent income: the decline condition from monotone transitions + +Açıkgöz's decline argument (`policy_lt_self_of_impatient`) runs the Euler inequality at the state +where consumption is lowest, and needs to know which state that is. With iid income it is the +lowest-income state, because every state shares a continuation. With persistent income the +continuations differ, and the lowest-income state is still the lowest-consumption state only if +its continuation is the STEEPEST — a poor household today expects to be poor tomorrow, so wealth +is worth more to it at the margin. + +That is Huggett (1993) Lemma 1, `v'(a, e_h) ≤ v'(a, e_l)`, proved there by induction on the +Bellman iterates under `π(e_h | e_h) ≥ π(e_h | e_l)`. This file is that induction, in secant form +and for a finite income process ordered by income. + +## Monotone transitions + +The condition is stated by its use: a state with higher income has a transition row that makes +every income-antitone function of tomorrow's state smaller in expectation +(`MonotoneTransitions`). For two states it is exactly Huggett's `π(h|h) ≥ π(h|l)` +(`monotoneTransitions_of_two_states`); for a Tauchen chain it is first-order stochastic +dominance of the rows, which the chain has by construction. + +## The induction + +`IncDiffAcrossStates v` says the increments of `v` in assets are antitone in income. It survives +the Bellman operator because the envelope derivative is `R · u'(c)` and consumption at a fixed +asset level is increasing in income (`consumptionFnOf_le_across_states`) — which in turn follows +from single crossing: a richer household with a flatter continuation saves less of its extra +resources than it receives. The base case is the zero continuation, and the property passes to +the value function as a pointwise limit. +-/ + +@[expose] public section + +open Set Filter Topology BoundedContinuousFunction + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) + +/-! ### The two properties -/ + +/-- **Monotone transitions.** A higher-income state's transition row makes every +income-antitone function of tomorrow's state smaller in expectation: first-order stochastic +dominance of the rows, in the income order. -/ +def MonotoneTransitions : Prop := + ∀ z₁ z₂ : Z, P.income z₁ ≤ P.income z₂ → ∀ f : Z → ℝ, + (∀ w₁ w₂ : Z, P.income w₁ ≤ P.income w₂ → f w₂ ≤ f w₁) → + ∑ z' : Z, P.transitionMatrix z₂ z' * f z' ≤ ∑ z' : Z, P.transitionMatrix z₁ z' * f z' + +/-- iid income has monotone transitions, trivially: the rows are all equal. -/ +theorem monotoneTransitions_of_iid (hiid : P.IidIncome) : P.MonotoneTransitions := by + intro z₁ z₂ _ f _ + exact le_of_eq (Finset.sum_congr rfl fun z' _ => by rw [hiid z₂ z₁ z']) + +/-- Changing the rate leaves the income process, hence monotonicity, untouched. -/ +theorem monotoneTransitions_withRate {assetCap : ℝ} {P : IncomeFluctuation Z 0 assetCap} + (hmono : P.MonotoneTransitions) {r : ℝ} (hrr : P.RateOK r) : + (P.withRate r hrr).MonotoneTransitions := hmono + +/-- **Increments of `v` in assets are antitone in income**: the secant form of Huggett's +`v'(a, e_h) ≤ v'(a, e_l)`. -/ +def IncDiffAcrossStates (v : (ℝ × Z) →ᵇ ℝ) : Prop := + ∀ z₁ z₂ : Z, P.income z₁ ≤ P.income z₂ → ∀ x y : ℝ, + x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → + v (y, z₂) - v (x, z₂) ≤ v (y, z₁) - v (x, z₁) + +theorem incDiffAcrossStates_zero : P.IncDiffAcrossStates 0 := by + intro _ _ _ _ _ _ _ _ + simp + +/-- The continuation inherits the property, by monotone transitions: the increment +`v (y, ·) - v (x, ·)` is income-antitone, and the poorer state's row weights it more. -/ +theorem contOf_sub_le_across_states (hmono : P.MonotoneTransitions) {v : (ℝ × Z) →ᵇ ℝ} + (hv : P.IncDiffAcrossStates v) {z₁ z₂ : Z} (hz : P.income z₁ ≤ P.income z₂) + {x y : ℝ} (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : x ≤ y) : + P.contOf v z₂ y - P.contOf v z₂ x ≤ P.contOf v z₁ y - P.contOf v z₁ x := by + simp only [contOf, ← Finset.sum_sub_distrib, ← mul_sub] + exact hmono z₁ z₂ hz (fun z' => v (y, z') - v (x, z')) fun w₁ w₂ hw => hv w₁ w₂ hw x y hx hy hxy + +/-! ### Consumption rises with income at a fixed asset level + +The exchange argument of `consumptionFn_mono_of_cont_eq`, with the shared continuation replaced +by an ordered pair: the richer state has a flatter continuation as well as more resources, and +both push consumption up. -/ + +/-- **Consumption is ordered across states** against a continuation whose increments are +antitone in income. -/ +theorem consumptionFnOf_le_across_states {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) {z₁ z₂ : Z} + (hid : ∀ x y : ℝ, x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → + P.contOf v z₂ y - P.contOf v z₂ x ≤ P.contOf v z₁ y - P.contOf v z₁ x) + {a a' : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) + (hres : P.resources (a, z₁) ≤ P.resources (a', z₂)) : + P.consumptionFnOf v z₁ a ≤ P.consumptionFnOf v z₂ a' := by + set Δ : ℝ := P.resources (a', z₂) - P.resources (a, z₁) with hΔdef + have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith + set b : ℝ := P.policyOf v (a, z₁) with hbdef + set b' : ℝ := P.policyOf v (a', z₂) with hb'def + by_contra hcon + rw [not_le] at hcon + have hgap : b + Δ < b' := by + simp only [consumptionFnOf, consumption] at hcon + rw [hΔdef]; linarith + have hbmem : b ∈ P.toExtended.feasible (a, z₁) := P.policyOf_mem v (a, z₁) + have hb'mem : b' ∈ P.toExtended.feasible (a', z₂) := P.policyOf_mem v (a', z₂) + rw [P.feasible_eq] at hbmem hb'mem + have hms : P.maxSaving (a', z₂) ≤ P.maxSaving (a, z₁) + Δ := by + have hmin : min assetCap (P.resources (a', z₂)) + ≤ min assetCap (P.resources (a, z₁)) + Δ := by + rcases le_total assetCap (P.resources (a, z₁)) with h | h + · rw [min_eq_left h] + exact le_trans (min_le_left _ _) (by linarith) + · rw [min_eq_right h] + exact le_trans (min_le_right _ _) (by rw [hΔdef]; linarith) + rw [P.maxSaving_eq, P.maxSaving_eq] + linarith + have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z₂) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z₁) := by + rw [P.feasible_eq] + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hc1 : P.consumption (a, z₁) b ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hc2 : P.consumption (a', z₂) b' ∈ P.dom := P.consumption_policyOf_mem_dom v ha' + have he1 : P.consumption (a', z₂) (b + Δ) = P.consumption (a, z₁) b := by + simp only [consumption, hΔdef]; ring + have he2 : P.consumption (a, z₁) (b' - Δ) = P.consumption (a', z₂) b' := by + simp only [consumption, hΔdef]; ring + have hI := P.objROf_le_of_mem v ha hshiftdown (by rw [he2]; exact hc2) + have hII := P.objROf_le_of_mem v ha' hshiftup (by rw [he1]; exact hc1) + have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + have hb'reg : b' ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + -- concavity of the poorer continuation, shifted by `Δ` + have hshift := (P.concaveOn_contOf hv z₁).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) + hbreg (by simpa using hb'reg) (by linarith) hΔ + rw [sub_add_cancel] at hshift + -- and the richer continuation is flatter still + have hacross := hid (b + Δ) b' ⟨by linarith [hbreg.1], by linarith [hb'reg.2]⟩ hb'reg hgap.le + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hchain := mul_le_mul_of_nonneg_left (le_trans hacross hshift) hβ + simp only [objROf, he1, he2] at hI hII + have hIIeq : P.objROf v (a', z₂) (b + Δ) = P.objROf v (a', z₂) b' := by + simp only [objROf, he1] + linarith [hI, hII, hchain] + have hbell : P.toExtended.objectiveE v (a', z₂) (b + Δ) + = ((P.toExtended.bellmanFn v (a', z₂) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, + ← P.objectiveE_eq_coe_of v ha' (P.policyOf_mem v _) hc2] + exact P.policyOf_optimal v _ + exact absurd (P.optimal_action_unique_of_concaveSlices hv ha' hshiftup (P.policyOf_mem v _) + hbell (P.policyOf_optimal v _)) (by rw [← hb'def]; linarith) + +/-! ### The Bellman step, by the envelope -/ + +/-- **Huggett's Lemma 1, one step.** If `v` has income-antitone increments then so does +`bellman v`: the derivative of `bellman v (·, z)` is `R · u'(c_v(·, z))`, and consumption at a +fixed asset level rises with income. -/ +theorem incDiffAcrossStates_bellman (hmono : P.MonotoneTransitions) + {v : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) + (hv : ConcaveSlices assetFloor assetCap v) (hpc : P.PositiveConsumptionAll) + (hslack : ∀ x ∈ Icc assetFloor assetCap, ∀ z : Z, P.policyOf v (x, z) < assetCap) + (hid : P.IncDiffAcrossStates v) : + P.IncDiffAcrossStates (P.toExtended.bellman v) := by + intro z₁ z₂ hz x y hx hy hxy + have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one + -- the difference `bellman v (·, z₁) - bellman v (·, z₂)` is monotone in assets + have key : ∀ a ∈ Ioo assetFloor assetCap, ∃ D : ℝ, 0 ≤ D ∧ + HasDerivAt (fun t => (P.toExtended.bellman v) (t, z₁) - (P.toExtended.bellman v) (t, z₂)) + D a := by + intro a ha + have hamem : a ∈ Icc assetFloor assetCap := ⟨ha.1.le, ha.2.le⟩ + have hpos₁ : 0 < P.consumptionFnOf v z₁ a := hpc v hv z₁ a hamem + have hpos₂ : 0 < P.consumptionFnOf v z₂ a := hpc v hv z₂ a hamem + have hd₁ := P.hasDerivAt_bellman ha.1 ha.2 (P.concaveSlices_bellman hv z₁) + (P.policyOf_lt_maxSaving hpc hv hamem (hslack a hamem z₁)) hpos₁ (hderiv _ hpos₁) + have hd₂ := P.hasDerivAt_bellman ha.1 ha.2 (P.concaveSlices_bellman hv z₂) + (P.policyOf_lt_maxSaving hpc hv hamem (hslack a hamem z₂)) hpos₂ (hderiv _ hpos₂) + refine ⟨_, ?_, hd₁.sub hd₂⟩ + have hc : P.consumptionFnOf v z₁ a ≤ P.consumptionFnOf v z₂ a := + P.consumptionFnOf_le_across_states hv + (fun x y hx hy hxy => P.contOf_sub_le_across_states hmono hid hz hx hy hxy) hamem hamem + (by simp only [resources]; linarith) + have hdu := hanti (mem_Ioi.mpr hpos₁) (mem_Ioi.mpr hpos₂) hc + nlinarith [hR] + have hmonoOn : MonotoneOn + (fun t => (P.toExtended.bellman v) (t, z₁) - (P.toExtended.bellman v) (t, z₂)) + (Icc assetFloor assetCap) := by + refine monotoneOn_of_deriv_nonneg (convex_Icc _ _) ?_ ?_ ?_ + · exact (((P.toExtended.bellman v).continuous.comp + (continuous_id.prodMk continuous_const)).sub ((P.toExtended.bellman v).continuous.comp + (continuous_id.prodMk continuous_const))).continuousOn + · rw [interior_Icc] + intro t ht + obtain ⟨D, _, hD⟩ := key t ht + exact hD.differentiableAt.differentiableWithinAt + · rw [interior_Icc] + intro t ht + obtain ⟨D, hD0, hD⟩ := key t ht + rw [hD.deriv] + exact hD0 + have := hmonoOn hx hy hxy + simp only at this + linarith + +/-! ### Along the iteration, and at the fixed point -/ + +/-- **Huggett's Lemma 1 for the value function.** -/ +theorem incDiffAcrossStates_valueFunction (hmono : P.MonotoneTransitions) {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hpc : P.PositiveConsumptionAll) + (hslack : ∀ n : ℕ, ∀ x ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap) : + P.IncDiffAcrossStates P.toExtended.valueFunction := by + have hslices : ∀ n : ℕ, + ConcaveSlices assetFloor assetCap ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact concaveSlices_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact P.concaveSlices_bellman ih + have hstep : ∀ n : ℕ, P.IncDiffAcrossStates ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by + intro n + induction n with + | zero => exact P.incDiffAcrossStates_zero + | succ k ih => + rw [Function.iterate_succ_apply'] + exact P.incDiffAcrossStates_bellman hmono hderiv hanti (hslices k) hpc (hslack k) ih + have hpt : ∀ p : ℝ × Z, + Tendsto (fun n => ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) p) atTop + (𝓝 (P.toExtended.valueFunction p)) := fun p => + (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp + (P.toExtended.tendsto_iterate_valueFunction 0)).tendsto_at p + intro z₁ z₂ hz x y hx hy hxy + exact le_of_tendsto_of_tendsto ((hpt (y, z₂)).sub (hpt (x, z₂))) ((hpt (y, z₁)).sub (hpt (x, z₁))) + (Eventually.of_forall fun n => hstep n z₁ z₂ hz x y hx hy hxy) + +/-- **Consumption is lowest where income is lowest**, for a persistent income process with +monotone transitions. The replacement for `consumptionFn_le_of_income_le`. -/ +theorem consumptionFn_le_of_income_le_monotone (hmono : P.MonotoneTransitions) {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hpc : P.PositiveConsumptionAll) + (hslack : ∀ n : ℕ, ∀ x ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap) + {z₁ z₂ : Z} (hinc : P.income z₁ ≤ P.income z₂) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) : + P.consumptionFn z₁ a ≤ P.consumptionFn z₂ a := + P.consumptionFnOf_le_across_states (v := P.toExtended.valueFunction) P.concaveSlices_valueFunction + (fun x y hx hy hxy => P.contOf_sub_le_across_states hmono + (P.incDiffAcrossStates_valueFunction hmono hderiv hanti hpc hslack) hinc hx hy hxy) + ha ha (by simp only [resources]; linarith) + +/-- **Açıkgöz Proposition 4 with persistent income.** Assets strictly decline at the lowest +income state, at every asset level above the borrowing constraint, given `β(1+r) < 1` and +monotone transitions. -/ +theorem policy_lt_self_of_impatient_monotone + (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hmono : P.MonotoneTransitions) + {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hdupos : ∀ c : ℝ, 0 < c → 0 < du c) + (hpc : P.PositiveConsumptionAll) + (hslackIt : ∀ n : ℕ, ∀ x ∈ Icc assetFloor assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap) + (hslack : ∀ (z : Z), ∀ x ∈ Icc assetFloor assetCap, P.policy (x, z) < P.maxSaving (x, z)) + {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha0 : assetFloor < a) : + P.policy (a, z₀) < a := + P.policy_lt_self_of_impatient hβR hderiv hanti hdupos + (fun z x hx => hpc _ P.concaveSlices_valueFunction z x hx) hslack ha ha0 + (fun z' => P.consumptionFn_le_of_income_le_monotone hmono hderiv hanti hpc hslackIt (hz₀ z') ha) + +/-! ### Two states -/ + +/-- **Huggett's condition is monotone transitions**, for two income states ordered by income: +`π(h | h) ≥ π(h | l)`. -/ +theorem monotoneTransitions_of_two_states {assetFloor assetCap : ℝ} + (P : IncomeFluctuation (Fin 2) assetFloor assetCap) + (hinc : P.income 0 ≤ P.income 1) + (hπ : P.transitionMatrix 0 1 ≤ P.transitionMatrix 1 1) : P.MonotoneTransitions := by + intro z₁ z₂ hz f hf + have hrow : ∀ z : Fin 2, P.transitionMatrix z 0 = 1 - P.transitionMatrix z 1 := by + intro z + have := P.transitionMatrix_sum z + simp only [Fin.sum_univ_two] at this + linarith + have hsum : ∀ z : Fin 2, ∑ z' : Fin 2, P.transitionMatrix z z' * f z' + = f 0 + P.transitionMatrix z 1 * (f 1 - f 0) := by + intro z + simp only [Fin.sum_univ_two, hrow z] + ring + rw [hsum, hsum] + have hf10 : f 1 ≤ f 0 := hf 0 1 hinc + have h₁ : z₁ = 0 ∨ z₁ = 1 := by fin_cases z₁ <;> simp + have h₂ : z₂ = 0 ∨ z₂ = 1 := by fin_cases z₂ <;> simp + rcases h₁ with rfl | rfl <;> rcases h₂ with rfl | rfl + · exact le_rfl + · nlinarith + · -- `income 1 ≤ income 0` forces the incomes equal, hence `f 0 = f 1` + have hf01 : f 0 ≤ f 1 := hf 1 0 hz + nlinarith + · exact le_rfl + +end IncomeFluctuation + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean new file mode 100644 index 000000000..8bf12b386 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean @@ -0,0 +1,351 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Models.ConsumptionFloor +public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationLipschitz + + +/-! +# A natural asset bound for log utility + +`exists_decline_of_consumption_lower_bound` turns a LINEAR lower bound on consumption into the +statement that assets above an explicit threshold decline — the natural asset bound, which is +what `assetCap` has been standing in for, and what +`not_concaveOn_consumptionFn_of_cap_binds` says has to hold before the Carroll–Kimball +hypothesis is even true. Its input, the linear bound, has been missing. This file supplies it +for log utility. + +## The argument is the crudest possible deviation + +Saving nothing is always feasible. Comparing the optimum with it, + + `u (resources) + β · cont 0 ≤ u (consumption) + β · cont (policy)`, + +so `u resources - u consumption ≤ β (cont (policy) - cont 0) ≤ 2 β ‖V‖`. For LOG utility that +reads `log (resources / consumption) ≤ 2 β ‖V‖`, which is a linear bound outright: + + `consumption ≥ exp (-2 β ‖V‖) · resources`. + +## Why this works for log and for nothing else + +The deviation is informative only because log is UNBOUNDED ABOVE, so `u resources` grows without +limit and drags `u consumption` up with it. With CRRA `γ > 1` utility is bounded above, the same +comparison gives only `c^(1-γ)` bounded, hence a constant floor and no linear bound at all — the +crude deviation says nothing about rich households. That is another face of the same split +`crra_pinch` found, and it is the case the structure leaves us with anyway. + +## What is honest about the constant + +`exp (-2 β ‖V‖)` is very lossy. The sharp constant is Ma and Toda's asymptotic MPC +`c_bar = 1 - (β R^(1-γ))^(1/γ)`, at which the decline condition becomes exactly `β R < 1` +(`one_sub_mpc_mul_of_asymptotic`). Getting it needs the Euler equation, hence the envelope +condition, hence the machinery that `IncomeFluctuationEnvelope` supplies only under side +conditions. So the bound proved here is qualitatively right and quantitatively weak: it +establishes a natural asset bound, but only for interest rates small relative to `exp (-2 β ‖V‖)`, +where the sharp argument would ask only for impatience. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +namespace IncomeFluctuation + +variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] +variable {assetFloor assetCap : ℝ} +variable (P : IncomeFluctuation Z assetFloor assetCap) + +/-- The slack in the "save nothing" comparison: twice the size of the value function, discounted. -/ +noncomputable def deviationGap : ℝ := 2 * P.discount * ‖P.toExtended.valueFunction‖ + +theorem deviationGap_nonneg : 0 ≤ P.deviationGap := by + have : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have := norm_nonneg P.toExtended.valueFunction + simp only [deviationGap] + positivity + +/-- **The optimum beats saving nothing, so utility cannot fall far short of the utility of +eating everything.** This holds for any utility function; it is the log case that turns it into +a linear bound. -/ +theorem utility_resources_sub_le {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.u (P.resources (a, z) - assetFloor) - P.u (P.consumptionFn z a) ≤ P.deviationGap := by + have hmem0 : assetFloor ∈ P.toExtended.feasible (a, z) := ⟨le_rfl, le_max_left _ _⟩ + have hc0 : 0 < P.consumption (a, z) assetFloor := by + have h1 := P.minConsumption_le_consumption_floor (a, z) + have h2 := P.minConsumption_pos + simp only [consumption]; linarith + have hopt := P.objR_le_of_mem ha hmem0 (P.mem_dom_of_pos hc0) + simp only [objR, consumption] at hopt + have hb1 := abs_le.mp (P.abs_cont_le z (P.policy (a, z))) + have hb2 := abs_le.mp (P.abs_cont_le z assetFloor) + have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg + have hgap : (P.discount : ℝ) * (P.cont z (P.policy (a, z)) - P.cont z assetFloor) + ≤ P.deviationGap := by + simp only [deviationGap] + nlinarith [hb1.2, hb2.1, hβ] + simp only [consumptionFn, consumption] + nlinarith [hopt, hgap] + +/-- **A linear lower bound on consumption, for log utility.** The one place the development gets +a bound that scales with resources rather than being a constant. -/ +theorem log_consumption_linear_lower_bound (hpc : P.PositiveConsumption) (hu : P.u = Real.log) + {a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + Real.exp (-P.deviationGap) * (P.resources (a, z) - assetFloor) ≤ P.consumptionFn z a := by + have hm : 0 < P.resources (a, z) - assetFloor := by + have h1 := P.minConsumption_le_consumption_floor (a, z) + have h2 := P.minConsumption_pos + linarith + have hc : 0 < P.consumptionFn z a := P.consumptionFn_pos hpc ha z + have hkey := P.utility_resources_sub_le ha z + rw [hu] at hkey + have hdiv : Real.log ((P.resources (a, z) - assetFloor) / P.consumptionFn z a) + ≤ P.deviationGap := by + rw [Real.log_div hm.ne' hc.ne'] + exact hkey + have hle : (P.resources (a, z) - assetFloor) / P.consumptionFn z a ≤ Real.exp P.deviationGap := + (Real.log_le_iff_le_exp (by positivity)).mp hdiv + rw [div_le_iff₀ hc] at hle + rw [Real.exp_neg] + rw [inv_mul_le_iff₀ (Real.exp_pos _)] + linarith + +/-- **The natural asset bound for log utility.** Assets above an explicit threshold decline, so +the household never accumulates without limit and the asset cap can be justified rather than +imposed. + +The hypothesis on the interest rate is the price of the crude constant: the sharp argument would +ask only for `β (1 + r) < 1`. -/ +theorem exists_natural_asset_bound_log (hpc : P.PositiveConsumption) (hu : P.u = Real.log) + (z : Z) + (hr : (1 - Real.exp (-P.deviationGap)) * (1 + P.interest) < 1) : + ∃ ā : ℝ, ∀ a ∈ Icc assetFloor assetCap, ā < a → P.policy (a, z) < a := + P.exists_decline_of_consumption_lower_bound hr + fun _ ha => P.log_consumption_linear_lower_bound hpc hu ha z + +/-! ### A better constant, from a smaller deviation + +`log_consumption_linear_lower_bound` compares the optimum with saving NOTHING, which is a huge +deviation and costs an exponential: `ε = exp (-2 β ‖V‖)`. Comparing instead with saving a +FRACTION `θ` of the optimum costs only a ratio, and letting `θ` approach one turns the bound +polynomial: + + `consumption ≥ resources / (1 + 2 · 2 β ‖V‖)`. + +No derivative is taken. The reason a near-zero deviation is affordable is that BOTH sides shrink +with `1 - θ`: the utility gain is `log (1 + (1-θ) b / c)`, and the continuation loss is bounded by +`2‖V‖ (1-θ) / θ` because concavity makes the slope of `cont` on `[θb, b]` no larger than its slope +on `[0, θb]`. The factor `(1-θ)` cancels, and what survives is the comparison of the two +constants. + +`θ = (G + 1/2) / (1 + G)` is chosen so that `θ (1 + G) - G = 1/2` exactly, which is what makes the +final inequality `b ≤ 2 G c`. + +The gain is small when `‖V‖` is small and large when it is not: at `G = 1.3` the two constants are +`0.27` and `0.28`, at `G = 5` they are `0.007` and `0.09`. Since `‖V‖` grows like `|log minIncome|`, +it is the dispersed calibrations — the ones the precautionary motive needs — where this matters. +-/ + +/-- `log (c + d) - log c ≥ d / (c + d)`, the elementary inequality behind the bound. -/ +theorem log_sub_log_ge {c d : ℝ} (hc : 0 < c) (hd : 0 ≤ d) : + d / (c + d) ≤ Real.log (c + d) - Real.log c := by + have hcd : 0 < c + d := by linarith + have h := Real.log_le_sub_one_of_pos (x := c / (c + d)) (by positivity) + rw [Real.log_div hc.ne' hcd.ne'] at h + have he : c / (c + d) - 1 = -(d / (c + d)) := by field_simp; ring + rw [he] at h + linarith + +/-- The arithmetic behind `b ≤ 2 G c`, isolated so the search stays small. -/ +private theorem two_gap_cancel {B G c θ : ℝ} (hθkey : θ * (1 + G) - G = 1 / 2) + (hcancel : θ * B ≤ G * (c + (1 - θ) * B)) : B ≤ 2 * G * c := by + have h : B * (θ * (1 + G) - G) = B * (1 / 2) := by rw [hθkey] + linarith [hcancel, h] + +/-- **A polynomial linear lower bound on consumption.** Sharper than +`log_consumption_linear_lower_bound` whenever the value function is large, and proved from a +deviation to `θ` times the optimal saving rather than to zero. -/ +theorem log_consumption_linear_lower_bound' (hpc : P.PositiveConsumption) (hu : P.u = Real.log) + {a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + (P.resources (a, z) - assetFloor) / (1 + 2 * P.deviationGap) ≤ P.consumptionFn z a := by + have hres0 : P.resources (a, z) = P.consumptionFn z a + P.policy (a, z) := by + simp only [consumptionFn, consumption]; ring + have hc0 : 0 < P.consumptionFn z a := P.consumptionFn_pos hpc ha z + set G : ℝ := P.deviationGap with hGdef + have hG : 0 ≤ G := P.deviationGap_nonneg + set b : ℝ := P.policy (a, z) with hbdef + set c : ℝ := P.consumptionFn z a with hcdef + have hc : 0 < c := hc0 + have hres : P.resources (a, z) = c + b := hres0 + have hb2 : b - assetFloor ≤ 2 * G * c := by + rcases eq_or_lt_of_le (P.policy_mem_region (a, z)).1 with hzero | hbpos + · rw [← hbdef] at hzero + rw [← hzero, sub_self] + exact mul_nonneg (by linarith) hc.le + rw [← hbdef] at hbpos + have hbf : (0 : ℝ) < b - assetFloor := by linarith + set θ : ℝ := (G + 1 / 2) / (1 + G) with hθdef + have hden : (0 : ℝ) < 1 + G := by linarith + have hθ0 : 0 < θ := by rw [hθdef]; positivity + have hθ1 : θ < 1 := by rw [hθdef, div_lt_one hden]; linarith + have h1θ : (0 : ℝ) < 1 - θ := by linarith + have hθkey : θ * (1 + G) - G = 1 / 2 := by rw [hθdef]; field_simp; ring + have hbreg : b ∈ Icc assetFloor assetCap := P.policy_mem_region _ + have hmem0 : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + -- deviate from `b` a fraction `1-θ` of the way back to the borrowing limit + set d : ℝ := assetFloor + θ * (b - assetFloor) with hddef + have hθb0 : assetFloor < d := by rw [hddef]; nlinarith + have hθbb : d < b := by rw [hddef]; nlinarith + have hfeas : d ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨by linarith, by linarith [(P.policy_mem (a, z)).2]⟩ + have hcons : P.consumption (a, z) d = c + (1 - θ) * (b - assetFloor) := by + simp only [consumption, hddef] + rw [hres] + ring + have hdpos : (0 : ℝ) ≤ (1 - θ) * (b - assetFloor) := mul_nonneg h1θ.le hbf.le + have hcpos : 0 < P.consumption (a, z) d := by rw [hcons]; linarith + have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hcpos) + simp only [objR, hcons, hu] at hopt + rw [show P.consumption (a, z) (P.policy (a, z)) = c from rfl, + show P.policy (a, z) = b from rfl] at hopt + -- concavity bounds the continuation loss + have hslope := (P.concaveOn_cont z).slope_anti_adjacent hmem0 hbreg hθb0 hθbb + have hb1 := abs_le.mp (P.abs_cont_le z d) + have hb0 := abs_le.mp (P.abs_cont_le z assetFloor) + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hd1 : d - assetFloor = θ * (b - assetFloor) := by rw [hddef]; ring + have hd2 : b - d = (1 - θ) * (b - assetFloor) := by rw [hddef]; ring + have hloss : P.cont z b - P.cont z d + ≤ 2 * ‖P.toExtended.valueFunction‖ * (1 - θ) / θ := by + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + rw [hd1, hd2] at hslope + rw [le_div_iff₀ hθ0] + have hkey : (P.cont z b - P.cont z d) * θ * (b - assetFloor) + ≤ 2 * ‖P.toExtended.valueFunction‖ * (1 - θ) * (b - assetFloor) := by + nlinarith [hslope, hb1.2, hb0.1, hbf, h1θ] + exact le_of_mul_le_mul_right hkey hbf + have heq : (P.discount : ℝ) * (2 * ‖P.toExtended.valueFunction‖ * (1 - θ) / θ) + = G * (1 - θ) / θ := by + rw [hGdef, IncomeFluctuation.deviationGap]; ring + have hβloss : (P.discount : ℝ) * (P.cont z b - P.cont z d) ≤ G * (1 - θ) / θ := by + have hmul := mul_le_mul_of_nonneg_left hloss hβ + linarith [hmul, heq.le, heq.ge] + -- the utility gain + have hgain := log_sub_log_ge hc hdpos + have hdist : (P.discount : ℝ) * (P.cont z b - P.cont z d) + = (P.discount : ℝ) * P.cont z b - (P.discount : ℝ) * P.cont z d := by ring + have hchain : (1 - θ) * (b - assetFloor) / (c + (1 - θ) * (b - assetFloor)) + ≤ G * (1 - θ) / θ := by + linarith [hopt, hβloss, hgain, hdist.le, hdist.ge] + rw [div_le_div_iff₀ (by linarith) hθ0] at hchain + have hcancel : θ * (b - assetFloor) ≤ G * (c + (1 - θ) * (b - assetFloor)) := by + refine le_of_mul_le_mul_left ?_ h1θ + nlinarith [hchain] + exact two_gap_cancel hθkey hcancel + rw [hres, div_le_iff₀ (by linarith)] + linarith [hb2] + +/-! ### The other half of exhaustion: the corner, for log + +`exists_decline_of_consumption_lower_bound` gives assets falling above a threshold. The ergodic +argument also needs the constraint to BIND below one — `policy = 0` on `[0, a₀]` at the bad income +state — and `policy_eq_zero_of_corner_at` supplies that from two explicit constants: a Lipschitz +bound `L` on the value function and a lower bound `m` on the secant slope of `u`, with `β L < m`. + +Both are computable for log. The secant slope of `log` on `(0, R]` is at least `1/R`, from +`log x ≤ x - 1`. The Lipschitz constant is the fixed point of the operator's own estimate, +`L = K (1+r) / (1 - β(1+r))` with `K = slopeBound log minIncome = 2 log 2 / minIncome`, so `β L < m` +reads + + `resources · β · 2 log 2 (1+r) / (minIncome (1 - β(1+r))) < 1`, + +a condition on the state's resources alone. Since resources at the bad state are +`minIncome + (1+r) a`, it cuts out an interval of low assets — exactly what the ergodic argument +wants, and exactly why the condition has to be state-dependent rather than global. -/ + +/-- The secant slope of `log` on `(0, R]` is at least `1 / R`. -/ +theorem log_marginal_bound {R : ℝ} (hR : 0 < R) {c d : ℝ} (hd : 0 < d) (hdc : d ≤ c) + (hcR : c ≤ R) : 1 / R * (c - d) ≤ Real.log c - Real.log d := by + have hc : 0 < c := lt_of_lt_of_le hd hdc + have hkey : Real.log (d / c) ≤ d / c - 1 := + Real.log_le_sub_one_of_pos (div_pos hd hc) + rw [Real.log_div hd.ne' hc.ne'] at hkey + have hstep : (c - d) / c ≤ Real.log c - Real.log d := by + rw [sub_div, div_self hc.ne'] + linarith + refine le_trans ?_ hstep + rw [div_mul_eq_mul_div, one_mul, div_le_div_iff₀ hR hc] + nlinarith [sub_nonneg.mpr hdc] + +/-- The slope bound of `log` at the income floor, in closed form. -/ +theorem log_slopeBoundU (hu : P.u = Real.log) : + P.slopeBoundU = 2 * Real.log 2 / P.minConsumption := by + have hm := P.minConsumption_pos + simp only [slopeBoundU, slopeBound, hu] + rw [← Real.log_div hm.ne' (by positivity), show P.minConsumption / (P.minConsumption / 2) = 2 by + field_simp] + field_simp + +/-- The Lipschitz constant the operator's own estimate is a fixed point of. -/ +noncomputable def logLipschitz : ℝ := + 2 * Real.log 2 / P.minConsumption * (1 + P.interest) / (1 - P.discount * (1 + P.interest)) + +theorem logLipschitz_nonneg (hβR : P.discount * (1 + P.interest) < 1) : + 0 ≤ P.logLipschitz := by + have hm := P.minConsumption_pos + have hr := P.interest_gt_neg_one + have hlog : (0 : ℝ) ≤ Real.log 2 := Real.log_nonneg (by norm_num) + refine div_nonneg (by positivity) (by linarith) + +/-- **The value function is Lipschitz with the explicit log constant.** -/ +theorem log_valueFunction_lipschitz (hu : P.u = Real.log) + (hβR : P.discount * (1 + P.interest) < 1) : + ∀ z : Z, ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, + |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| + ≤ P.logLipschitz * |x - y| := by + refine P.valueFunction_lipschitz (P.logLipschitz_nonneg hβR) (le_of_eq ?_) + have hne : (1 : ℝ) - P.discount * (1 + P.interest) ≠ 0 := by linarith + simp only [logLipschitz, P.log_slopeBoundU hu] + field_simp + ring + +/-- **The borrowing constraint binds where resources are small**, with an explicit threshold in +the primitives. -/ +theorem log_policy_eq_zero_of_resources (hdom : P.Unbounded) (hu : P.u = Real.log) + (hβR : P.discount * (1 + P.interest) < 1) {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) + (hlt : P.discount * P.logLipschitz < 1 / (P.resources s - assetFloor)) : + P.policy s = assetFloor := + P.policy_eq_zero_of_corner_at (P.log_valueFunction_lipschitz hu hβR) hs + (fun c d hd hdc hc => by + rw [hu] + refine log_marginal_bound ?_ (by rw [hdom] at hd; exact hd) hdc hc + have h1 := P.minConsumption_le_consumption_floor s + have h2 := P.minConsumption_pos + linarith) + hlt + + + +end IncomeFluctuation + +/-- **The bound is not vacuous.** At `r = 0` the interest-rate hypothesis holds automatically, +since `exp` is positive, so the log witness has a natural asset bound outright. -/ +theorem logImpatient_natural_asset_bound (z : Fin 2) : + ∃ ā : ℝ, ∀ a ∈ Icc (0 : ℝ) 1, ā < a → logImpatient.policy (a, z) < a := by + refine logImpatient.exists_natural_asset_bound_log + (logImpatient.positiveConsumption_of_unbounded rfl) logImpatient_u z ?_ + have hpos := Real.exp_pos (-logImpatient.deviationGap) + have hint : logImpatient.interest = 0 := rfl + rw [hint] + linarith + + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean b/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean new file mode 100644 index 000000000..ee1caf5c9 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean @@ -0,0 +1,200 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Probability.CDF +public import Mathlib.Probability.Distributions.Gaussian.Real +public import LeanPool.AiyagariEquilibrium.Models.MonotoneTransitions + + +/-! +# Tauchen (1986) chains have monotone transitions + +Tauchen's discretisation of `z' = ρ z + σ ε`, `ε ~ N(0,1)`, on the grid `lo, lo + w, …, lo + n w` +puts on state `j` the normal mass of the interval of half-width `w/2` about `lo + j w`, with the +end states taking the tails. Written through the row's cumulative mass on states below `k`, + + `F_i(k) = Φ((lo + k w - w/2 - ρ(lo + i w)) / σ)` for `0 < k ≤ n`, `F_i(0) = 0`, `F_i(n+1) = 1`, + +the entry is `F_i(j+1) - F_i(j)`. Everything about the chain is then one fact about `Φ`: it is +monotone. Entries are non-negative and rows sum to one because `F_i` rises from `0` to `1` in +`k`; the rows are ordered by first-order stochastic dominance because `F_i(k)` FALLS in `i` when +`ρ ≥ 0`; and the lowest state is reachable from everywhere because `Φ > 0`. + +There is nothing special about seven states. Aiyagari (1994) used seven; the results here are for +`n + 1` states, any `n`. + +## First-order stochastic dominance on a finite chain + +`sum_mul_le_of_cum_le` is the finite FOSD lemma: if `q` puts no more mass than `p` on every +initial segment, then `q` gives the smaller expectation to every antitone `f`. Summation by parts +(`Finset.sum_range_by_parts`) does it in one line. +-/ + +@[expose] public section + +open Set Finset MeasureTheory ProbabilityTheory + +namespace LeanEconomics + +/-! ### The standard normal cdf -/ + +/-- The standard normal distribution function `Φ`. -/ +noncomputable def normalCdf : ℝ → ℝ := cdf (gaussianReal 0 1) + +theorem normalCdf_mono : Monotone normalCdf := monotone_cdf _ + +theorem normalCdf_nonneg (x : ℝ) : 0 ≤ normalCdf x := cdf_nonneg _ x + +theorem normalCdf_le_one (x : ℝ) : normalCdf x ≤ 1 := cdf_le_one _ x + +/-- `Φ > 0` everywhere: the normal density is positive and `Iic x` has positive Lebesgue +measure. -/ +theorem normalCdf_pos (x : ℝ) : 0 < normalCdf x := by + have h1 : (1 : NNReal) ≠ 0 := one_ne_zero + have hint : 0 < ∫ y in Iic x, gaussianPDFReal 0 1 y := by + rw [setIntegral_pos_iff_support_of_nonneg_ae + (Filter.Eventually.of_forall fun y => gaussianPDFReal_nonneg 0 1 y) + (integrable_gaussianPDFReal 0 1).integrableOn] + have hsupp : Function.support (gaussianPDFReal 0 1) = univ := by + ext y; simp [Function.mem_support, (gaussianPDFReal_pos 0 1 y h1).ne'] + rw [hsupp, univ_inter, Real.volume_Iic] + exact ENNReal.zero_lt_top + unfold normalCdf + rw [cdf_eq_real, Measure.real, gaussianReal_apply_eq_integral 0 h1, ENNReal.toReal_ofReal hint.le] + exact hint + +/-! ### The finite FOSD lemma -/ + +/-- **First-order stochastic dominance on `{0, …, n}`.** Two probability vectors given through +cumulative functions `F` and `G` (`p j = F (j+1) - F j`, `F 0 = 0`, `F (n+1) = 1`, likewise `G`), +with `G ≤ F` pointwise — `q` puts no more mass on any initial segment — give, for every antitone +`f`, the inequality `∑ q f ≤ ∑ p f`. -/ +theorem sum_mul_le_of_cum_le {n : ℕ} {p q : Fin (n + 1) → ℝ} {F G : ℕ → ℝ} + (hp : ∀ j : Fin (n + 1), p j = F (j.val + 1) - F j.val) + (hq : ∀ j : Fin (n + 1), q j = G (j.val + 1) - G j.val) + (hF0 : F 0 = 0) (hG0 : G 0 = 0) (hFn : F (n + 1) = 1) (hGn : G (n + 1) = 1) + (hle : ∀ k, G k ≤ F k) {f : Fin (n + 1) → ℝ} (hf : ∀ j j' : Fin (n + 1), j ≤ j' → f j' ≤ f j) : + ∑ j, q j * f j ≤ ∑ j, p j * f j := by + -- extend `f` to `ℕ` + set fe : ℕ → ℝ := fun k => if h : k < n + 1 then f ⟨k, h⟩ else 0 with hfe + have hfe_eq : ∀ j : Fin (n + 1), fe j.val = f j := by + intro j; simp only [hfe, j.isLt, ↓reduceDIte] + have hfe_anti : ∀ k, k < n → fe (k + 1) ≤ fe k := by + intro k hk + have h1 : k < n + 1 := by omega + have h2 : k + 1 < n + 1 := by omega + simp only [hfe, h1, h2, ↓reduceDIte] + exact hf ⟨k, h1⟩ ⟨k + 1, h2⟩ (Fin.mk_le_mk.mpr (Nat.le_succ k)) + -- the two sums as range sums, then summation by parts + have key : ∀ (H : ℕ → ℝ) (r : Fin (n + 1) → ℝ), + (∀ j : Fin (n + 1), r j = H (j.val + 1) - H j.val) → H 0 = 0 → H (n + 1) = 1 → + ∑ j, r j * f j = fe n - ∑ i ∈ range n, (fe (i + 1) - fe i) * H (i + 1) := by + intro H r hr hH0 hHn + have h1 : ∑ j, r j * f j = ∑ i ∈ range (n + 1), fe i • (H (i + 1) - H i) := by + rw [← Fin.sum_univ_eq_sum_range (fun i => fe i • (H (i + 1) - H i)) (n + 1)] + refine Finset.sum_congr rfl fun j _ => ?_ + rw [hr j, hfe_eq j, smul_eq_mul, mul_comm] + rw [h1, Finset.sum_range_by_parts fe (fun i => H (i + 1) - H i) (n + 1)] + simp only [Nat.add_sub_cancel, Finset.sum_range_sub, hH0, hHn, sub_zero, smul_eq_mul, mul_one] + rw [key F p hp hF0 hFn, key G q hq hG0 hGn] + have hterm : ∀ i ∈ range n, + (fe (i + 1) - fe i) * F (i + 1) ≤ (fe (i + 1) - fe i) * G (i + 1) := by + intro i hi + have hi' : i < n := Finset.mem_range.mp hi + exact mul_le_mul_of_nonpos_left (hle (i + 1)) (by linarith [hfe_anti i hi']) + linarith [Finset.sum_le_sum hterm] + +/-! ### The chain -/ + +/-- **Tauchen's cumulative row**: the mass row `i` puts on states below `k`. -/ +noncomputable def tauchenCum (ρ σ lo w : ℝ) (n : ℕ) (i : Fin (n + 1)) (k : ℕ) : ℝ := + if k = 0 then 0 else if n + 1 ≤ k then 1 + else normalCdf ((lo + k * w - w / 2 - ρ * (lo + i * w)) / σ) + +/-- **Tauchen's transition matrix** on `n + 1` states. -/ +noncomputable def tauchen (ρ σ lo w : ℝ) (n : ℕ) (i j : Fin (n + 1)) : ℝ := + tauchenCum ρ σ lo w n i (j.val + 1) - tauchenCum ρ σ lo w n i j.val + +variable {ρ σ lo w : ℝ} {n : ℕ} + +theorem tauchenCum_zero (i : Fin (n + 1)) : tauchenCum ρ σ lo w n i 0 = 0 := by + simp [tauchenCum] + +theorem tauchenCum_top (i : Fin (n + 1)) : tauchenCum ρ σ lo w n i (n + 1) = 1 := by + simp [tauchenCum] + +/-- The cumulative row rises in `k`. -/ +theorem tauchenCum_mono (hσ : 0 < σ) (hw : 0 < w) (i : Fin (n + 1)) : + Monotone (tauchenCum ρ σ lo w n i) := by + intro k k' hkk' + unfold tauchenCum + by_cases hk : k = 0 + · subst hk + simp only [ite_true] + split_ifs <;> first | exact le_rfl | exact zero_le_one | exact normalCdf_nonneg _ + · have hk' : k' ≠ 0 := by omega + simp only [hk, hk', ite_false] + by_cases htop : n + 1 ≤ k + · have htop' : n + 1 ≤ k' := le_trans htop hkk' + simp [htop, htop'] + · simp only [htop, ite_false] + split_ifs with htop' + · exact normalCdf_le_one _ + · apply normalCdf_mono + apply div_le_div_of_nonneg_right _ hσ.le + have : (k : ℝ) ≤ k' := by exact_mod_cast hkk' + nlinarith + +/-- The cumulative row falls in the state when `ρ ≥ 0`: first-order stochastic dominance. -/ +theorem tauchenCum_anti (hρ : 0 ≤ ρ) (hσ : 0 < σ) (hw : 0 < w) {i i' : Fin (n + 1)} (hii' : i ≤ i') + (k : ℕ) : tauchenCum ρ σ lo w n i' k ≤ tauchenCum ρ σ lo w n i k := by + unfold tauchenCum + split_ifs + · exact le_rfl + · exact le_rfl + · apply normalCdf_mono + apply div_le_div_of_nonneg_right _ hσ.le + have : (i : ℝ) ≤ i' := by exact_mod_cast hii' + nlinarith [mul_nonneg hρ hw.le] + +theorem tauchen_nonneg (hσ : 0 < σ) (hw : 0 < w) (i j : Fin (n + 1)) : + 0 ≤ tauchen ρ σ lo w n i j := + sub_nonneg.mpr (tauchenCum_mono hσ hw i (Nat.le_succ _)) + +theorem tauchen_sum (i : Fin (n + 1)) : ∑ j, tauchen ρ σ lo w n i j = 1 := by + unfold tauchen + rw [Fin.sum_univ_eq_sum_range + (fun k => tauchenCum ρ σ lo w n i (k + 1) - tauchenCum ρ σ lo w n i k) (n + 1), + Finset.sum_range_sub, tauchenCum_top, tauchenCum_zero, sub_zero] + +/-- The lowest state is reached from everywhere in one step. -/ +theorem tauchen_pos_zero (i : Fin (n + 1)) : 0 < tauchen ρ σ lo w n i 0 := by + unfold tauchen + simp only [Fin.val_zero, zero_add] + rw [tauchenCum_zero, sub_zero] + unfold tauchenCum + simp only [one_ne_zero, ite_false] + split_ifs + · exact one_pos + · exact normalCdf_pos _ + +/-- **A Tauchen chain with `ρ ≥ 0` has monotone transitions**, for any economy whose income is +strictly increasing in the state. -/ +theorem monotoneTransitions_of_tauchen {assetFloor assetCap : ℝ} + (P : IncomeFluctuation.{0} (Fin (n + 1)) assetFloor assetCap) + (hπ : P.transitionMatrix = tauchen ρ σ lo w n) (hinc : StrictMono P.income) + (hρ : 0 ≤ ρ) (hσ : 0 < σ) (hw : 0 < w) : P.MonotoneTransitions := by + intro z₁ z₂ hz f hf + rw [hπ] + have hz' : z₁ ≤ z₂ := hinc.le_iff_le.mp hz + refine sum_mul_le_of_cum_le (F := tauchenCum ρ σ lo w n z₁) (G := tauchenCum ρ σ lo w n z₂) + (fun j => rfl) (fun j => rfl) (tauchenCum_zero _) (tauchenCum_zero _) (tauchenCum_top _) + (tauchenCum_top _) (fun k => tauchenCum_anti hρ hσ hw hz' k) ?_ + intro j j' hjj' + exact hf j j' (hinc.monotone hjj') + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Topology/Berge.lean b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean new file mode 100644 index 000000000..6ba0f849e --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean @@ -0,0 +1,274 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Topology.Semicontinuity.Hemicontinuity +public import Mathlib.Topology.Order.Compact +public import Mathlib.Topology.Instances.Real.Lemmas + + +/-! +# Berge's maximum theorem + +Consider maximising a continuous objective `f x y` over a constraint set `Γ x` that moves +with the parameter `x`. Berge's maximum theorem says that if `Γ` is continuous -- upper and +lower hemicontinuous -- with nonempty compact values, then the value of the problem varies +continuously with `x`, and the set of maximisers varies upper hemicontinuously. + +The two halves need the two halves of the continuity of `Γ`, and it is worth keeping track +of which: upper hemicontinuity gives upper semicontinuity of the value, lower +hemicontinuity gives lower semicontinuity of it. Neither alone suffices. + +Mathlib supplies the hemicontinuity definitions (`UpperHemicontinuousAt`, +`LowerHemicontinuousAt`) but not this theorem. + +## Main definitions + +* `LeanEconomics.maxValue f Γ x` : the value `sSup (f x '' Γ x)` of the problem at `x`. +* `LeanEconomics.argmax f Γ x` : the set of maximisers at `x`. + +## Main results + +* `LeanEconomics.continuousAt_maxValue` : **the maximum theorem** -- the value function is + continuous. +* `LeanEconomics.upperHemicontinuousAt_argmax` : the maximiser correspondence is upper + hemicontinuous. +* `LeanEconomics.argmax_nonempty`, `LeanEconomics.isCompact_argmax` : it has nonempty + compact values. + +## A check on the hypotheses + +Berge's hypotheses are strong enough to be worth checking they can all hold at once: +`continuous_maxValue_const` instantiates them at a constraint set that does not move with +the parameter. Without such a witness the theorems above could be vacuous. + +## Implementation notes + +`eventually_forall_lt` is the engine. It says that a strict bound on `f` over `Γ x` +persists into a neighbourhood of `x`, and it is proved from the generalized tube lemma +together with upper hemicontinuity. Upper semicontinuity of the value follows at once, and +so does the hard half of upper hemicontinuity of `argmax`, applied there to the +correspondence `x ↦ Γ x ∩ Vᶜ`. Stating it without reference to a supremum is what lets it +serve the second purpose, where that correspondence may take empty values. + +## References + +* Berge, *Espaces topologiques: Fonctions multivoques*, 1959. +* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*, theorem 3.6. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] +variable {f : X → Y → ℝ} {Γ : X → Set Y} {x : X} {c : ℝ} + +/-- The value of the problem `max f x y` subject to `y ∈ Γ x`. -/ +noncomputable def maxValue (f : X → Y → ℝ) (Γ : X → Set Y) (x : X) : ℝ := sSup (f x '' Γ x) + +/-- The set of maximisers of `f x` over `Γ x`. The codomain is any preorder, so that the +same definition serves the extended-real objectives of +`LeanEconomics.Topology.BergeEReal`. -/ +def argmax {Z : Type*} [Preorder Z] (f : X → Y → Z) (Γ : X → Set Y) (x : X) : Set Y := + {y ∈ Γ x | IsMaxOn (f x) (Γ x) y} + +/-- The objective as a function of the choice alone, with the parameter fixed. -/ +theorem continuous_fiber (hf : Continuous ↿f) (x : X) : Continuous (f x) := + hf.comp (Continuous.prodMk_right x) + +theorem bddAbove_image (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) : + BddAbove (f x '' Γ x) := + hΓc.bddAbove_image (continuous_fiber hf x).continuousOn + +theorem le_maxValue (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) {y : Y} (hy : y ∈ Γ x) : + f x y ≤ maxValue f Γ x := + le_csSup (bddAbove_image hf hΓc) ⟨y, hy, rfl⟩ + +omit [TopologicalSpace X] [TopologicalSpace Y] in +theorem maxValue_le (hΓne : (Γ x).Nonempty) (h : ∀ y ∈ Γ x, f x y ≤ c) : + maxValue f Γ x ≤ c := + csSup_le (hΓne.image _) (by rintro _ ⟨y, hy, rfl⟩; exact h y hy) + +/-- **The engine.** A strict upper bound on the objective over the constraint set survives +into a neighbourhood of the parameter. This is the generalized tube lemma combined with +upper hemicontinuity, and it makes no nonemptiness assumption, which matters because it is +applied below to a correspondence that may be empty-valued. -/ +theorem eventually_forall_lt (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) + (huhc : UpperHemicontinuousAt Γ x) (hc : ∀ y ∈ Γ x, f x y < c) : + ∀ᶠ x' in 𝓝 x, ∀ y ∈ Γ x', f x' y < c := by + have hW : IsOpen (↿f ⁻¹' Iio c) := isOpen_Iio.preimage hf + have hsub : ({x} : Set X) ×ˢ Γ x ⊆ ↿f ⁻¹' Iio c := by + rintro ⟨x', y⟩ ⟨hx', hy⟩ + rw [mem_singleton_iff] at hx' + subst hx' + exact hc y hy + obtain ⟨u, v, hu, hv, hxu, hΓv, huv⟩ := generalized_tube_lemma isCompact_singleton hΓc hW hsub + filter_upwards [huhc.forall_isOpen v hv hΓv, hu.mem_nhds (hxu rfl)] with x' hΓ' hx'u + intro y hy + have hmem : (x', y) ∈ u ×ˢ v := ⟨hx'u, hΓ' hy⟩ + exact huv hmem + +/-- Upper hemicontinuity of the constraint set gives upper semicontinuity of the value. -/ +theorem upperSemicontinuousAt_maxValue (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) : + UpperSemicontinuousAt (maxValue f Γ) x := by + intro c hc + obtain ⟨c', hc1, hc2⟩ := exists_between hc + have h : ∀ y ∈ Γ x, f x y < c' := fun y hy => lt_of_le_of_lt (le_maxValue hf (hΓc x) hy) hc1 + filter_upwards [eventually_forall_lt hf (hΓc x) huhc h] with x' hx' + exact lt_of_le_of_lt (maxValue_le (hΓne x') fun y hy => (hx' y hy).le) hc2 + +/-- Lower hemicontinuity of the constraint set gives lower semicontinuity of the value. -/ +theorem lowerSemicontinuousAt_maxValue (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (hlhc : LowerHemicontinuousAt Γ x) : + LowerSemicontinuousAt (maxValue f Γ) x := by + intro c hc + obtain ⟨_, ⟨y₀, hy₀, rfl⟩, hy₀c⟩ := exists_lt_of_lt_csSup ((hΓne x).image _) hc + have hW : IsOpen (↿f ⁻¹' Ioi c) := isOpen_Ioi.preimage hf + obtain ⟨u, v, hu, hv, hxu, hy₀v, huv⟩ := isOpen_prod_iff.mp hW x y₀ hy₀c + filter_upwards [(lowerHemicontinuousAt_iff.mp hlhc) v hv ⟨y₀, hy₀, hy₀v⟩, + hu.mem_nhds hxu] with x' hne hx'u + obtain ⟨y, hyΓ, hyv⟩ := hne + have hmem : (x', y) ∈ u ×ˢ v := ⟨hx'u, hyv⟩ + exact lt_of_lt_of_le (huv hmem) (le_maxValue hf (hΓc x') hyΓ) + +/-- **Berge's maximum theorem**, the value function half: if the constraint correspondence +is continuous with nonempty compact values and the objective is continuous, then the value +of the problem is continuous in the parameter. -/ +theorem continuousAt_maxValue (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) + (hlhc : LowerHemicontinuousAt Γ x) : ContinuousAt (maxValue f Γ) x := by + rw [ContinuousAt, tendsto_order] + exact ⟨lowerSemicontinuousAt_maxValue hf hΓne hΓc hlhc, + upperSemicontinuousAt_maxValue hf hΓne hΓc huhc⟩ + +/-- The value function is continuous, given continuity of the correspondence everywhere. -/ +theorem continuous_maxValue (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuous Γ) (hlhc : LowerHemicontinuous Γ) : + Continuous (maxValue f Γ) := + continuous_iff_continuousAt.mpr fun x => + continuousAt_maxValue hf hΓne hΓc (huhc x) (hlhc x) + +/-- A maximiser exists: the constraint set is compact and the objective continuous. -/ +theorem argmax_nonempty (hf : Continuous ↿f) (hΓne : (Γ x).Nonempty) (hΓc : IsCompact (Γ x)) : + (argmax f Γ x).Nonempty := by + obtain ⟨y, hy, hmax⟩ := hΓc.exists_isMaxOn hΓne (continuous_fiber hf x).continuousOn + exact ⟨y, hy, hmax⟩ + +theorem isCompact_argmax (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) : + IsCompact (argmax f Γ x) := by + have hcl : IsClosed {y | IsMaxOn (f x) (Γ x) y} := by + have heq : {y | IsMaxOn (f x) (Γ x) y} = ⋂ z ∈ Γ x, {y | f x z ≤ f x y} := by + ext y; simp [isMaxOn_iff] + rw [heq] + exact isClosed_biInter fun z _ => isClosed_le continuous_const (continuous_fiber hf x) + exact hΓc.inter_right hcl + +/-- At a maximiser the objective equals the value of the problem. -/ +theorem maxValue_eq (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) {y : Y} + (hy : y ∈ argmax f Γ x) : maxValue f Γ x = f x y := + le_antisymm (maxValue_le ⟨y, hy.1⟩ fun z hz => isMaxOn_iff.mp hy.2 z hz) + (le_maxValue hf hΓc hy.1) + +/-- **Berge's maximum theorem**, the maximiser half: the set of maximisers is upper +hemicontinuous. It need not be lower hemicontinuous -- a maximiser can appear abruptly when +a tie is reached -- which is why the conclusion is one-sided. -/ +theorem upperHemicontinuousAt_argmax (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) + (hlhc : LowerHemicontinuousAt Γ x) : UpperHemicontinuousAt (argmax f Γ) x := by + apply UpperHemicontinuousAt.of_forall_isOpen + intro V hV hsub + by_cases hsubV : Γ x ⊆ V + · -- Nothing to do: the whole constraint set is eventually inside `V`. + filter_upwards [huhc.forall_isOpen V hV hsubV] with x' h + exact fun y hy => h hy.1 + · -- The part of the constraint set outside `V` is compact, nonempty, and strictly + -- suboptimal; both facts persist into a neighbourhood of `x`. + obtain ⟨y', hy', hy'V⟩ := Set.not_subset.mp hsubV + have hKc : IsCompact (Γ x ∩ Vᶜ) := (hΓc x).inter_right hV.isClosed_compl + obtain ⟨y₁, hy₁, hmax₁⟩ := + hKc.exists_isMaxOn ⟨y', hy', hy'V⟩ (continuous_fiber hf x).continuousOn + have hlt : f x y₁ < maxValue f Γ x := by + rcases (le_maxValue hf (hΓc x) hy₁.1).lt_or_eq with h | h + · exact h + · refine absurd (hsub ⟨hy₁.1, isMaxOn_iff.mpr fun z hz => ?_⟩) hy₁.2 + rw [h] + exact le_maxValue hf (hΓc x) hz + obtain ⟨c, hc1, hc2⟩ := exists_between hlt + have hbound : ∀ y ∈ Γ x ∩ Vᶜ, f x y < c := fun y hy => + lt_of_le_of_lt (isMaxOn_iff.mp hmax₁ y hy) hc1 + filter_upwards [eventually_forall_lt hf hKc (huhc.inter hV.isClosed_compl) hbound, + lowerSemicontinuousAt_maxValue hf hΓne hΓc hlhc c hc2] with x' h1 h2 + intro y hy + by_contra hyV + have hlt' : f x' y < c := h1 y ⟨hy.1, hyV⟩ + rw [← maxValue_eq hf (hΓc x') hy] at hlt' + exact absurd h2 (not_lt.mpr hlt'.le) + +/-- **A single-valued upper hemicontinuous correspondence is a continuous function.** This +is how the second half of Berge becomes a continuity statement about the optimal policy, +once the maximiser is known to be unique. -/ +theorem continuous_of_upperHemicontinuous_singleton {Γ : X → Set Y} {g : X → Y} + (huhc : UpperHemicontinuous Γ) (hsingle : ∀ x, Γ x = {g x}) : Continuous g := by + rw [continuous_iff_continuousAt] + intro x + rw [ContinuousAt, tendsto_nhds] + intro V hV hgx + have hsub : Γ x ⊆ V := by + rw [hsingle x] + simpa using hgx + filter_upwards [huhc.forall_isOpen x V hV hsub] with x' h + refine h ?_ + rw [hsingle x'] + rfl + +/-- The same, on a set: single-valuedness is only needed where the conclusion is wanted. +This is the version the household models need, since the optimal action is unique on the +region where the value function is concave, not necessarily everywhere. -/ +theorem continuousOn_of_upperHemicontinuous_singleton {Γ : X → Set Y} {g : X → Y} {s : Set X} + (huhc : UpperHemicontinuous Γ) (hsingle : ∀ x ∈ s, Γ x = {g x}) : ContinuousOn g s := by + intro x hx + rw [ContinuousWithinAt, tendsto_nhds] + intro V hV hgx + have hsub : Γ x ⊆ V := by + rw [hsingle x hx] + simpa using hgx + filter_upwards [nhdsWithin_le_nhds (huhc.forall_isOpen x V hV hsub), self_mem_nhdsWithin] + with x' h hx' + refine h ?_ + rw [hsingle x' hx'] + rfl + +/-! ### A constraint set that does not move + +The degenerate case, recorded to show the hypotheses of the theorems above are +simultaneously satisfiable. -/ + +section Constant + +variable (K : Set Y) + +theorem upperHemicontinuous_const : UpperHemicontinuous (fun _ : X => K) := + UpperHemicontinuous.of_forall_isOpen fun _ _ _ h => Eventually.of_forall fun _ => h + +theorem lowerHemicontinuous_const : LowerHemicontinuous (fun _ : X => K) := + lowerHemicontinuous_iff.mpr fun _ => + lowerHemicontinuousAt_iff.mpr fun _ _ h => Eventually.of_forall fun _ => h + +/-- **Berge is not vacuous.** With a fixed compact nonempty constraint set every hypothesis +of the maximum theorem holds, and the value of the problem is continuous in the +parameter. -/ +theorem continuous_maxValue_const (hf : Continuous ↿f) (hKne : K.Nonempty) (hKc : IsCompact K) : + Continuous (maxValue f fun _ : X => K) := + continuous_maxValue hf (fun _ => hKne) (fun _ => hKc) + (upperHemicontinuous_const K) (lowerHemicontinuous_const K) + +end Constant + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean b/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean new file mode 100644 index 000000000..a6d4890c8 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import LeanPool.AiyagariEquilibrium.Topology.Berge +public import Mathlib.Topology.Instances.EReal.Lemmas + + +/-! +# Berge's maximum theorem for extended-real objectives + +`LeanEconomics.Topology.Berge` proves the maximum theorem for a real-valued objective. Here +the objective may take the value `-∞`, which is what a reward like `log c` does at zero +consumption. + +Allowing `-∞` is what lets a dynamic program be posed without a floor on the reward. Every +model in this library that uses a floor does so to keep the reward real-valued and bounded, +and then has to prove separately that the floor never binds. With an extended-real +objective the floor is unnecessary from the start. + +## What changes, and what does not + +Less than one might expect, and two of the changes make the proof *simpler*: `EReal` is a +complete lattice, so `le_sSup` and `sSup_le` carry no boundedness or nonemptiness side +conditions, unlike their conditionally-complete counterparts. + +Upper semicontinuity here goes through *attainment* rather than interpolation: the supremum +over a nonempty compact set is achieved, so a strict bound at every point of the set is a +strict bound on the supremum. (`DenselyOrdered EReal` does exist in Mathlib, as +`instDenselyOrderedEReal`, so the interpolating proof would work too; the argmax half below +uses it.) + +## Main results + +* `LeanEconomics.continuous_maxValueE` : the maximum theorem for `EReal`-valued objectives. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] +variable {f : X → Y → EReal} {Γ : X → Set Y} {x : X} {c : EReal} + +/-- The value of a maximisation problem whose objective may be `-∞`. No boundedness +hypothesis is needed: `EReal` is a complete lattice. -/ +noncomputable def maxValueE (f : X → Y → EReal) (Γ : X → Set Y) (x : X) : EReal := + sSup (f x '' Γ x) + +omit [TopologicalSpace X] [TopologicalSpace Y] in +theorem le_maxValueE {y : Y} (hy : y ∈ Γ x) : f x y ≤ maxValueE f Γ x := le_sSup ⟨y, hy, rfl⟩ + +omit [TopologicalSpace X] [TopologicalSpace Y] in +theorem maxValueE_le (h : ∀ y ∈ Γ x, f x y ≤ c) : maxValueE f Γ x ≤ c := + sSup_le (by rintro _ ⟨y, hy, rfl⟩; exact h y hy) + +theorem continuous_fiberE (hf : Continuous ↿f) (x : X) : Continuous (f x) := + hf.comp (Continuous.prodMk_right x) + +/-- The engine, exactly as in the real case: a strict upper bound over the constraint set +survives into a neighbourhood of the parameter. -/ +theorem eventually_forall_ltE (hf : Continuous ↿f) (hΓc : IsCompact (Γ x)) + (huhc : UpperHemicontinuousAt Γ x) (hc : ∀ y ∈ Γ x, f x y < c) : + ∀ᶠ x' in 𝓝 x, ∀ y ∈ Γ x', f x' y < c := by + have hW : IsOpen (↿f ⁻¹' Iio c) := isOpen_Iio.preimage hf + have hsub : ({x} : Set X) ×ˢ Γ x ⊆ ↿f ⁻¹' Iio c := by + rintro ⟨x', y⟩ ⟨hx', hy⟩ + rw [mem_singleton_iff] at hx' + subst hx' + exact hc y hy + obtain ⟨u, v, hu, hv, hxu, hΓv, huv⟩ := generalized_tube_lemma isCompact_singleton hΓc hW hsub + filter_upwards [huhc.forall_isOpen v hv hΓv, hu.mem_nhds (hxu rfl)] with x' hΓ' hx'u + intro y hy + have hmem : (x', y) ∈ u ×ˢ v := ⟨hx'u, hΓ' hy⟩ + exact huv hmem + +/-- Upper hemicontinuity of the constraint set gives upper semicontinuity of the value. +This goes through attainment of the supremum rather than interpolating a midpoint. -/ +theorem upperSemicontinuousAt_maxValueE (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) : + UpperSemicontinuousAt (maxValueE f Γ) x := by + intro c hc + have h : ∀ y ∈ Γ x, f x y < c := fun y hy => lt_of_le_of_lt (le_maxValueE hy) hc + filter_upwards [eventually_forall_ltE hf (hΓc x) huhc h] with x' hx' + obtain ⟨y₀, hy₀, heq⟩ := + (hΓc x').exists_sSup_image_eq (hΓne x') (continuous_fiberE hf x').continuousOn + rw [maxValueE, heq] + exact hx' y₀ hy₀ + +/-- Lower hemicontinuity of the constraint set gives lower semicontinuity of the value. -/ +theorem lowerSemicontinuousAt_maxValueE (hf : Continuous ↿f) + (hlhc : LowerHemicontinuousAt Γ x) : LowerSemicontinuousAt (maxValueE f Γ) x := by + intro c hc + obtain ⟨_, ⟨y₀, hy₀, rfl⟩, hy₀c⟩ := lt_sSup_iff.mp hc + have hW : IsOpen (↿f ⁻¹' Ioi c) := isOpen_Ioi.preimage hf + obtain ⟨u, v, hu, hv, hxu, hy₀v, huv⟩ := isOpen_prod_iff.mp hW x y₀ hy₀c + filter_upwards [(lowerHemicontinuousAt_iff.mp hlhc) v hv ⟨y₀, hy₀, hy₀v⟩, + hu.mem_nhds hxu] with x' hne hx'u + obtain ⟨y, hyΓ, hyv⟩ := hne + have hmem : (x', y) ∈ u ×ˢ v := ⟨hx'u, hyv⟩ + exact lt_of_lt_of_le (huv hmem) (le_maxValueE hyΓ) + +/-- **Berge's maximum theorem for an extended-real objective.** -/ +theorem continuousAt_maxValueE (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) + (hlhc : LowerHemicontinuousAt Γ x) : ContinuousAt (maxValueE f Γ) x := by + rw [ContinuousAt, tendsto_order] + exact ⟨lowerSemicontinuousAt_maxValueE hf hlhc, + upperSemicontinuousAt_maxValueE hf hΓne hΓc huhc⟩ + +theorem continuous_maxValueE (hf : Continuous ↿f) (hΓne : ∀ x, (Γ x).Nonempty) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuous Γ) (hlhc : LowerHemicontinuous Γ) : + Continuous (maxValueE f Γ) := + continuous_iff_continuousAt.mpr fun x => + continuousAt_maxValueE hf hΓne hΓc (huhc x) (hlhc x) + +/-- At a maximiser the objective equals the value, exactly as in the real case. -/ +theorem maxValueE_eq (hf : Continuous ↿f) (hΓne : (Γ x).Nonempty) (hΓc : IsCompact (Γ x)) : + ∃ y ∈ Γ x, maxValueE f Γ x = f x y := by + obtain ⟨y₀, hy₀, heq⟩ := hΓc.exists_sSup_image_eq hΓne (continuous_fiberE hf x).continuousOn + exact ⟨y₀, hy₀, heq⟩ + +omit [TopologicalSpace X] [TopologicalSpace Y] in +/-- At a maximiser the objective equals the value. In `EReal` this needs no side +conditions, the order being complete. -/ +theorem maxValueE_eq_of_mem_argmax {y : Y} (hy : y ∈ argmax f Γ x) : + maxValueE f Γ x = f x y := + le_antisymm (maxValueE_le fun z hz => isMaxOn_iff.mp hy.2 z hz) (le_maxValueE hy.1) + +/-- A maximiser exists. -/ +theorem argmaxE_nonempty (hf : Continuous ↿f) (hΓne : (Γ x).Nonempty) + (hΓc : IsCompact (Γ x)) : (argmax f Γ x).Nonempty := by + obtain ⟨y, hy, hmax⟩ := hΓc.exists_isMaxOn hΓne (continuous_fiberE hf x).continuousOn + exact ⟨y, hy, hmax⟩ + +/-- **Berge's maximum theorem, the maximiser half, for an extended-real objective.** The +proof is the real one unchanged: away from an open set containing the maximisers the +objective is strictly below the value, that gap survives into a neighbourhood by the +engine, and lower semicontinuity of the value keeps the value above the gap. -/ +theorem upperHemicontinuousAt_argmaxE (hf : Continuous ↿f) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuousAt Γ x) + (hlhc : LowerHemicontinuousAt Γ x) : UpperHemicontinuousAt (argmax f Γ) x := by + apply UpperHemicontinuousAt.of_forall_isOpen + intro V hV hsub + by_cases hsubV : Γ x ⊆ V + · filter_upwards [huhc.forall_isOpen V hV hsubV] with x' h + exact fun y hy => h hy.1 + · obtain ⟨y', hy', hy'V⟩ := Set.not_subset.mp hsubV + have hKc : IsCompact (Γ x ∩ Vᶜ) := (hΓc x).inter_right hV.isClosed_compl + obtain ⟨y₁, hy₁, hmax₁⟩ := + hKc.exists_isMaxOn ⟨y', hy', hy'V⟩ (continuous_fiberE hf x).continuousOn + have hlt : f x y₁ < maxValueE f Γ x := by + rcases (le_maxValueE hy₁.1).lt_or_eq with h | h + · exact h + · refine absurd (hsub ⟨hy₁.1, isMaxOn_iff.mpr fun z hz => ?_⟩) hy₁.2 + rw [h] + exact le_maxValueE hz + obtain ⟨c, hc1, hc2⟩ := exists_between hlt + have hbound : ∀ y ∈ Γ x ∩ Vᶜ, f x y < c := fun y hy => + lt_of_le_of_lt (isMaxOn_iff.mp hmax₁ y hy) hc1 + filter_upwards [eventually_forall_ltE hf hKc (huhc.inter hV.isClosed_compl) hbound, + lowerSemicontinuousAt_maxValueE hf hlhc c hc2] with x' h1 h2 + intro y hy + by_contra hyV + have hlt' : f x' y < c := h1 y ⟨hy.1, hyV⟩ + rw [← maxValueE_eq_of_mem_argmax hy] at hlt' + exact absurd h2 (not_lt.mpr hlt'.le) + +theorem upperHemicontinuous_argmaxE (hf : Continuous ↿f) + (hΓc : ∀ x, IsCompact (Γ x)) (huhc : UpperHemicontinuous Γ) (hlhc : LowerHemicontinuous Γ) : + UpperHemicontinuous (argmax f Γ) := fun x => + upperHemicontinuousAt_argmaxE hf hΓc (huhc x) (hlhc x) + +end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean b/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean new file mode 100644 index 000000000..c6ef973b4 --- /dev/null +++ b/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean @@ -0,0 +1,101 @@ +/- +Copyright (c) 2026 Robert Kirkby. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Robert Kirkby +-/ + +module +public import Mathlib.Topology.Semicontinuity.Hemicontinuity +public import Mathlib.Topology.MetricSpace.Thickening +public import Mathlib.Topology.Instances.Real.Lemmas + + +/-! +# A closed interval with moving endpoints is a continuous correspondence + +`x ↦ Set.Icc (f x) (g x)` is upper and lower hemicontinuous when `f` and `g` are +continuous. This is the hypothesis Berge's maximum theorem needs, and a budget set +`{a' | 0 ≤ a' ≤ m a}` is exactly of this shape, so these two lemmas are what let a +constrained consumption problem be posed as a dynamic program. + +## Main results + +* `LeanEconomics.upperHemicontinuous_Icc` +* `LeanEconomics.lowerHemicontinuous_Icc` + +## The asymmetry in the hypotheses + +Upper hemicontinuity needs no order hypothesis: where `g x < f x` the interval is empty, +and the empty set is contained in everything. + +Lower hemicontinuity needs `f ≤ g` *everywhere*, not merely at the point in question, and +this is not a technicality. If `f x = g x` while `f x' > g x'` at nearby `x'`, then `Γ x` +is a single point and `Γ x'` is empty, so an open set meeting `Γ x` meets nothing nearby. +Lower hemicontinuity genuinely fails there. An interval that is allowed to collapse and +then vanish is not a continuous correspondence. +-/ + +@[expose] public section + +open Set Filter Topology + +namespace LeanEconomics + +variable {X : Type*} [TopologicalSpace X] {f g : X → ℝ} + +/-- An interval with continuous endpoints is upper hemicontinuous. -/ +theorem upperHemicontinuous_Icc (hf : Continuous f) (hg : Continuous g) : + UpperHemicontinuous fun x => Set.Icc (f x) (g x) := by + apply UpperHemicontinuous.of_forall_isOpen + intro x V hV hsub + rcases le_or_gt (f x) (g x) with hle | hlt + · -- The interval is nonempty: thicken it slightly and stay inside `V`. + obtain ⟨ε, hε, hthick⟩ := isCompact_Icc.exists_thickening_subset_open hV hsub + have h1 : ∀ᶠ x' in 𝓝 x, |f x' - f x| < ε / 2 := by + simpa [Real.dist_eq] using Metric.tendsto_nhds.mp (hf.tendsto x) (ε / 2) (by positivity) + have h2 : ∀ᶠ x' in 𝓝 x, |g x' - g x| < ε / 2 := by + simpa [Real.dist_eq] using Metric.tendsto_nhds.mp (hg.tendsto x) (ε / 2) (by positivity) + filter_upwards [h1, h2] with x' hx1 hx2 + intro z hz + obtain ⟨hf1, hf2⟩ := abs_lt.mp hx1 + obtain ⟨hg1, hg2⟩ := abs_lt.mp hx2 + obtain ⟨hzl, hzu⟩ := hz + apply hthick + rw [Metric.mem_thickening_iff] + refine ⟨max (f x) (min (g x) z), ⟨le_max_left _ _, max_le hle (min_le_left _ _)⟩, ?_⟩ + rw [Real.dist_eq] + rcases le_total z (f x) with h | h + · rw [min_eq_right (h.trans hle), max_eq_left h, abs_lt] + constructor <;> linarith + · rcases le_total z (g x) with h' | h' + · rw [min_eq_right h', max_eq_right h, sub_self, abs_zero] + exact hε + · rw [min_eq_left h', max_eq_right hle, abs_lt] + constructor <;> linarith + · -- The interval is empty, and stays empty nearby. + have hev : ∀ᶠ x' in 𝓝 x, g x' < f x' := + hg.continuousAt.eventually_lt hf.continuousAt hlt + filter_upwards [hev] with x' h + rw [Set.Icc_eq_empty_of_lt h] + exact Set.empty_subset V + +/-- An interval with continuous endpoints that never collapses past emptiness is lower +hemicontinuous. -/ +theorem lowerHemicontinuous_Icc (hf : Continuous f) (hg : Continuous g) + (hle : ∀ x, f x ≤ g x) : LowerHemicontinuous fun x => Set.Icc (f x) (g x) := by + refine lowerHemicontinuous_iff.mpr fun x => lowerHemicontinuousAt_iff.mpr ?_ + intro V hV hne + obtain ⟨z, hzI, hzV⟩ := hne + -- Clamp `z` into the interval at each nearby point; the clamp is continuous and fixes + -- `z` at `x`, so it stays inside `V` nearby and witnesses the intersection. + have hw : Continuous fun x' => max (f x') (min (g x') z) := + hf.max (hg.min continuous_const) + have hwx : max (f x) (min (g x) z) = z := by + rw [min_eq_right hzI.2, max_eq_right hzI.1] + have hev : ∀ᶠ x' in 𝓝 x, max (f x') (min (g x') z) ∈ V := by + have h := (hw.tendsto x) (hV.mem_nhds (by rw [hwx]; exact hzV)) + exact h + filter_upwards [hev] with x' hx' + exact ⟨_, ⟨le_max_left _ _, max_le (hle x') (min_le_left _ _)⟩, hx'⟩ + +end LeanEconomics diff --git a/LeanPool/projects/aiyagari-equilibrium.yaml b/LeanPool/projects/aiyagari-equilibrium.yaml new file mode 100644 index 000000000..f64e0180c --- /dev/null +++ b/LeanPool/projects/aiyagari-equilibrium.yaml @@ -0,0 +1,62 @@ +slug: aiyagari-equilibrium +title: Capped log Aiyagari equilibrium and dynamic programming +summary: >- + Proves uniqueness of the equilibrium interest rate for capped log-utility + households with Aiyagari's discount and firm parameters, no borrowing, + and monotone finite-state earnings transitions, subject to an explicit + cap-slack inequality at the larger rate. Instantiates the earnings + process with arbitrary finite Tauchen chains. Bisection brackets a given + equilibrium and converges along a stationary-distribution selection; + existence of that equilibrium is a hypothesis of the bisection result. + Preserves Bellman contraction theory, real and extended-real Berge + maximum theorems, stationary Markov distributions, consumption concavity, + Euler inequalities, and marginal-propensity bounds used in the proofs. +branch: mathematical economics +entry_module: LeanPool.AiyagariEquilibrium +authors: + - Robert Kirkby +source: + url: https://github.com/LeanEconomics/LeanEconomics + github_repo: LeanEconomics/LeanEconomics + commit: 8295256bbd64ab488c8cb436d68e7064db560dfc + completion_url: https://github.com/LeanEconomics/LeanEconomics/commit/0cf50632dad6952f6feb45efec5a8fe2f3ff0876 + completed_at: '2026-09-18T07:21:51Z' +license: Apache-2.0 +status: verified +provenance: AI +main_declarations: + - LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo +main_results: + - declaration: LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo + informal: >- + For log households with discount 24/25, capital share 9/25, + depreciation 2/25, no borrowing, and a finite Tauchen earnings chain + with nonnegative persistence below one, two stationary equilibria + with rates in (-2/25, 1/24) coincide, provided the explicit asset-cap + inequality holds at their larger rate. + - declaration: LeanEconomics.IncomeFluctuation.aiyagari1994_log_bisection + informal: >- + Given an equilibrium rate in a compact interval inside (-2/25, 1/24), + a stationary-distribution selection and the cap and monotonicity + hypotheses, bisection on excess supply brackets that rate at every + step, bounds the midpoint error by the initial width divided by + 2^(n+1), and has left endpoints converging to the rate. + - declaration: LeanEconomics.continuousAt_maxValue + informal: >- + A continuous objective maximized over a nonempty compact-valued + correspondence that is upper and lower hemicontinuous has a + continuous maximum value at the parameter. + - declaration: LeanEconomics.Blackwell.contractingWith + informal: >- + A monotone operator on bounded continuous real functions that + discounts constants by a nonnegative factor below one is a + contraction with that factor. +tags: + - mathematical-economics + - dynamic-programming + - stationary-equilibrium + - markov-processes +msc: + - 91B50 + - 90C39 + - 60J05 From 60b546ceb48e5f5273153e4715cc9cc69478a3b4 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 10:15:03 +0000 Subject: [PATCH 02/19] Reduce Aiyagari proof search and redundant imports --- .../Analysis/DecreasingIncrements.lean | 3 --- .../Analysis/DifferentiableSandwich.lean | 3 --- .../AiyagariEquilibrium/Analysis/HARA.lean | 1 - .../Analysis/PowerMean.lean | 4 +--- .../Analysis/RelativeRiskAversion.lean | 1 - .../Distribution/Feller.lean | 3 --- .../Distribution/Stationary.lean | 1 - .../Distribution/Uniqueness.lean | 1 - .../DynamicProgramming/Bellman.lean | 1 - .../DynamicProgramming/Blackwell.lean | 1 - .../DynamicProgramming/Extended.lean | 1 - .../ExtendedStochastic.lean | 1 - .../DynamicProgramming/Stochastic.lean | 1 - .../Equilibrium/Aiyagari.lean | 1 - .../Equilibrium/AiyagariContinuity.lean | 1 - .../Equilibrium/AiyagariUniqueness.lean | 1 - .../Equilibrium/Bisection.lean | 1 - .../Equilibrium/CapitalSupplyMonotone.lean | 1 - .../AiyagariEquilibrium/Equilibrium/Firm.lean | 3 --- .../Equilibrium/PositiveCapital.lean | 2 -- .../Equilibrium/SignChange.lean | 6 ----- .../Equilibrium/Uniqueness.lean | 1 - .../Models/AiyagariTauchenWitness.lean | 1 - .../Models/BoundedIncomeFluctuation.lean | 1 - LeanPool/AiyagariEquilibrium/Models/CRRA.lean | 1 - .../Models/CRRAConstants.lean | 1 - .../Models/ConsumptionFloor.lean | 1 - .../Models/ConsumptionSavingsUnbounded.lean | 2 -- .../Models/EulerCorner.lean | 1 - .../Models/ImpatientDecline.lean | 1 - .../Models/IncomeFluctuation.lean | 1 - .../Models/IncomeFluctuationAugmented.lean | 1 - .../Models/IncomeFluctuationBounds.lean | 2 -- .../IncomeFluctuationCarrollKimball.lean | 1 - .../Models/IncomeFluctuationConsumption.lean | 1 - .../Models/IncomeFluctuationCrossRate.lean | 1 - .../Models/IncomeFluctuationEnvelope.lean | 1 - .../Models/IncomeFluctuationEuler.lean | 1 - .../Models/IncomeFluctuationHARA.lean | 7 ------ .../Models/IncomeFluctuationIterate.lean | 4 ---- .../Models/IncomeFluctuationLipschitz.lean | 1 - .../Models/IncomeFluctuationMonotone.lean | 1 - .../Models/IncomeFluctuationRate.lean | 1 - .../Models/IncomeFluctuationRateMonotone.lean | 1 - .../Models/IncomeFluctuationRateStep.lean | 3 --- .../Models/IncomeFluctuationRegion.lean | 1 - .../Models/LargeBetaLight.lean | 1 - .../Models/LogAiyagari.lean | 1 - .../Models/MinimalMPC.lean | 24 +++++++++---------- .../Models/MonotoneTransitions.lean | 1 - .../Models/NaturalAssetBound.lean | 15 +++++++----- .../AiyagariEquilibrium/Models/Tauchen.lean | 1 - .../AiyagariEquilibrium/Topology/Berge.lean | 2 -- .../Topology/BergeEReal.lean | 1 - .../Topology/IccCorrespondence.lean | 1 - 55 files changed, 22 insertions(+), 101 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean index 5c0601719..0fa6d665e 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean @@ -5,10 +5,7 @@ Authors: Robert Kirkby -/ module -public import Mathlib.Analysis.Convex.Function public import Mathlib.Analysis.Convex.SpecificFunctions.Basic -public import Mathlib.Analysis.Convex.Slope - /-! # Decreasing increments of a concave function diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean b/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean index 8b17b9953..a04bac445 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/DifferentiableSandwich.lean @@ -5,11 +5,8 @@ Authors: Robert Kirkby -/ module -public import Mathlib.Analysis.Calculus.LocalExtr.Basic -public import Mathlib.Analysis.Calculus.Deriv.Add public import Mathlib.Analysis.Convex.Deriv - /-! # The Differentiable Sandwich Lemma diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean index c297d64c7..7ac768517 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion public import LeanPool.AiyagariEquilibrium.Models.CRRA - /-! # HARA utility diff --git a/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean b/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean index 15f9d29b1..4144ea780 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/PowerMean.lean @@ -5,10 +5,8 @@ Authors: Robert Kirkby -/ module -public import Mathlib.Analysis.MeanInequalities +public import Mathlib.Algebra.BigOperators.Field public import Mathlib.Analysis.MeanInequalitiesPow -public import Mathlib.Analysis.Convex.SpecificFunctions.Basic - /-! # Weighted power means with a negative exponent diff --git a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean index cea6a6f9f..d4cd2a3c9 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements public import Mathlib.Analysis.Convex.Deriv public import Mathlib.Analysis.SpecialFunctions.Pow.Deriv - /-! # Relative risk aversion at most one diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean b/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean index 90223ef45..4cee5d2ea 100644 --- a/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean +++ b/LeanPool/AiyagariEquilibrium/Distribution/Feller.lean @@ -7,9 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation public import Mathlib.MeasureTheory.Measure.ProbabilityMeasure -public import Mathlib.MeasureTheory.Integral.Bochner.ContinuousLinearMap -public import Mathlib.MeasureTheory.Integral.Bochner.SumMeasure - /-! # The agent distribution: state space and Markov operator diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean b/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean index 2d52aea41..4175dba77 100644 --- a/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean +++ b/LeanPool/AiyagariEquilibrium/Distribution/Stationary.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Distribution.Feller public import Mathlib.MeasureTheory.Measure.Prokhorov - /-! # Existence of a stationary agent distribution diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean index ed516f470..000b64ae5 100644 --- a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Distribution.Stationary public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone - /-! # Uniqueness of the stationary agent distribution diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean index 04849c946..c3cb4e51d 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Bellman.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Blackwell public import LeanPool.AiyagariEquilibrium.Topology.Berge - /-! # The Bellman operator of a deterministic dynamic program diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean index 13195bac5..223120e33 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean @@ -8,7 +8,6 @@ module public import Mathlib.Topology.ContinuousMap.Bounded.Normed public import Mathlib.Topology.MetricSpace.Contracting - /-! # Blackwell's sufficient conditions diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean index 18177c57f..62a3af300 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Extended.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Bellman public import LeanPool.AiyagariEquilibrium.Topology.BergeEReal - /-! # Dynamic programs with extended-real rewards diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean index b45efe5ee..e7754e787 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/ExtendedStochastic.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Extended public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Stochastic - /-! # Stochastic dynamic programs with extended-real rewards diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean index 09e2d2fe3..7ae5ec5cc 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Stochastic.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Bellman - /-! # Stochastic dynamic programming with finitely many shocks diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean index c862c1f62..1df88fc2e 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness - /-! # Aiyagari stationary equilibrium diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean index 388388de7..6d28f822a 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariContinuity.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationAugmented public import LeanPool.AiyagariEquilibrium.Equilibrium.Aiyagari - /-! # Capital supply is continuous, and the equilibrium theorem applies diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index a1852de14..a3995148f 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -11,7 +11,6 @@ public import LeanPool.AiyagariEquilibrium.Equilibrium.Uniqueness public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange - /-! # Uniqueness of the equilibrium rate for a log economy at large `β` diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean index 1f5f6b682..1d982e36b 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness - /-! # Bisection finds the equilibrium rate, and the log Aiyagari instance diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean index d3a17a773..3377c3e47 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/CapitalSupplyMonotone.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness public import LeanPool.AiyagariEquilibrium.Equilibrium.Uniqueness - /-! # Light (2018) Theorem 2: aggregate savings rise with the interest rate diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean index 79f415e6d..96760eda8 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity - /-! # The firm side @@ -155,7 +154,6 @@ theorem capitalDemand_eq_iff {A δ r K : ℝ} (hA : 0 < A) (hr : 0 < r + δ) (hK field_simp ring - /-- **Equilibrium in implied-rate form.** A rate is an equilibrium exactly when the capital its households hold implies it back. -/ theorem isAiyagariEquilibrium_iff_impliedRate {Z : Type*} [Fintype Z] [Nonempty Z] @@ -172,7 +170,6 @@ theorem isAiyagariEquilibrium_iff_impliedRate {Z : Type*} [Fintype Z] [Nonempty · rintro ⟨μ, hμ, hpos, himp⟩ exact ⟨μ, hμ, (capitalDemand_eq_iff hA hr hpos).mpr himp⟩ - namespace IncomeFluctuation variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean index 500079cd4..7c1f82de2 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Distribution.Feller public import LeanPool.AiyagariEquilibrium.Equilibrium.Aiyagari public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone - /-! # Aggregate capital is strictly positive @@ -538,7 +537,6 @@ theorem policy_ge_of_gain (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) rw [← Real.log_div (by linarith) (by linarith)] exact Real.log_le_log (div_pos (by linarith) (by linarith)) hratio - end IncomeFluctuation end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean index 18843c9f4..2460933da 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean @@ -10,7 +10,6 @@ public import LeanPool.AiyagariEquilibrium.Equilibrium.PositiveCapital public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption public import LeanPool.AiyagariEquilibrium.Models.MinimalMPC - /-! # The sign change, both ends @@ -115,7 +114,6 @@ theorem exists_firm_of_bounds {rlo rhi m M : ℝ} (hlt : rlo < rhi) (hm : 0 < m) rw [div_le_iff₀ (by positivity)] nlinarith [hsq, hsM2, hsm2] - namespace IncomeFluctuation variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] @@ -285,7 +283,6 @@ theorem exists_equilibrium_of_technology {rlo rhi : ℝ} (hrlo : 0 < 1 + rlo) (h P.exists_equilibrium_of_ceiling_and_floor hrlo hlt huniq (fun _ μ _ => P.aggregateCapital_le μ) hfloor A δ hδ hDlo hDhi - /-- **An Aiyagari equilibrium from household-side hypotheses alone.** Uniqueness of the stationary distribution at each rate, and a positive floor under capital supply AT THE TOP OF THE INTERVAL, suffice: the firm is then calibrated to meet them. @@ -312,7 +309,6 @@ theorem exists_equilibrium_of_uniqueness_and_floor {rlo rhi : ℝ} (hrlo : 0 < 1 exact ⟨A, δ, hA, hrδ, P.exists_equilibrium_of_technology hrlo hlt huniq hfloor A δ hrδ hDlo hDhi⟩ - /-- **Capital supply is bounded above by the impatience of the household.** Every household saves at most `1 - ε` of its resources, capital is mean saving, and resources are income plus the return on capital — so the bound closes on itself. @@ -419,7 +415,6 @@ the rate: raising `r` raises `(1+r) t / (y₀ + (1+r) t)` towards one, so the ri while the left does not move. Checking the condition at the LOW end of the interval therefore covers all of it. -/ - theorem gain_term_mono {y₀ t : ℝ} (hy₀ : 0 < y₀) (ht : 0 < t) {s₀ s₁ : ℝ} (hs₀ : 0 < s₀) (hs : s₀ ≤ s₁) : Real.log (1 + s₀ * t / (y₀ + s₀ * t)) ≤ Real.log (1 + s₁ * t / (y₀ + s₁ * t)) := by @@ -431,7 +426,6 @@ theorem gain_term_mono {y₀ t : ℝ} (hy₀ : 0 < y₀) (ht : 0 < t) {s₀ s₁ nlinarith [mul_le_mul_of_nonneg_right hs (mul_nonneg ht.le hy₀.le)] linarith - /-- **An Aiyagari equilibrium with the Cobb–Douglas firm**, from two-sided bounds on capital supply. Both ends of the sign change are now theorems; what is assumed is a positive floor under capital supply, uniform in the rate. -/ diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean index 094fe5a84..2e028aa7d 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Equilibrium.Firm - /-! # Uniqueness of the equilibrium interest rate diff --git a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean index 6201895ea..e369f496a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean +++ b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.Tauchen public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness - /-! # Aiyagari (1994) with his own earnings process diff --git a/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean index 179333021..3a028bbfd 100644 --- a/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean +++ b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean @@ -11,7 +11,6 @@ public import LeanPool.AiyagariEquilibrium.Distribution.Stationary public import LeanPool.AiyagariEquilibrium.Equilibrium.PositiveCapital public import Mathlib.Analysis.Convex.SpecificFunctions.Pow - /-! # CES with `γ < 1`: utility bounded below diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean index 20e1ae7bf..4843e1722 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion - /-! # CRRA utility, and where it fits diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 91114f09d..61d8cf8e8 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.BoundedIncomeFluctuation public import LeanPool.AiyagariEquilibrium.Models.NaturalAssetBound - /-! # The quantitative constants for CES diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean index b01277026..c994b3e40 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds public import LeanPool.AiyagariEquilibrium.Models.CRRA - /-! # A consumption floor from the margin, not from the level diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean index cc393ff1b..d87ceaa71 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionSavingsUnbounded.lean @@ -8,10 +8,8 @@ module public import LeanPool.AiyagariEquilibrium.DynamicProgramming.Extended public import LeanPool.AiyagariEquilibrium.Topology.IccCorrespondence public import Mathlib.Analysis.SpecialFunctions.Pow.Continuity -public import Mathlib.Analysis.SpecialFunctions.Log.Basic public import Mathlib.Analysis.Convex.SpecificFunctions.Basic - /-! # Consumption and savings with period utility unbounded below diff --git a/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean index 8857c0dd5..3ac3a366e 100644 --- a/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean +++ b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline - /-! # The corner condition from the Euler inequality diff --git a/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean index d67747e98..a6b61b64e 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness - /-! # Açıkgöz (2018) Proposition 4: the natural asset bound from impatience alone diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean index 354657807..c67acc6eb 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.DynamicProgramming.ExtendedStochastic public import LeanPool.AiyagariEquilibrium.Topology.IccCorrespondence public import LeanPool.AiyagariEquilibrium.Models.ConsumptionSavingsUnbounded - /-! # The income fluctuation problem diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean index cc784b518..78ae0b66c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationAugmented.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate public import LeanPool.AiyagariEquilibrium.Distribution.Stationary - /-! # The interest rate as a state variable diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean index e260c1269..025062267 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean @@ -6,8 +6,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation -public import Mathlib.Algebra.Order.Group.Pointwise.Interval - /-! # Consumption at the optimum is bounded away from zero diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean index 2b5d32253..aa511011f 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption public import LeanPool.AiyagariEquilibrium.Analysis.PowerMean - /-! # Concavity of the consumption function as a closed class diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean index 20a184153..0a6cccef1 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationMonotone public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion - /-! # The consumption function diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean index 19d897558..8dbb9c9cc 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCrossRate.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds - /-! # Comparing two interest rates diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean index 3a74d8804..43c327ebb 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation public import LeanPool.AiyagariEquilibrium.Analysis.DifferentiableSandwich public import Mathlib.Analysis.Calculus.FDeriv.Extend - /-! # The lazy agent's value, and the envelope condition diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean index 503a1108c..4f5525834 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEnvelope public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCarrollKimball public import LeanPool.AiyagariEquilibrium.Models.CRRA - /-! # The Euler equation along the iteration, and the borrowing limit diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index cf7c8d524..f0076ba6c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -12,7 +12,6 @@ public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange public import LeanPool.AiyagariEquilibrium.Analysis.HARA - /-! # Carroll and Kimball beyond CRRA: the HARA induction step @@ -205,7 +204,6 @@ theorem concaveOn_consumptionFnOf_bellman_of_hara {γ η : ℝ} (hγ : 0 < γ) ( exact P.hara_egmMap_consumptionFnOf hγ hβ hu hη ha rfl hA0 hAmax (hcpos a ha) hint' (fun z' => hpos z' _ (P.feasible_subset_region (P.policyOf_mem _ (a, z)))) - /-- **CRRA is the `η = 0` member**, so the HARA step really does generalise the CRRA one rather than sitting beside it. -/ theorem concaveOn_consumptionFnOf_bellman_of_crra_via_hara {γ : ℝ} (hγ : 0 < γ) @@ -221,7 +219,6 @@ theorem concaveOn_consumptionFnOf_bellman_of_crra_via_hara {γ : ℝ} (hγ : 0 < P.concaveOn_consumptionFnOf_bellman_of_hara hγ le_rfl hβ (by rw [hu, haraUtility_zero_shift]) z hpos hconc hmono hcpos hint - /-! ### Marginal bounds A shifted CRRA has FINITE marginal utility at zero, `η ^ (-γ)`, so like CARA it satisfies no @@ -565,7 +562,6 @@ theorem concaveOn_consumptionFn_of_hara {γ η δ : ℝ} (hγ0 : 0 < γ) (hγ1 : exact P.concaveOn_consumptionFn_of_iterates (P.concaveOn_consumptionFnOf_iterates_of_hara hγ0 hη hβ hu hpos hslack) z - /-! ### The two comparative-statics corollaries, for shifted CRRA Light's Theorem 1 and the impatience-driven decline both take the marginal-utility hypotheses @@ -625,7 +621,6 @@ theorem hara_exists_exhaust_of_impatient {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 (hara_marginal_antitone hγ0 hη) (fun c hc => hara_marginal_pos hη hc) hpos hslack hz₀ ha₀ hle hzero - /-! ### The supply floor The household saves something, so aggregate capital is positive. The shape is the CRRA one: the @@ -707,7 +702,6 @@ theorem hara_policy_ge_of_gain {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) rw [hu] exact hara_cost_of_saving hγ hη hh0 hhinc hm hb0 hbh - /-! ### The corner condition, in primitives The corner test of `policy_eq_zero_of_corner_at` is `β L < m`: the discounted slope of the value @@ -833,7 +827,6 @@ theorem hara_le_aggregateCapital_of_gain {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 rw [show h - h / 2 = h / 2 from by ring] at hgen exact hgen - /-- **The supply floor with the two powers bounded separately.** The gain condition compares `(η + y₁ - t)^(-γ)`, the marginal cost of saving `t` in the high state, against `(η + y₀ + Rt)^(-γ)`, the marginal value of having it in the low state. Bounding the first from diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index 8541ad789..a6d192df2 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler public import LeanPool.AiyagariEquilibrium.Models.CRRAConstants - /-! # Positivity and monotonicity along the iteration @@ -590,7 +589,6 @@ theorem concaveOn_consumptionFn_of_crra {γ : ℝ} (hpc : P.PositiveConsumptionA P.concaveOn_consumptionFn_of_iterates (P.concaveOn_consumptionFnOf_iterates_of_crra hpc hγ0 hβ hu hslack) z - /-- **Carroll and Kimball (1996) for CRRA, from a calibration.** Beyond CRRA utility and marginal utility unbounded at zero, the only hypothesis left is one inequality: the asset cap must sit above the uniform bound on saving that the marginal bound `m` produces. Nothing about @@ -891,8 +889,6 @@ theorem concaveOn_consumptionFn_of_oscSpread {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 (fun n a ha z' => P.crra_policyOf_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc (hslices n) P.oscSpread_nonneg (P.oscOn_iterate n) hlt ha z')) z - - /-! ### A slope bound from the OSCILLATION rather than the level `contSlopeConstOf` prices the continuation's slope by `‖v‖`, which is what the CRRA arguments diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean index 282b50119..2903b2419 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationLipschitz.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationBounds public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements - /-! # The Bellman operator preserves Lipschitz continuity in assets diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean index 6f1d071f4..babc9165f 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuation public import LeanPool.AiyagariEquilibrium.Analysis.DecreasingIncrements - /-! # The optimal policy is increasing in assets diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean index 2fa6ae8a7..d4d013ff6 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRate.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRegion public import LeanPool.AiyagariEquilibrium.Distribution.Feller - /-! # Varying the interest rate diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean index b8dcd595a..4ce938713 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationConsumption public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate - /-! # Light (2018) Theorem 1: saving rises with the interest rate diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean index a922e151d..369fa6402 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateMonotone - /-! # Light (2018), step 5: increasing differences propagate across rates @@ -109,7 +108,6 @@ theorem consumption_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (z : simp only [consumption, resources, IncomeFluctuation.withRate_income, IncomeFluctuation.withRate_interest, max_eq_right ha, max_eq_right hscale, harg] - /-- **The whole solution at `(a, r₁)` is the solution at `(t a, r₂)`.** -/ theorem bellmanFn_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) (v : (ℝ × Z) →ᵇ ℝ) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : @@ -197,7 +195,6 @@ theorem consumptionFnOf_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) ( simp only [consumptionFnOf] rw [P.policyOf_withRate h₁ h₂ hr hv z ha, P.consumption_withRate h₁ h₂ z _ ha.1] - /-! ### The derivative assembly With the budget coincidence in hand the whole comparison lives in ONE economy: the difference to diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean index f350d860e..a5e0d36f6 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean @@ -7,7 +7,6 @@ Authors: Robert Kirkby module public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationCrossRate - /-! # The contraction, run on the compact region alone diff --git a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean index 696465da6..ceb053e29 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.MinimalMPC public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRateStep public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationHARA - /-! # Light's Theorem 1 at large `β` diff --git a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean index 45f2e8552..5e565c9f2 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean @@ -10,7 +10,6 @@ public import LeanPool.AiyagariEquilibrium.Models.EulerCorner public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness public import LeanPool.AiyagariEquilibrium.Models.MonotoneTransitions - /-! # The Doeblin chain at large `β` diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index df93109b2..55ca9dcd4 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate public import LeanPool.AiyagariEquilibrium.Analysis.HARA - /-! # The minimal marginal propensity to consume @@ -171,7 +170,7 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) have hcA : c = P.resources (a, z) - A := rfl have hc0 : 0 < c := hposW z a ha rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 - · nlinarith [hκ1, hc0, hcA, hA0] + · nlinarith only [hκ1, hc0, hcA, hA0] · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => P.policyOf_lt_maxSaving_of_pos (hposv z' A hAmem) (hcapv A hAmem z') have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hposv z' A hAmem @@ -189,7 +188,7 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) simp only [resources, max_eq_right hAmem.1] have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') rw [hres, hXdef] - nlinarith [hκ0] + nlinarith only [hκ0, hinc] have hsum : ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) ≤ X ^ (-γ) := by calc ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) @@ -207,7 +206,8 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) linarith [hchain] have hXTc : X ≤ T * c := le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc rw [hXdef] at hXTc - nlinarith [hXTc, hkey, hR, hcA] + refine le_of_mul_le_mul_right (a := 1 + P.interest) ?_ hR + linear_combination hXTc - c * hkey - (P.minMPC γ * (1 + P.interest)) * hcA /-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, uniformly in the horizon — so it passes to the value function. @@ -248,7 +248,7 @@ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) rw [hz]; simp [consumption] rw [heq] - nlinarith [hres, hκ1] + nlinarith only [hres, hκ1] | succ k ih => intro z a ha have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) @@ -373,7 +373,7 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) rw [hz]; simp [consumption] rw [heq] - nlinarith [hres, hκ1] + nlinarith only [hres, hκ1] exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ | succ k ih => obtain ⟨ihm, ihc⟩ := ih @@ -461,7 +461,7 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) rw [hz]; simp [consumption] rw [heq] - nlinarith [hres, hκ1, hη] + nlinarith only [hres, hκ1, hη] | succ k ih => intro z a ha have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) @@ -478,7 +478,7 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : have := hpos (k + 1) z a ha rwa [hiter] at this rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 - · nlinarith [hκ1, hc0, hcA, hA0, hη] + · nlinarith only [hκ1, hc0, hcA, hA0, hη] · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => P.policyOf_lt_maxSaving_of_pos (hpos k z' A hAmem) (hcap k A hAmem z') have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hpos k z' A hAmem @@ -500,7 +500,7 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : simp only [resources, max_eq_right hAmem.1] have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') rw [hres, hXdef] - nlinarith [hκ0] + nlinarith only [hκ0, hinc] have hsum : ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) ≤ X ^ (-γ) := by calc ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) @@ -520,7 +520,9 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc rw [hXdef] at hXTc -- `κR + Þ = R`, and the extra `η` term has the right sign because `κ ≥ 0` - nlinarith [hXTc, hkey, hR, hcA, hκ0, hη, hT0] + refine le_of_mul_le_mul_right (a := 1 + P.interest) ?_ hR + linear_combination hXTc - (η + c) * hkey + - (P.minMPC γ * (1 + P.interest)) * hcA + mul_nonneg hκ0.le hη /-- **The shifted-CRRA bound at the value function.** -/ theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) @@ -682,6 +684,4 @@ theorem crra_exists_exhaust_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr end IncomeFluctuation - - end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean index 81b68abbf..a7c1c0b67 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean @@ -9,7 +9,6 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationEuler public import LeanPool.AiyagariEquilibrium.Models.ImpatientDecline public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate - /-! # Persistent income: the decline condition from monotone transitions diff --git a/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean index 8bf12b386..97929dbdf 100644 --- a/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean +++ b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Models.ConsumptionFloor public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationLipschitz - /-! # A natural asset bound for log utility @@ -228,9 +227,16 @@ theorem log_consumption_linear_lower_bound' (hpc : P.PositiveConsumption) (hu : rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope rw [hd1, hd2] at hslope rw [le_div_iff₀ hθ0] + have hbound : P.cont z d - P.cont z assetFloor ≤ + 2 * ‖P.toExtended.valueFunction‖ := by linarith only [hb1.2, hb0.1] have hkey : (P.cont z b - P.cont z d) * θ * (b - assetFloor) ≤ 2 * ‖P.toExtended.valueFunction‖ * (1 - θ) * (b - assetFloor) := by - nlinarith [hslope, hb1.2, hb0.1, hbf, h1θ] + calc + _ = (P.cont z b - P.cont z d) * (θ * (b - assetFloor)) := by ring + _ ≤ (P.cont z d - P.cont z assetFloor) * ((1 - θ) * (b - assetFloor)) := hslope + _ ≤ 2 * ‖P.toExtended.valueFunction‖ * ((1 - θ) * (b - assetFloor)) := + mul_le_mul_of_nonneg_right hbound (mul_nonneg h1θ.le hbf.le) + _ = _ := by ring exact le_of_mul_le_mul_right hkey hbf have heq : (P.discount : ℝ) * (2 * ‖P.toExtended.valueFunction‖ * (1 - θ) / θ) = G * (1 - θ) / θ := by @@ -248,7 +254,7 @@ theorem log_consumption_linear_lower_bound' (hpc : P.PositiveConsumption) (hu : rw [div_le_div_iff₀ (by linarith) hθ0] at hchain have hcancel : θ * (b - assetFloor) ≤ G * (c + (1 - θ) * (b - assetFloor)) := by refine le_of_mul_le_mul_left ?_ h1θ - nlinarith [hchain] + convert hchain using 1 <;> ring exact two_gap_cancel hθkey hcancel rw [hres, div_le_iff₀ (by linarith)] linarith [hb2] @@ -332,8 +338,6 @@ theorem log_policy_eq_zero_of_resources (hdom : P.Unbounded) (hu : P.u = Real.lo linarith) hlt - - end IncomeFluctuation /-- **The bound is not vacuous.** At `r = 0` the interest-rate hypothesis holds automatically, @@ -347,5 +351,4 @@ theorem logImpatient_natural_asset_bound (z : Fin 2) : rw [hint] linarith - end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean b/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean index ee1caf5c9..a790ce3a5 100644 --- a/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean +++ b/LeanPool/AiyagariEquilibrium/Models/Tauchen.lean @@ -9,7 +9,6 @@ public import Mathlib.Probability.CDF public import Mathlib.Probability.Distributions.Gaussian.Real public import LeanPool.AiyagariEquilibrium.Models.MonotoneTransitions - /-! # Tauchen (1986) chains have monotone transitions diff --git a/LeanPool/AiyagariEquilibrium/Topology/Berge.lean b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean index 6ba0f849e..56fb12178 100644 --- a/LeanPool/AiyagariEquilibrium/Topology/Berge.lean +++ b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean @@ -6,10 +6,8 @@ Authors: Robert Kirkby module public import Mathlib.Topology.Semicontinuity.Hemicontinuity -public import Mathlib.Topology.Order.Compact public import Mathlib.Topology.Instances.Real.Lemmas - /-! # Berge's maximum theorem diff --git a/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean b/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean index a6d4890c8..8f37bd5ca 100644 --- a/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean +++ b/LeanPool/AiyagariEquilibrium/Topology/BergeEReal.lean @@ -8,7 +8,6 @@ module public import LeanPool.AiyagariEquilibrium.Topology.Berge public import Mathlib.Topology.Instances.EReal.Lemmas - /-! # Berge's maximum theorem for extended-real objectives diff --git a/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean b/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean index c6ef973b4..d0a10341d 100644 --- a/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean +++ b/LeanPool/AiyagariEquilibrium/Topology/IccCorrespondence.lean @@ -9,7 +9,6 @@ public import Mathlib.Topology.Semicontinuity.Hemicontinuity public import Mathlib.Topology.MetricSpace.Thickening public import Mathlib.Topology.Instances.Real.Lemmas - /-! # A closed interval with moving endpoints is a continuous correspondence From 8ae1429ef04136cda4a866d0b388f400786da44a Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 10:22:24 +0000 Subject: [PATCH 03/19] Reduce repeated nonlinear search in consumption iteration --- .../Models/IncomeFluctuationIterate.lean | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index a6d192df2..9dbe2a573 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -238,7 +238,7 @@ theorem consumptionFnOf_mono {v : (ℝ × Z) →ᵇ ℝ} simp only [objROf, he1, he2] at hI hII have hIIeq : P.objROf v (a', z) (b + Δ) = P.objROf v (a', z) b' := by simp only [objROf, he1] - nlinarith [hI, hII, hscaled] + nlinarith only [hI, hII, hscaled] have hbell : P.toExtended.objectiveE v (a', z) (b + Δ) = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, @@ -468,7 +468,7 @@ theorem policyOf_le_of_marginal_bound {v : (ℝ × Z) →ᵇ ℝ} have hmg := hmarg (c + (b - assetFloor) / 2) c hcdom (by linarith) hcmax rw [show c + (b - assetFloor) / 2 - c = (b - assetFloor) / 2 from by ring] at hmg rw [← sub_le_iff_le_add', le_div_iff₀ hm] - nlinarith [hopt, hmg, mul_le_mul_of_nonneg_left hloss hβ] + nlinarith only [hopt, hmg, mul_le_mul_of_nonneg_left hloss hβ] /-- The Bellman operator adds at most the reward and contracts what it inherits. -/ theorem norm_bellman_le (v : (ℝ × Z) →ᵇ ℝ) : @@ -792,10 +792,12 @@ theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h rw [div_le_div_iff₀ (show (0 : ℝ) < b - w by linarith) (show (0 : ℝ) < w - assetFloor by linarith), hbw, hwf] at hslope have h2 : (P.contOf v z b - P.contOf v z w) * (θ * (b - assetFloor)) - ≤ G * ((1 - θ) * (b - assetFloor)) := by - nlinarith [hslope, hgap, mul_pos (show (0:ℝ) < 1 - θ by linarith) hbf] + ≤ G * ((1 - θ) * (b - assetFloor)) := + hslope.trans (mul_le_mul_of_nonneg_right hgap + (mul_nonneg (sub_pos.mpr hθ1).le hbf.le)) rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] - nlinarith [h2, hbf] + refine le_of_mul_le_mul_right (a := b - assetFloor) ?_ hbf + convert h2 using 1 <;> ring -- the marginal gain, priced where it lands have hmg : (c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by @@ -986,7 +988,7 @@ theorem exists_consumptionFnOf_floor_of_marginal {M G : ℝ} linarith [hopt, hscaled, e.le, e.ge] have hmarg := hδ c d hcmem (by linarith) hdδ rw [hhdef] at hh0 - nlinarith [hmarg, hutil, hh0, hlt] + nlinarith only [hmarg, hutil, hh0, hlt] /-- **Positive consumption from a finite marginal bound**, against every continuation with concave slices and oscillation at most `G`. -/ From a34eb839bcba47a2a1f96c957cab88db0a19a05a Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 10:42:47 +0000 Subject: [PATCH 04/19] Share household proof arguments and update model scope notes --- .../Equilibrium/Aiyagari.lean | 50 +++---- LeanPool/AiyagariEquilibrium/Models/CRRA.lean | 45 +++---- .../Models/IncomeFluctuation.lean | 64 ++++++--- .../IncomeFluctuationCarrollKimball.lean | 68 +--------- .../Models/IncomeFluctuationEnvelope.lean | 61 +++++---- .../Models/IncomeFluctuationEuler.lean | 40 +----- .../Models/IncomeFluctuationIterate.lean | 115 ++-------------- .../Models/IncomeFluctuationMonotone.lean | 127 ++++++++++++------ 8 files changed, 214 insertions(+), 356 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean index 1df88fc2e..4da3774ef 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Aiyagari.lean @@ -23,9 +23,10 @@ The firm side is left abstract: any continuous capital demand schedule `D` will Cobb-Douglas firm with depreciation is one instance among many, and nothing here depends on which. -What is assumed rather than proved is that capital supply moves continuously with the -interest rate. That is the remaining gap, and it is a real one -- see the note at the end of -this file. +This module takes continuity of capital supply as an explicit hypothesis. The later +`AiyagariContinuity` module proves it for a fixed-cap, zero-borrowing-floor rate family from a +stationary selection that is unique at each rate; its equilibrium theorem also requires a +continuous demand schedule and opposite signs of excess demand at the interval endpoints. -/ @[expose] public section @@ -117,34 +118,17 @@ end Equilibrium end LeanEconomics /-! -## What remains - -`exists_aiyagari_equilibrium` takes continuity of capital supply `S` as a hypothesis. The -pieces already proved discharge part of it and identify exactly what is missing. - -Given a family of household problems indexed by the interest rate: - -1. `continuousAt_fixedPoint` (in `DynamicProgramming/Parametric.lean`) reduces continuity of - `r ↦ valueFunction r` to continuity of `r ↦ bellman r v₀` at the SINGLE function - `v₀ = valueFunction r₀`, in sup norm. The Blackwell contraction modulus is the discount - factor, which does not vary with `r`, so the common-modulus hypothesis is free. -2. From a continuous value function, continuity of the policy in `r` would follow by the same - Berge argument that already gives continuity in the state, since the maximiser is unique. -3. From a continuous policy, `r ↦ push r` is weakly continuous, so the set of stationary - distributions has closed graph; with UNIQUENESS (`stationary_unique_of_exhausts`) a - convergent subnet argument on the compact space of probability measures gives continuity - of `r ↦ μ r`. -4. `continuous_aggregateCapital` then gives continuity of `S`, since the asset coordinate is - bounded and continuous. This step is proved. - -Step 1 is the substantial one: it needs a sup-norm (not pointwise) estimate, uniform over the -state space, and the extended-real reward makes it delicate near zero consumption, where -utility falls to `-∞`. The estimate is true -- the optimum keeps consumption bounded away -from zero because the value is finite -- but proving it uniformly in `r` is real work. - -A second obstacle is structural rather than mathematical: `assetCap` is a FIELD of -`IncomeFluctuation`, so `(Pf r).State` is a different type for each `r`. Any statement -quantifying over the family across rates has to either fix the cap across the family or carry -transports. That is why the theorem above quantifies the distribution inside an existential, -where the dependence causes no trouble. +## Rate continuity and the remaining hypotheses + +`IncomeFluctuation Z assetFloor assetCap` takes the asset bounds as parameters. Fixing them +across a rate family gives a common state space for its stationary distributions. + +For a zero borrowing floor, `IncomeFluctuation.continuousOn_capitalSupply` in +`Equilibrium/AiyagariContinuity.lean` proves rate continuity from a selection of stationary +distributions and uniqueness at every rate of a closed interval. Its +`IncomeFluctuation.exists_equilibrium_of_selection` then applies the intermediate value +theorem, assuming a continuous capital-demand schedule and endpoint sign inequalities. +The stationary selection, its uniqueness and the endpoint inequalities remain explicit +hypotheses of that result. The more abstract theorem in this file also allows other fixed +borrowing floors, while taking continuity of the realised supply schedule as a hypothesis. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean index 4843e1722..f1e653d41 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean @@ -11,35 +11,22 @@ public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion /-! # CRRA utility, and where it fits -Two separate strands of the development each ask something of `u`, and this file specialises to -the CRRA family to see what they ask together. - -* The income fluctuation structure requires `tendsto_atBot_u`: utility falls to `-∞` as - consumption vanishes. That is what makes consumption positive at the optimum without a floor, - and everything downstream — the policy, the distribution, the equilibrium — rests on it. -* Light (2018) requires relative risk aversion at most one, `RelativeRiskAversionLeOne`. - -Within CRRA the first holds exactly when `γ ≥ 1` and the second exactly when `γ ≤ 1`, so -**together they hold only at `γ = 1`: log utility**. That is `crra_pinch`, the point of this file. - -The pinch is sharp in both directions and neither side is an artefact of the proofs. For `γ < 1` -period utility is bounded below by zero, so it cannot tend to `-∞` under any argument -(`not_tendsto_atBot_crraUtility`); for `γ > 1` the utility is strictly CONCAVE in log consumption, -so `RelativeRiskAversionLeOne` fails as badly as it can -(`strictConcaveOn_crra_comp_exp`). - -## What this says about the uniqueness programme - -The formalisation's only worked witness, `NonVacuity.impatient`, uses `u c = -c⁻¹`, which is CRRA -with `γ = 2` (`crraUtility_two`). It satisfies the existence machinery and fails Light's -condition. Log utility is the one CRRA calibration where both can hold, and it sits on the -boundary of each — `u ∘ exp` is affine there, neither convex-strictly nor concave-strictly. - -This is the parametric cost of the uniqueness argument stated exactly, and it matches what the -survey literature reports: Toda and Walsh (2024) call `RRA ≤ 1` "a quite strong parametric -restriction". Escaping `γ = 1` means giving up `tendsto_atBot_u`, which means carrying a -consumption floor or an Inada-free treatment of the corner — a different development, not a -different proof. +This file compares two conditions on the CRRA utility family: + +* `Tendsto u (𝓝[>] 0) atBot`, which gives unbounded-below utility at zero consumption; +* Light's relative-risk-aversion bound `RelativeRiskAversionLeOne`. + +For CRRA the first holds exactly when `γ ≥ 1` and the second exactly when `γ ≤ 1`. +`crra_pinch` records that both hold only for log utility, `γ = 1`. For `γ < 1`, +`not_tendsto_atBot_crraUtility` proves utility is bounded below; for `γ > 1`, +`strictConcaveOn_crra_comp_exp` rules out the relative-risk-aversion bound. + +The household structure itself supports both cases. Its utility domain can be `Ioi 0` or +`Ici 0`, with continuity of the extended reward in place of an unconditional divergence +assumption. `IncomeFluctuation.Unbounded` recovers divergence in the open-domain case. +`BoundedIncomeFluctuation` supplies a bounded-utility CES model, and the later iteration +machinery obtains its positive consumption from a marginal Inada condition. Thus the pinch +restricts the unbounded-below CRRA subclass, rather than every household model in the library. ## The single derivative formula diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean index c67acc6eb..f4c93432c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean @@ -551,13 +551,13 @@ theorem concaveOn_valueFunction (z : Z) : P.toExtended.blackwell P.toExtended.discount_lt_one (fun v hv => P.concaveOn_bellman v hv) z -/-- **The optimal action is unique.** -/ -theorem optimal_action_unique {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a₀ a₁ : ℝ} +/-- **The optimal action is unique** for any continuation with concave slices. -/ +theorem optimal_action_unique_of_concave {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) + {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a₀ a₁ : ℝ} (h₀ : a₀ ∈ P.toExtended.feasible s) (h₁ : a₁ ∈ P.toExtended.feasible s) - (hm₀ : P.toExtended.objectiveE P.toExtended.valueFunction s a₀ - = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) - (hm₁ : P.toExtended.objectiveE P.toExtended.valueFunction s a₁ - = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) : + (hm₀ : P.toExtended.objectiveE v s a₀ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) + (hm₁ : P.toExtended.objectiveE v s a₁ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) : a₀ = a₁ := by by_contra hne obtain ⟨hc₀, hb₀⟩ := P.bellmanFn_eq_of_optimal hs h₀ hm₀ @@ -565,7 +565,8 @@ theorem optimal_action_unique {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetC have hhalf : (0 : ℝ) < 1 / 2 := by norm_num have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num have hmem : (1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁ ∈ P.toExtended.feasible s := by - simpa using convex_Icc assetFloor (P.maxSaving s) h₀ h₁ hhalf.le hhalf.le hsum + rw [P.feasible_eq] at h₀ h₁ ⊢ + exact convex_Icc assetFloor (P.maxSaving s) h₀ h₁ hhalf.le hhalf.le hsum have hcmid : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) = (1 / 2 : ℝ) * P.consumption s a₀ + (1 / 2 : ℝ) * P.consumption s a₁ := by simp only [consumption]; ring @@ -577,36 +578,43 @@ theorem optimal_action_unique {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetC intro h exact hne (by linarith) have hu := P.strictConcaveOn_u_dom.2 hc₀ hc₁ hcne hhalf hhalf hsum - have hexp : (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * P.toExtended.valueFunction (a₀, z')) - + (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * P.toExtended.valueFunction (a₁, z')) - ≤ ∑ z', P.transitionMatrix s.2 z' - * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') := by + have hexp : (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₀, z')) + + (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₁, z')) + ≤ ∑ z', P.transitionMatrix s.2 z' * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') := by rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] refine Finset.sum_le_sum fun z' _ => ?_ - have hvz := (P.concaveOn_valueFunction z').2 (P.feasible_subset_region h₀) - (P.feasible_subset_region h₁) hhalf.le hhalf.le hsum + have hvz := (hv z').2 (P.feasible_subset_region h₀) (P.feasible_subset_region h₁) + hhalf.le hhalf.le hsum simp only [smul_eq_mul] at hvz nlinarith [P.transitionMatrix_nonneg s.2 z'] have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg have hscaled := mul_le_mul_of_nonneg_left hexp hβ simp only [smul_eq_mul] at hu - have hle := P.toExtended.le_bellmanFn P.toExtended.valueFunction hmem - have hobj : P.toExtended.objectiveE P.toExtended.valueFunction s - ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) + have hle := P.toExtended.le_bellmanFn v hmem + have hobj : P.toExtended.objectiveE v s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) = ((P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + P.discount * ∑ z', P.transitionMatrix s.2 z' - * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') - : ℝ) : EReal) := by + * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') : ℝ) : EReal) := by rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs hmem hcm, ← EReal.coe_add] rfl rw [hobj] at hle have hle' : P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) + P.discount * ∑ z', P.transitionMatrix s.2 z' - * P.toExtended.valueFunction ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') - ≤ P.toExtended.bellmanFn P.toExtended.valueFunction s := by exact_mod_cast hle + * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') + ≤ P.toExtended.bellmanFn v s := by exact_mod_cast hle rw [hcmid] at hle' linarith +/-- **The optimal action is unique.** -/ +theorem optimal_action_unique {s : ℝ × Z} (hs : s.1 ∈ Icc assetFloor assetCap) {a₀ a₁ : ℝ} + (h₀ : a₀ ∈ P.toExtended.feasible s) (h₁ : a₁ ∈ P.toExtended.feasible s) + (hm₀ : P.toExtended.objectiveE P.toExtended.valueFunction s a₀ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) + (hm₁ : P.toExtended.objectiveE P.toExtended.valueFunction s a₁ + = ((P.toExtended.bellmanFn P.toExtended.valueFunction s : ℝ) : EReal)) : + a₀ = a₁ := by + exact P.optimal_action_unique_of_concave P.concaveOn_valueFunction hs h₀ h₁ hm₀ hm₁ + /-- The optimal saving choice. -/ noncomputable def policy (s : ℝ × Z) : ℝ := Classical.choose (P.toExtended.exists_optimal_action P.toExtended.valueFunction s) @@ -621,6 +629,22 @@ theorem policy_optimal (s : ℝ × Z) : (Classical.choose_spec (P.toExtended.exists_optimal_action P.toExtended.valueFunction s)).2 +/-- The optimal saving choice under an arbitrary continuation. At the value function this is +definitionally `policy`. -/ +noncomputable def policyOf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : ℝ := + Classical.choose (P.toExtended.exists_optimal_action v s) + +theorem policyOf_mem (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : + P.policyOf v s ∈ P.toExtended.feasible s := + (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).1 + +theorem policyOf_optimal (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : + P.toExtended.objectiveE v s (P.policyOf v s) + = ((P.toExtended.bellmanFn v s : ℝ) : EReal) := + (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).2 + +theorem policyOf_valueFunction : P.policyOf P.toExtended.valueFunction = P.policy := rfl + theorem policy_mem_region (s : ℝ × Z) : P.policy s ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policy_mem s) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean index aa511011f..e395828b8 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -134,9 +134,9 @@ theorem concaveSlices_valueFunction : /-! ### The optimal action of an arbitrary continuation -`IncomeFluctuation` gives uniqueness of the optimal action at the value function. The closure -argument needs it along the whole approximating sequence, so the same proof is run with any -continuation whose slices are concave. -/ +`IncomeFluctuation` proves uniqueness for every continuation whose asset slices are concave. +The closure argument applies that shared theorem along the whole approximating sequence; the +wrapper below gives its hypothesis the name `ConcaveSlices`. -/ /-- **The optimal action is unique** for any continuation with concave slices. -/ theorem optimal_action_unique_of_concaveSlices {v : (ℝ × Z) →ᵇ ℝ} @@ -146,67 +146,7 @@ theorem optimal_action_unique_of_concaveSlices {v : (ℝ × Z) →ᵇ ℝ} (hm₀ : P.toExtended.objectiveE v s a₀ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) (hm₁ : P.toExtended.objectiveE v s a₁ = ((P.toExtended.bellmanFn v s : ℝ) : EReal)) : a₀ = a₁ := by - by_contra hne - obtain ⟨hc₀, hb₀⟩ := P.bellmanFn_eq_of_optimal hs h₀ hm₀ - obtain ⟨hc₁, hb₁⟩ := P.bellmanFn_eq_of_optimal hs h₁ hm₁ - have hhalf : (0 : ℝ) < 1 / 2 := by norm_num - have hsum : (1 : ℝ) / 2 + 1 / 2 = 1 := by norm_num - have hmem : (1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁ ∈ P.toExtended.feasible s := by - rw [P.feasible_eq] at h₀ h₁ ⊢ - exact convex_Icc assetFloor (P.maxSaving s) h₀ h₁ hhalf.le hhalf.le hsum - have hcmid : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) - = (1 / 2 : ℝ) * P.consumption s a₀ + (1 / 2 : ℝ) * P.consumption s a₁ := by - simp only [consumption]; ring - have hcm : P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) ∈ P.dom := by - rw [hcmid] - simpa only [smul_eq_mul] using P.convex_dom hc₀ hc₁ hhalf.le hhalf.le hsum - have hcne : P.consumption s a₀ ≠ P.consumption s a₁ := by - simp only [consumption] - intro h - exact hne (by linarith) - have hu := P.strictConcaveOn_u_dom.2 hc₀ hc₁ hcne hhalf hhalf hsum - have hexp : (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₀, z')) - + (1 / 2 : ℝ) * (∑ z', P.transitionMatrix s.2 z' * v (a₁, z')) - ≤ ∑ z', P.transitionMatrix s.2 z' * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') := by - rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] - refine Finset.sum_le_sum fun z' _ => ?_ - have hvz := (hv z').2 (P.feasible_subset_region h₀) (P.feasible_subset_region h₁) - hhalf.le hhalf.le hsum - simp only [smul_eq_mul] at hvz - nlinarith [P.transitionMatrix_nonneg s.2 z'] - have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg - have hscaled := mul_le_mul_of_nonneg_left hexp hβ - simp only [smul_eq_mul] at hu - have hle := P.toExtended.le_bellmanFn v hmem - have hobj : P.toExtended.objectiveE v s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁) - = ((P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) - + P.discount * ∑ z', P.transitionMatrix s.2 z' - * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') : ℝ) : EReal) := by - rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom hs hmem hcm, ← EReal.coe_add] - rfl - rw [hobj] at hle - have hle' : P.u (P.consumption s ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁)) - + P.discount * ∑ z', P.transitionMatrix s.2 z' - * v ((1 / 2 : ℝ) * a₀ + (1 / 2 : ℝ) * a₁, z') - ≤ P.toExtended.bellmanFn v s := by exact_mod_cast hle - rw [hcmid] at hle' - linarith - -/-- The optimal saving choice under an arbitrary continuation. At the value function this is -definitionally `policy`. -/ -noncomputable def policyOf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : ℝ := - Classical.choose (P.toExtended.exists_optimal_action v s) - -theorem policyOf_mem (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : - P.policyOf v s ∈ P.toExtended.feasible s := - (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).1 - -theorem policyOf_optimal (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) : - P.toExtended.objectiveE v s (P.policyOf v s) - = ((P.toExtended.bellmanFn v s : ℝ) : EReal) := - (Classical.choose_spec (P.toExtended.exists_optimal_action v s)).2 - -theorem policyOf_valueFunction : P.policyOf P.toExtended.valueFunction = P.policy := rfl + exact P.optimal_action_unique_of_concave hv hs h₀ h₁ hm₀ hm₁ /-- The consumption function implied by an arbitrary continuation. -/ noncomputable def consumptionFnOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a : ℝ) : ℝ := diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean index 43c327ebb..de753f1be 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean @@ -116,18 +116,24 @@ work is the neighbourhood: the frozen saving is feasible at the state it came fr poorer states it need not be, so the lower bound is only local. Continuity of the feasible boundary gives an interval on which it survives, provided the saving is interior there. -/ -theorem hasDerivAt_valueFunction (hpc : P.PositiveConsumption) {z : Z} {a du : ℝ} +/-- **The envelope condition for `bellman v`.** The marginal value of assets is the gross +interest rate times the marginal utility of the consumption the household actually chooses. +Clausen and Strub's sandwich, run one step of the iteration at a time rather than at the fixed +point. -/ +theorem hasDerivAt_bellman_of_optimal {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a a' du : ℝ} (ha : assetFloor < a) (hacap : a < assetCap) - (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) - fun x => P.toExtended.valueFunction (x, z)) - (hint : P.policy (a, z) < P.maxSaving (a, z)) - (hu : HasDerivAt P.u du (P.resources (a, z) - P.policy (a, z))) : - HasDerivAt (fun x => P.toExtended.valueFunction (x, z)) ((1 + P.interest) * du) a := by - set V := P.toExtended.valueFunction with hV - set a' := P.policy (a, z) with ha' + (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) fun x => (P.toExtended.bellman v) (x, z)) + (ha'mem : a' ∈ P.toExtended.feasible (a, z)) + (hopt : P.toExtended.objectiveE v (a, z) a' + = ((P.toExtended.bellmanFn v (a, z) : ℝ) : EReal)) + (hint : a' < P.maxSaving (a, z)) + (hc : 0 < P.consumption (a, z) a') + (hu : HasDerivAt P.u du (P.consumption (a, z) a')) : + HasDerivAt (fun x => (P.toExtended.bellman v) (x, z)) ((1 + P.interest) * du) a := by have hamem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := ⟨ha.le, hacap.le⟩ - have hc : 0 < P.consumption (a, z) a' := P.consumption_policy_pos hpc hamem - have ha'0 : assetFloor ≤ a' := (P.policy_mem (a, z)).1 + have ha'0 : assetFloor ≤ a' := ha'mem.1 + have hba : ∀ x : ℝ, P.toExtended.bellmanFn v (x, z) = (P.toExtended.bellman v) (x, z) := + fun x => by rw [← P.toExtended.bellman_apply] -- the frozen saving stays feasible, and consumption stays positive, near `a` have hcms : ContinuousAt (fun x => P.maxSaving (x, z)) a := (P.continuous_maxSaving.comp (continuous_id.prodMk continuous_const)).continuousAt @@ -142,31 +148,38 @@ theorem hasDerivAt_valueFunction (hpc : P.PositiveConsumption) {z : Z} {a du : have h4 : ∀ᶠ x in 𝓝 a, 0 < P.consumption (x, z) a' := hccon.eventually_const_lt hc filter_upwards [h1, h2, h3, h4] with x hx1 hx2 hx3 hx4 using ⟨hx1, hx2, hx3, hx4⟩ obtain ⟨l, r, hmem, hsub⟩ := mem_nhds_iff_exists_Ioo_subset.mp hev - -- on that interval the lazy value is a lower bound have hIsub : Ioo l r ⊆ Icc assetFloor assetCap := fun x hx => ⟨(hsub hx).1.le, (hsub hx).2.1.le⟩ - have hlow : ∀ x ∈ Ioo l r, P.lazyValue V z a' x ≤ V (x, z) := by + have hlow : ∀ x ∈ Ioo l r, P.lazyValue v z a' x ≤ (P.toExtended.bellman v) (x, z) := by intro x hx obtain ⟨hx1, hx2, hx3, hx4⟩ := hsub hx have hfe : a' ∈ P.toExtended.feasible (x, z) := ⟨ha'0, hx3.le⟩ - have := P.lazyValue_le V z ⟨hx1.le, hx2.le⟩ hfe hx4 - rwa [hV, show P.toExtended.bellmanFn P.toExtended.valueFunction (x, z) - = P.toExtended.valueFunction (x, z) from by - rw [← P.toExtended.bellman_apply, P.toExtended.bellman_valueFunction]] at this - -- and it touches at `a` - have htouch : P.lazyValue V z a' a = V (a, z) := by - have hfe : a' ∈ P.toExtended.feasible (a, z) := P.policy_mem (a, z) - have := P.lazyValue_eq V z hamem hfe hc (P.policy_optimal (a, z)) - rwa [hV, show P.toExtended.bellmanFn P.toExtended.valueFunction (a, z) - = P.toExtended.valueFunction (a, z) from by - rw [← P.toExtended.bellman_apply, P.toExtended.bellman_valueFunction]] at this + have := P.lazyValue_le v z ⟨hx1.le, hx2.le⟩ hfe hx4 + rwa [hba x] at this + have htouch : P.lazyValue v z a' a = (P.toExtended.bellman v) (a, z) := by + have hfe : a' ∈ P.toExtended.feasible (a, z) := ha'mem + have := P.lazyValue_eq v z hamem hfe hc hopt + rwa [hba a] at this refine (hconc.subset hIsub (convex_Ioo l r)).hasDerivAt_of_lowerBound hmem (Ioo_mem_nhds hmem.1 hmem.2) ⟨(l + a) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, by linarith [hmem.1, hmem.2]⟩ ⟨(a + r) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, by linarith [hmem.1, hmem.2]⟩ - hlow htouch (P.hasDerivAt_lazyValue V z a' ha hu) + hlow htouch (P.hasDerivAt_lazyValue v z a' ha hu) + +theorem hasDerivAt_valueFunction (hpc : P.PositiveConsumption) {z : Z} {a du : ℝ} + (ha : assetFloor < a) (hacap : a < assetCap) + (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) + fun x => P.toExtended.valueFunction (x, z)) + (hint : P.policy (a, z) < P.maxSaving (a, z)) + (hu : HasDerivAt P.u du (P.resources (a, z) - P.policy (a, z))) : + HasDerivAt (fun x => P.toExtended.valueFunction (x, z)) ((1 + P.interest) * du) a := by + simpa only [P.toExtended.bellman_valueFunction] using + P.hasDerivAt_bellman_of_optimal ha hacap + (by simpa only [P.toExtended.bellman_valueFunction] using hconc) + (P.policy_mem (a, z)) (P.policy_optimal (a, z)) hint + (P.consumption_policy_pos hpc ⟨ha.le, hacap.le⟩) hu /-! ### At the borrowing constraint diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean index 4f5525834..78751f8af 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEuler.lean @@ -77,44 +77,8 @@ theorem hasDerivAt_bellman {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a du : ℝ} (hc : 0 < P.consumptionFnOf v z a) (hu : HasDerivAt P.u du (P.consumptionFnOf v z a)) : HasDerivAt (fun x => (P.toExtended.bellman v) (x, z)) ((1 + P.interest) * du) a := by - set a' := P.policyOf v (a, z) with ha' - have hamem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := ⟨ha.le, hacap.le⟩ - have ha'0 : assetFloor ≤ a' := (P.policyOf_mem v (a, z)).1 - have hba : ∀ x : ℝ, P.toExtended.bellmanFn v (x, z) = (P.toExtended.bellman v) (x, z) := - fun x => by rw [← P.toExtended.bellman_apply] - -- the frozen saving stays feasible, and consumption stays positive, near `a` - have hcms : ContinuousAt (fun x => P.maxSaving (x, z)) a := - (P.continuous_maxSaving.comp (continuous_id.prodMk continuous_const)).continuousAt - have hccon : ContinuousAt (fun x => P.consumption (x, z) a') a := - ((P.continuous_resources.comp (continuous_id.prodMk continuous_const)).sub - continuous_const).continuousAt - have hev : {x : ℝ | assetFloor < x ∧ x < assetCap ∧ a' < P.maxSaving (x, z) - ∧ 0 < P.consumption (x, z) a'} ∈ 𝓝 a := by - have h1 : ∀ᶠ x in 𝓝 a, assetFloor < x := lt_mem_nhds ha - have h2 : ∀ᶠ x in 𝓝 a, x < assetCap := gt_mem_nhds hacap - have h3 : ∀ᶠ x in 𝓝 a, a' < P.maxSaving (x, z) := hcms.eventually_const_lt hint - have h4 : ∀ᶠ x in 𝓝 a, 0 < P.consumption (x, z) a' := hccon.eventually_const_lt hc - filter_upwards [h1, h2, h3, h4] with x hx1 hx2 hx3 hx4 using ⟨hx1, hx2, hx3, hx4⟩ - obtain ⟨l, r, hmem, hsub⟩ := mem_nhds_iff_exists_Ioo_subset.mp hev - have hIsub : Ioo l r ⊆ Icc assetFloor assetCap := fun x hx => - ⟨(hsub hx).1.le, (hsub hx).2.1.le⟩ - have hlow : ∀ x ∈ Ioo l r, P.lazyValue v z a' x ≤ (P.toExtended.bellman v) (x, z) := by - intro x hx - obtain ⟨hx1, hx2, hx3, hx4⟩ := hsub hx - have hfe : a' ∈ P.toExtended.feasible (x, z) := ⟨ha'0, hx3.le⟩ - have := P.lazyValue_le v z ⟨hx1.le, hx2.le⟩ hfe hx4 - rwa [hba x] at this - have htouch : P.lazyValue v z a' a = (P.toExtended.bellman v) (a, z) := by - have hfe : a' ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v (a, z) - have := P.lazyValue_eq v z hamem hfe hc (P.policyOf_optimal v (a, z)) - rwa [hba a] at this - refine (hconc.subset hIsub (convex_Ioo l r)).hasDerivAt_of_lowerBound hmem - (Ioo_mem_nhds hmem.1 hmem.2) - ⟨(l + a) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, - by linarith [hmem.1, hmem.2]⟩ - ⟨(a + r) / 2, ⟨by linarith [hmem.1, hmem.2], by linarith [hmem.1, hmem.2]⟩, - by linarith [hmem.1, hmem.2]⟩ - hlow htouch (P.hasDerivAt_lazyValue v z a' ha hu) + exact P.hasDerivAt_bellman_of_optimal ha hacap hconc + (P.policyOf_mem v (a, z)) (P.policyOf_optimal v (a, z)) hint hc hu /-! ### The two lazy bounds on the marginal value of assets diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index 9dbe2a573..de96900bc 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -11,17 +11,15 @@ public import LeanPool.AiyagariEquilibrium.Models.CRRAConstants /-! # Positivity and monotonicity along the iteration -Carroll and Kimball's induction step needs the CONTINUATION's consumption function to be -positive, concave and nondecreasing. Concavity is the induction hypothesis; the other two are -facts, but they are proved in this development only at the FIXED POINT -(`positiveConsumption_of_unbounded`, `consumptionFn_mono`). This file restates them for an -arbitrary continuation, which is what the induction sees. - -Both proofs are the fixed-point proofs with `objR` replaced by `objROf`: the objective read -against an arbitrary continuation. The one structural fact they use is that the continuation -value depends on the saving chosen and the income state, never on current assets, so shifting -both households' plans by their resource gap leaves consumption alone and moves only the -continuation. That is true of `objROf` for exactly the same reason it is true of `objR`. +Carroll and Kimball's induction step needs the continuation's consumption function to be +positive, concave and nondecreasing. Concavity is the induction hypothesis. This file proves +positivity and consumption monotonicity against arbitrary continuations, together with saving +bounds uniform over the iteration. Saving-policy monotonicity is shared with the fixed-point +case in `IncomeFluctuationMonotone`. + +The exchange arguments use `objROf`, the real objective against an arbitrary continuation. +Continuation value depends on chosen saving and the income state, never on current assets, +so shifting household plans between asset levels cancels the continuation terms. -/ @[expose] public section @@ -37,14 +35,6 @@ variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCa /-! ### The objective against an arbitrary continuation -/ -/-- The continuation value of saving `x`, read against `v`. -/ -noncomputable def contOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : ℝ := - ∑ z' : Z, P.transitionMatrix z z' * v (x, z') - -/-- The objective, as a real number, read against `v`. -/ -noncomputable def objROf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) (x : ℝ) : ℝ := - P.u (P.consumption s x) + P.discount * P.contOf v s.2 x - theorem concaveOn_contOf {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.contOf v z) := by refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ @@ -104,93 +94,6 @@ theorem positiveConsumptionAll_of_rich (hrich : assetCap - assetFloor < P.minCon simp only [consumptionFnOf, consumption] linarith -/-- The objective in the reals agrees with the extended objective where consumption is -positive. -/ -theorem objectiveE_eq_coe_of (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} - (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) - (hcx : P.consumption (a, z) x ∈ P.dom) : - P.toExtended.objectiveE v (a, z) x = ((P.objROf v (a, z) x : ℝ) : EReal) := by - rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom (s := (a, z)) ha hx hcx, - ← EReal.coe_add] - rfl - -/-- **Any feasible action with positive consumption is worth at most the optimum**, whatever -the continuation. -/ -theorem objROf_le_of_mem (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} - (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) - (hcx : P.consumption (a, z) x ∈ P.dom) : - P.objROf v (a, z) x ≤ P.objROf v (a, z) (P.policyOf v (a, z)) := by - have hc : P.consumptionFnOf v z a ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have h1 := P.lazyValue_le_dom v z ha hx hcx - have h2 := P.lazyValue_eq_dom v z ha (P.policyOf_mem v (a, z)) hc - (P.policyOf_optimal v (a, z)) - simp only [lazyValue] at h1 h2 - simp only [objROf, contOf, consumption] - linarith - -/-! ### Monotonicity of the saving policy -/ - -/-- **The optimal policy is increasing in assets, whatever the continuation.** The exchange -argument of `policy_mono`, run against `v`. -/ -theorem policyOf_mono {v : (ℝ × Z) →ᵇ ℝ} - (hv : ConcaveSlices assetFloor assetCap v) {a a' : ℝ} {z : Z} - (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : - P.policyOf v (a, z) ≤ P.policyOf v (a', z) := by - by_contra hcon - rw [not_le] at hcon - have hR : P.resources (a, z) ≤ P.resources (a', z) := P.resources_mono hle - have hys : P.policyOf v (a, z) ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v _ - have hys' : P.policyOf v (a, z) ∈ P.toExtended.feasible (a', z) := P.feasible_mono hle hys - have hy's' : P.policyOf v (a', z) ∈ P.toExtended.feasible (a', z) := P.policyOf_mem v _ - have hy's : P.policyOf v (a', z) ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] at hys ⊢ - rw [P.feasible_eq] at hy's' - exact ⟨hy's'.1, le_trans hcon.le hys.2⟩ - have hcsy : P.consumption (a, z) (P.policyOf v (a, z)) ∈ P.dom := - P.consumption_policyOf_mem_dom v ha - have hcs'y' : P.consumption (a', z) (P.policyOf v (a', z)) ∈ P.dom := - P.consumption_policyOf_mem_dom v ha' - have hcsy' : P.consumption (a, z) (P.policyOf v (a', z)) ∈ P.dom := by - refine P.dom_upward hcsy ?_ - simp only [consumption]; linarith - have hcs'y : P.consumption (a', z) (P.policyOf v (a, z)) ∈ P.dom := by - refine P.dom_upward hcsy ?_ - simp only [consumption]; linarith - -- increasing differences, from concavity of `u` alone - have hshift := P.strictConcaveOn_u_dom.concaveOn.sub_le_sub_of_shift - (c₁ := P.resources (a, z) - P.policyOf v (a, z)) - (c₂ := P.resources (a, z) - P.policyOf v (a', z)) - (Δ := P.resources (a', z) - P.resources (a, z)) - (by simpa only [consumption] using hcsy) - (by - have he : P.resources (a, z) - P.policyOf v (a', z) - + (P.resources (a', z) - P.resources (a, z)) - = P.consumption (a', z) (P.policyOf v (a', z)) := by simp only [consumption]; ring - rw [he]; exact hcs'y') - (by linarith) (by linarith) - have hopt_s := P.objROf_le_of_mem v ha hy's hcsy' - have hge : P.objROf v (a', z) (P.policyOf v (a', z)) - ≤ P.objROf v (a', z) (P.policyOf v (a, z)) := by - simp only [objROf, consumption] at hopt_s ⊢ - have e₁ : P.resources (a, z) - P.policyOf v (a', z) - + (P.resources (a', z) - P.resources (a, z)) - = P.resources (a', z) - P.policyOf v (a', z) := by ring - have e₂ : P.resources (a, z) - P.policyOf v (a, z) - + (P.resources (a', z) - P.resources (a, z)) - = P.resources (a', z) - P.policyOf v (a, z) := by ring - rw [e₁, e₂] at hshift - linarith - have hopt' : P.objROf v (a', z) (P.policyOf v (a, z)) - = P.objROf v (a', z) (P.policyOf v (a', z)) := - le_antisymm (P.objROf_le_of_mem v ha' hys' hcs'y) hge - have hbell : P.toExtended.objectiveE v (a', z) (P.policyOf v (a, z)) - = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe_of v ha' hys' hcs'y, hopt', - ← P.objectiveE_eq_coe_of v ha' hy's' hcs'y'] - exact P.policyOf_optimal v _ - exact absurd (P.optimal_action_unique_of_concaveSlices hv (s := (a', z)) ha' hys' hy's' - hbell (P.policyOf_optimal v _)) (by linarith) - /-! ### Monotonicity of the consumption function -/ /-- **Consumption rises with assets, whatever the continuation.** The exchange argument of diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean index babc9165f..3c6b5f32e 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -32,7 +32,7 @@ The feasible set grows with assets, which is what makes both exchanges legal. @[expose] public section -open Set Filter Topology +open Set Filter Topology BoundedContinuousFunction namespace LeanEconomics @@ -91,67 +91,110 @@ theorem objR_le_of_mem {s : ℝ × Z} {x : ℝ} (hs : s.1 ∈ Icc assetFloor ass /-! ### Monotonicity -/ -/-- **The optimal policy is increasing in assets.** Holding the income state fixed, a -household with more assets saves at least as much. -/ -theorem policy_mono {a a' : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) - (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : - P.policy (a, z) ≤ P.policy (a', z) := by +/-- The continuation value of saving `x`, read against `v`. -/ +noncomputable def contOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : ℝ := + ∑ z' : Z, P.transitionMatrix z z' * v (x, z') + +/-- The objective, as a real number, read against `v`. -/ +noncomputable def objROf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) (x : ℝ) : ℝ := + P.u (P.consumption s x) + P.discount * P.contOf v s.2 x + +/-- The objective in the reals agrees with the extended objective where consumption is +positive. -/ +theorem objectiveE_eq_coe_of (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) + (hcx : P.consumption (a, z) x ∈ P.dom) : + P.toExtended.objectiveE v (a, z) x = ((P.objROf v (a, z) x : ℝ) : EReal) := by + rw [ExtendedStochasticProgram.objectiveE, P.reward_eq_coe_dom (s := (a, z)) ha hx hcx, + ← EReal.coe_add] + rfl + +/-- **Any feasible action with positive consumption is worth at most the optimum**, whatever +the continuation. -/ +theorem objROf_le_of_mem (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} + (ha : a ∈ Icc assetFloor assetCap) (hx : x ∈ P.toExtended.feasible (a, z)) + (hcx : P.consumption (a, z) x ∈ P.dom) : + P.objROf v (a, z) x ≤ P.objROf v (a, z) (P.policyOf v (a, z)) := by + have hc : P.consumption (a, z) (P.policyOf v (a, z)) ∈ P.dom := + (P.bellmanFn_eq_of_optimal ha (P.policyOf_mem v (a, z)) + (P.policyOf_optimal v (a, z))).1 + have hle := P.toExtended.le_bellmanFn v hx + rw [P.objectiveE_eq_coe_of v ha hx hcx, EReal.coe_le_coe_iff] at hle + have heq := P.policyOf_optimal v (a, z) + rw [P.objectiveE_eq_coe_of v ha (P.policyOf_mem v (a, z)) hc, + EReal.coe_eq_coe_iff] at heq + exact hle.trans_eq heq.symm + +/-! ### Monotonicity of the saving policy -/ + +/-- **The optimal policy is increasing in assets, whatever the continuation.** The exchange +argument uses concavity and uniqueness against `v`. -/ +theorem policyOf_mono {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.policyOf v (a, z) ≤ P.policyOf v (a', z) := by by_contra hcon rw [not_le] at hcon have hR : P.resources (a, z) ≤ P.resources (a', z) := P.resources_mono hle - -- both exchanges are feasible - have hys : P.policy (a, z) ∈ P.toExtended.feasible (a, z) := P.policy_mem _ - have hys' : P.policy (a, z) ∈ P.toExtended.feasible (a', z) := P.feasible_mono hle hys - have hy's' : P.policy (a', z) ∈ P.toExtended.feasible (a', z) := P.policy_mem _ - have hy's : P.policy (a', z) ∈ P.toExtended.feasible (a, z) := by + have hys : P.policyOf v (a, z) ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v _ + have hys' : P.policyOf v (a, z) ∈ P.toExtended.feasible (a', z) := P.feasible_mono hle hys + have hy's' : P.policyOf v (a', z) ∈ P.toExtended.feasible (a', z) := P.policyOf_mem v _ + have hy's : P.policyOf v (a', z) ∈ P.toExtended.feasible (a, z) := by rw [P.feasible_eq] at hys ⊢ rw [P.feasible_eq] at hy's' exact ⟨hy's'.1, le_trans hcon.le hys.2⟩ - -- all four consumptions are positive - have hcsy : P.consumption (a, z) (P.policy (a, z)) ∈ P.dom := P.consumption_policy_mem_dom ha - have hcs'y' : P.consumption (a', z) (P.policy (a', z)) ∈ P.dom := - P.consumption_policy_mem_dom ha' - have hcsy' : P.consumption (a, z) (P.policy (a', z)) ∈ P.dom := by + have hcsy : P.consumption (a, z) (P.policyOf v (a, z)) ∈ P.dom := + (P.bellmanFn_eq_of_optimal ha (P.policyOf_mem v _) (P.policyOf_optimal v _)).1 + have hcs'y' : P.consumption (a', z) (P.policyOf v (a', z)) ∈ P.dom := + (P.bellmanFn_eq_of_optimal ha' (P.policyOf_mem v _) (P.policyOf_optimal v _)).1 + have hcsy' : P.consumption (a, z) (P.policyOf v (a', z)) ∈ P.dom := by refine P.dom_upward hcsy ?_ simp only [consumption]; linarith - have hcs'y : P.consumption (a', z) (P.policy (a, z)) ∈ P.dom := by + have hcs'y : P.consumption (a', z) (P.policyOf v (a, z)) ∈ P.dom := by refine P.dom_upward hcsy ?_ simp only [consumption]; linarith - -- increasing differences, from concavity of u alone + -- increasing differences, from concavity of `u` alone have hshift := P.strictConcaveOn_u_dom.concaveOn.sub_le_sub_of_shift - (c₁ := P.resources (a, z) - P.policy (a, z)) - (c₂ := P.resources (a, z) - P.policy (a', z)) + (c₁ := P.resources (a, z) - P.policyOf v (a, z)) + (c₂ := P.resources (a, z) - P.policyOf v (a', z)) (Δ := P.resources (a', z) - P.resources (a, z)) (by simpa only [consumption] using hcsy) (by - have : P.resources (a, z) - P.policy (a', z) + have he : P.resources (a, z) - P.policyOf v (a', z) + (P.resources (a', z) - P.resources (a, z)) - = P.consumption (a', z) (P.policy (a', z)) := by simp only [consumption]; ring - rw [this]; exact hcs'y') + = P.consumption (a', z) (P.policyOf v (a', z)) := by simp only [consumption]; ring + rw [he]; exact hcs'y') (by linarith) (by linarith) - -- the poorer household's choice beats its rival at its own state - have hopt_s := P.objR_le_of_mem ha hy's hcsy' - -- so it also beats it at the richer state - have hge : P.objR (a', z) (P.policy (a', z)) ≤ P.objR (a', z) (P.policy (a, z)) := by - simp only [objR, consumption] at hopt_s ⊢ - have e₁ : P.resources (a, z) - P.policy (a', z) + have hopt_s := P.objROf_le_of_mem v ha hy's hcsy' + have hge : P.objROf v (a', z) (P.policyOf v (a', z)) + ≤ P.objROf v (a', z) (P.policyOf v (a, z)) := by + simp only [objROf, consumption] at hopt_s ⊢ + have e₁ : P.resources (a, z) - P.policyOf v (a', z) + (P.resources (a', z) - P.resources (a, z)) - = P.resources (a', z) - P.policy (a', z) := by ring - have e₂ : P.resources (a, z) - P.policy (a, z) + = P.resources (a', z) - P.policyOf v (a', z) := by ring + have e₂ : P.resources (a, z) - P.policyOf v (a, z) + (P.resources (a', z) - P.resources (a, z)) - = P.resources (a', z) - P.policy (a, z) := by ring + = P.resources (a', z) - P.policyOf v (a, z) := by ring rw [e₁, e₂] at hshift linarith - -- hence the poorer household's choice is optimal at the richer state too - have hopt' : P.objR (a', z) (P.policy (a, z)) = P.objR (a', z) (P.policy (a', z)) := - le_antisymm (P.objR_le_of_mem ha' hys' hcs'y) hge - have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z) (P.policy (a, z)) - = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe ha' hys' hcs'y, hopt', - ← P.objectiveE_eq_coe ha' hy's' hcs'y'] - exact P.policy_optimal _ - -- uniqueness collapses the two, contradicting the strict inequality - exact absurd (P.eq_policy_of_optimal ha' hys' hbell) (by linarith) + have hopt' : P.objROf v (a', z) (P.policyOf v (a, z)) + = P.objROf v (a', z) (P.policyOf v (a', z)) := + le_antisymm (P.objROf_le_of_mem v ha' hys' hcs'y) hge + have hbell : P.toExtended.objectiveE v (a', z) (P.policyOf v (a, z)) + = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of v ha' hys' hcs'y, hopt', + ← P.objectiveE_eq_coe_of v ha' hy's' hcs'y'] + exact P.policyOf_optimal v _ + exact absurd (P.optimal_action_unique_of_concave hv (s := (a', z)) ha' hys' hy's' + hbell (P.policyOf_optimal v _)) (by linarith) + +/-- **The optimal policy is increasing in assets.** Holding the income state fixed, a +household with more assets saves at least as much. -/ +theorem policy_mono {a a' : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) + (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.policy (a, z) ≤ P.policy (a', z) := by + simpa only [P.policyOf_valueFunction] using + P.policyOf_mono P.concaveOn_valueFunction ha ha' hle end IncomeFluctuation From 8abcd2d4bd109e5b2a0fa041540dea0afe9d42f2 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 10:51:45 +0000 Subject: [PATCH 05/19] Correct residual utility and consumption-bound documentation --- LeanPool/AiyagariEquilibrium/Models/CRRA.lean | 11 +++--- .../Models/CRRAConstants.lean | 23 ++++++------ .../Models/ConsumptionFloor.lean | 37 +++++++++---------- .../Models/IncomeFluctuation.lean | 3 +- .../Models/MinimalMPC.lean | 5 ++- .../Models/NaturalAssetBound.lean | 12 +++--- 6 files changed, 46 insertions(+), 45 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean index f1e653d41..9d8c5c09d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRA.lean @@ -41,8 +41,9 @@ open Set Filter Topology namespace LeanEconomics -/-- **CRRA period utility.** `c ^ (1 - γ) / (1 - γ)`, with the removable case `γ = 1` filled in -by `log`, which is its limit. -/ +/-- **CRRA period utility.** Power utility for `γ ≠ 1`, with the usual logarithmic convention +at `γ = 1`. For positive `c`, subtracting `1 / (1 - γ)` from the power branch gives the +normalization whose limit as `γ → 1` is `log c`; the raw branch does not have a finite limit. -/ noncomputable def crraUtility (γ c : ℝ) : ℝ := if γ = 1 then Real.log c else c ^ (1 - γ) / (1 - γ) @@ -156,9 +157,9 @@ theorem relativeRiskAversionLeOne_crraUtility_iff {γ : ℝ} : rw [he] exact not_relativeRiskAversionLeOne_crra h -/-- **The two requirements pinch CRRA to log utility.** The income fluctuation structure needs -utility to fall to `-∞` at zero consumption, which forces `γ ≥ 1`; Light's uniqueness argument -needs relative risk aversion at most one, which forces `γ ≤ 1`. -/ +/-- **The unbounded-domain requirements pinch CRRA to log utility.** Divergence to `-∞` at zero +forces `γ ≥ 1`, while relative risk aversion at most one forces `γ ≤ 1`. This conjunction +does not restrict the separate bounded-domain treatment of `0 < γ < 1`. -/ theorem crra_pinch {γ : ℝ} : (Tendsto (crraUtility γ) (𝓝[>] 0) atBot ∧ RelativeRiskAversionLeOne (crraUtility γ)) ↔ γ = 1 := by diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 61d8cf8e8..4af2628d5 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -17,10 +17,9 @@ gives the value function its Lipschitz constant), a bound on saving (which gives condition), and the calibration arithmetic that checks they fit together. All four were computed for `Real.log`, and `hu : P.u = Real.log` propagated through the whole chain. -This file does them for CES. The pleasant surprise is that most of it is not a duplication: -`hasDerivAt_crraUtility` already covers every `γ > 0`, log included, so the CES lemmas here -GENERALISE their log counterparts rather than sitting beside them. `crra_marginal_bound` at -`γ = 1` is `log_marginal_bound`, and `crraSlopeBound` at `γ = 1` is `2 log 2 / m`. +The common derivative formula `hasDerivAt_crraUtility` covers every `γ > 0`, including log, +so the marginal bounds also cover that case. The closed form `crraSlopeBound` is used only +when `γ ≠ 1`; the logarithmic slope bound is evaluated separately as `2 log 2 / m`. ## The one place the shape changes @@ -180,8 +179,9 @@ theorem crra_cost_of_saving {γ : ℝ} (hγ : 0 < γ) {m h R b : ℝ} (hh0 : 0 < /-! ### Secant slopes of CRRA, bounded above -/ -/-- The closed form of `slopeBound (crraUtility γ)` at the income floor. At `γ = 1` this is -`2 log 2 / m`, which is `log_slopeBoundU`. -/ +/-- The closed form of `slopeBound (crraUtility γ)` at the income floor for `γ ≠ 1`. +The literal expression below evaluates to zero at `γ = 1` under total real division, so +the logarithmic case uses `log_slopeBoundU` separately. -/ noncomputable def crraSlopeBound (γ m : ℝ) : ℝ := 2 * m ^ (-γ) * (1 - ((2 : ℝ) ^ (1 - γ))⁻¹) / (1 - γ) @@ -279,11 +279,12 @@ theorem policy_le_of_marginal_bound {m : ℝ} (hm : 0 < m) /-! ### The oscillation gap -`deviationGap = 2 β ‖V‖` is what the log development compares against, and it is not -translation-invariant. That is fatal for CES: `c ^ (1-γ) / (1-γ)` differs from `log` by the -constant `1 / (1-γ)`, so `‖V‖` blows up as `γ → 1` while the model it describes does not. Every -use of it is against a DIFFERENCE of continuation values, so what is really needed is the -oscillation, and that is `(u maxConsumption - u minIncome) / (1 - β)`. -/ +`deviationGap = 2 β ‖V‖` is not translation-invariant. For CES, subtracting +`1 / (1-γ)` from `c ^ (1-γ) / (1-γ)` gives the normalization that tends to `log c` +as `γ → 1` for positive `c`; it is not an equality with `log c` at a fixed `γ ≠ 1`. +A value-norm bound is sensitive to that additive utility constant. The comparisons here use +DIFFERENCES of continuation values, so an oscillation bound avoids this normalization dependence: +`(u maxConsumption - u minConsumption) / (1 - β)`. -/ /-- The spread of the value function, from the two constant plans. -/ noncomputable def oscGap : ℝ := diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean index c994b3e40..ff95915c4 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean @@ -30,29 +30,26 @@ deviation argument compares the optimum with saving `h` less: `u (c + h) - u c ≤ β * (cont a - cont (a - h))`, and the right-hand side has to be bounded by a multiple of `h`. The continuation is concave and -bounded, so its slope on `[0, x]` is at most `2‖V‖ / x` — which blows up as `x → 0`. Minimum -income kills that case: if consumption is small then saving is nearly all of resources, so -`a ≥ 3 minIncome / 4` and the slope constant is `4‖V‖ / minIncome`, depending on the state -through nothing at all. That is `contSlope_le`. +bounded, so its slope from `assetFloor` to `x` is at most `2‖V‖ / (x - assetFloor)`. The +positive sustainability bound `minConsumption` keeps the deviation away from that endpoint: +small consumption forces saving at least `assetFloor + 3 * minConsumption / 4`. The resulting +state-independent slope constant is `4‖V‖ / minConsumption`, as used by `cont_sub_le`. With a state-independent constant `L`, any consumption level whose marginal value beats `β L` cannot be optimal, and `MarginalInada` supplies one. The floor is explicit: -`min δ (minIncome / 4)`. - -## What this does NOT do, and why - -The plan was to swap `tendsto_atBot_u` out of the structure. It cannot simply be swapped, and -the reason is worth recording. That condition does a SECOND job: `continuous_rewardFn` uses it -to make `extendBot u` continuous into `EReal`, and continuity of the reward is what Berge's -maximum theorem runs on. With `u (0⁺)` finite, `extendBot u` jumps from a finite limit down to -`⊥` at zero consumption — it is not even upper semicontinuous there, so the maximum is not -attained by the general argument and the whole `ExtendedStochasticProgram` apparatus lapses. - -So admitting `γ < 1` needs the reward to stop being `extendBot u` and start being `u` itself, -real-valued and continuous on `[0, ∞)` — a different reward, hence a different structure, not a -different axiom. The floor proved here is what that structure will need and is stated so that it -transfers: it uses concavity and boundedness of the continuation and nothing else, and in -particular it never mentions `tendsto_atBot_u`. +`min δ (minConsumption / 4)`. + +## How the current bounded-domain model uses this result + +Divergence at zero has two distinct roles in the unbounded-domain model: it makes +`extendBot u` continuous into `EReal` and excludes zero consumption at an optimum. The marginal +argument here replaces the latter role. + +The current `IncomeFluctuation` structure has a utility-domain field. +`BoundedIncomeFluctuation` takes `dom = Ici 0`, where `extendDom` uses the finite value `u 0` +rather than `⊥`, and proves the needed reward continuity. Its marginal Inada argument then +establishes positive consumption separately. The floor results here fit that shared structure; +they do not require a new household type or a divergence assumption. -/ @[expose] public section diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean index f4c93432c..3465411ee 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuation.lean @@ -100,7 +100,8 @@ theorem continuousOn_extendDom_Ioi {u : ℝ → ℝ} (hc : ContinuousOn u (Ioi 0 rw [extendDom_Ioi] exact (continuous_extendBot hc ht).continuousOn -/-- The income fluctuation problem, with period utility unbounded below. -/ +/-- The income fluctuation problem, with a utility domain supporting bounded and unbounded +below period utility. -/ structure IncomeFluctuation (Z : Type*) [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] (assetFloor assetCap : ℝ) where /-- Income in each state. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index 55ca9dcd4..90cf94a02 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -116,8 +116,9 @@ theorem minMPC_one : P.minMPC 1 = 1 - (P.discount : ℝ) := by simp only [minMPC, div_one, Real.rpow_one] rw [mul_div_assoc, div_self hR.ne', mul_one] -/-- **The minimal MPC is positive** exactly when the household is impatient and the rate is -non-negative — Carroll's return impatience, which `βR < 1` implies once `R ≥ 1`. -/ +/-- **Sufficient conditions for a positive minimal MPC:** impatience and a nonnegative rate. +Here `βR < 1` and `R ≥ 1` imply the required return-impatience bound; these conditions are +not necessary, as `minMPC_one` also applies at negative admissible rates. -/ theorem minMPC_pos {γ : ℝ} (hγ0 : 0 < γ) (hint : 0 ≤ P.interest) (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) : 0 < P.minMPC γ := by have hR : (1 : ℝ) ≤ 1 + P.interest := by linarith diff --git a/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean index 97929dbdf..837779844 100644 --- a/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean +++ b/LeanPool/AiyagariEquilibrium/Models/NaturalAssetBound.lean @@ -29,13 +29,13 @@ reads `log (resources / consumption) ≤ 2 β ‖V‖`, which is a linear bound `consumption ≥ exp (-2 β ‖V‖) · resources`. -## Why this works for log and for nothing else +## Log and the other CRRA regimes -The deviation is informative only because log is UNBOUNDED ABOVE, so `u resources` grows without -limit and drags `u consumption` up with it. With CRRA `γ > 1` utility is bounded above, the same -comparison gives only `c^(1-γ)` bounded, hence a constant floor and no linear bound at all — the -crude deviation says nothing about rich households. That is another face of the same split -`crra_pinch` found, and it is the case the structure leaves us with anyway. +For log utility, `u resources` grows without limit and the deviation gives the proportional +consumption bound above. In the unbounded-domain CRRA branch with `γ > 1`, utility is bounded +above, so this comparison gives a constant consumption floor rather than that linear bound. +The separate bounded-domain treatment also permits `0 < γ < 1`; `crra_pinch` does not exclude +that regime from the shared household structure. ## What is honest about the constant From 25772bbbca5faf8909eaf6cd1ffdefd38ffe55b8 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 11:18:29 +0000 Subject: [PATCH 06/19] State reachability and sufficient corner assumptions explicitly --- LeanPool/AiyagariEquilibrium/Analysis/HARA.lean | 4 ++-- .../Equilibrium/AiyagariUniqueness.lean | 8 +++++--- .../AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean | 2 +- LeanPool/projects/aiyagari-equilibrium.yaml | 5 +++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean index 7ac768517..d6766688e 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -93,8 +93,8 @@ theorem hasDerivAt_haraUtility (γ : ℝ) {a c : ℝ} (hac : 0 < a + c) : /-! ### Light's condition -/ -/-- The factorisation that does the work: `c · u' c` splits into two nonnegative nondecreasing -pieces exactly when the shift is nonnegative and `γ ≤ 1`. -/ +/-- The factorisation used for Light's condition: when the shift is nonnegative and +`0 < γ ≤ 1`, both factors are nonnegative and nondecreasing on positive consumption. -/ theorem mul_rpow_neg_eq {γ a c : ℝ} (hc : 0 < c) (hac : 0 < a + c) : c * (a + c) ^ (-γ) = c ^ (1 - γ) * (c / (a + c)) ^ γ := by rw [Real.div_rpow hc.le hac.le, Real.rpow_neg hac.le] diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index a3995148f..070377813 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -416,7 +416,8 @@ theorem log_no_equilibrium_of_ceiling {α δ r : ℝ} (hu : P.u = crraUtility 1) /-! ### Aiyagari (1994), `μ = 1` `β = 0.96`, `α = 0.36`, `δ = 0.08`, log utility, no borrowing, and a labour endowment process -with monotone transitions about which nothing else is assumed. The time-preference rate is +with monotone transitions and a lowest-income state reached with positive probability from +every state in one step. The time-preference rate is `λ = 1/0.96 - 1 = 1/24`, and the theorem covers every interval `[rlo, rhi]` with `-δ < rlo ≤ rhi < 1/24` — so the whole of `(-δ, λ)`, the range on which Cobb--Douglas demand is finite and the household impatient; his reported equilibrium rates at `μ = 1` lie between `3.3%` @@ -424,8 +425,9 @@ and `4.17%`. -/ /-- **Aiyagari (1994) at `μ = 1`: the equilibrium interest rate is unique** on `[rlo, rhi]` for every `-δ < rlo ≤ rhi` below the time-preference rate `1/24`, for any economy with his -preferences and technology and any earnings process with monotone transitions, provided the -asset cap clears `(48/25) · maxIncome / (1 - (24/25)(1+rhi))`. -/ +preferences and technology, monotone earnings transitions and positive one-step access to a +lowest-income state, provided the asset cap clears +`(48/25) · maxIncome / (1 - (24/25)(1+rhi))`. -/ theorem aiyagari1994_log_equilibriumRate_unique {rlo rhi : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) (hrlo : -2 / 25 < rlo) (hlohi : rlo ≤ rhi) (hlam : rhi < 1 / 24) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index f0076ba6c..05c44f4d1 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -713,7 +713,7 @@ CRRA, so the test becomes one inequality among the primitives. consumption: `(η + minConsumption / 2) ^ (-γ)`. * The marginal value at `(a, z₀)` for `a ≤ a₀` is at least `(η + income z₀ + R a₀) ^ (-γ)`. -So the corner holds below `a₀` exactly when +A sufficient condition for the corner to hold below `a₀` is `β · (η + minConsumption/2) ^ (-γ) · R / (1 - β R) < (η + income z₀ + R a₀) ^ (-γ)`, diff --git a/LeanPool/projects/aiyagari-equilibrium.yaml b/LeanPool/projects/aiyagari-equilibrium.yaml index f64e0180c..49b967ecc 100644 --- a/LeanPool/projects/aiyagari-equilibrium.yaml +++ b/LeanPool/projects/aiyagari-equilibrium.yaml @@ -3,8 +3,9 @@ title: Capped log Aiyagari equilibrium and dynamic programming summary: >- Proves uniqueness of the equilibrium interest rate for capped log-utility households with Aiyagari's discount and firm parameters, no borrowing, - and monotone finite-state earnings transitions, subject to an explicit - cap-slack inequality at the larger rate. Instantiates the earnings + and monotone finite-state earnings transitions with positive one-step + access to a lowest-income state, subject to an explicit cap-slack + inequality at the larger rate. Instantiates the earnings process with arbitrary finite Tauchen chains. Bisection brackets a given equilibrium and converges along a stationary-distribution selection; existence of that equilibrium is a hypothesis of the bisection result. From f2396600b8ad30dadfab66fbc38a214eea323971 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 12:50:54 +0000 Subject: [PATCH 07/19] Clarify normalized clearing and share household proof arguments --- LeanPool/AiyagariEquilibrium.lean | 2 +- .../Distribution/Uniqueness.lean | 13 +- .../Equilibrium/AiyagariUniqueness.lean | 48 ++-- .../Equilibrium/Bisection.lean | 23 +- .../AiyagariEquilibrium/Equilibrium/Firm.lean | 20 +- .../Models/AiyagariTauchenWitness.lean | 24 +- .../Models/CRRAConstants.lean | 61 +---- .../Models/ConsumptionFloor.lean | 186 +++++++++----- .../Models/ImpatientDecline.lean | 77 ++---- .../IncomeFluctuationCarrollKimball.lean | 49 ++-- .../Models/IncomeFluctuationConsumption.lean | 147 ++++++----- .../Models/IncomeFluctuationHARA.lean | 3 +- .../Models/IncomeFluctuationIterate.lean | 241 ------------------ .../Models/IncomeFluctuationMonotone.lean | 24 ++ .../Models/IncomeFluctuationRateStep.lean | 1 - .../Models/LogAiyagari.lean | 2 +- .../Models/MinimalMPC.lean | 13 +- .../Models/MonotoneTransitions.lean | 69 +---- LeanPool/projects/aiyagari-equilibrium.yaml | 84 +++--- 19 files changed, 404 insertions(+), 683 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium.lean b/LeanPool/AiyagariEquilibrium.lean index b3e268f4b..e8aad7c5f 100644 --- a/LeanPool/AiyagariEquilibrium.lean +++ b/LeanPool/AiyagariEquilibrium.lean @@ -10,7 +10,7 @@ public import LeanPool.AiyagariEquilibrium.Models.AiyagariTauchenWitness public import LeanPool.AiyagariEquilibrium.Equilibrium.Bisection /-! -# Capped log Aiyagari equilibrium and dynamic programming +# Normalized capped log-household clearing and dynamic programming Source: url:https://github.com/LeanEconomics/LeanEconomics Authors: Robert Kirkby diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean index 000b64ae5..b62867452 100644 --- a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean @@ -480,16 +480,19 @@ theorem tendsto_pushProb_iterate {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : to check directly, and in the earlier calibrations it was obtained only by making the policy identically zero. -Açıkgöz (2018) obtains it instead from two natural facts, in the proof of his Proposition 5: +In Açıkgöz's April 2016 author draft, Proposition 5 (printed page 9; proof in Appendix E, +printed page 32) uses two natural facts: the constraint binds near zero, and assets strictly decline above that at the worst income state. A continuous strictly declining map on a compact interval has a UNIFORM minimum decline, so finitely many periods carry the richest household below the threshold, and one more step pins it to the constraint. -His Proposition 4, which produces the asset bound itself, is a different matter: its proof -runs the Euler equation at an interior optimum and takes limits of `u'(x+ε)/u'(x)`, so it -needs differentiability of the utility and the first-order condition. This development -deliberately has neither. The lemma below extracts only the part that does not. -/ +In that edition, Proposition 3 (printed page 8) supplies worst-state decline, while +Proposition 4 (the same page) supplies a finite asset bound. The source is +https://mpra.ub.uni-muenchen.de/71066/1/MPRA_paper_71066.pdf. +The lemma below uses only continuity, binding near the constraint and strict decline on an +already compact interval; the Euler arguments establishing these inputs appear separately. + -/ omit [MeasurableSpace Z] [BorelSpace Z] in theorem exists_exhaust_of_decline {z₀ : Z} {a₀ : ℝ} (ha₀ : assetFloor < a₀) (hle : a₀ ≤ assetCap) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index 070377813..f840ef551 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -12,39 +12,27 @@ public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariContinuity public import LeanPool.AiyagariEquilibrium.Equilibrium.SignChange /-! -# Uniqueness of the equilibrium rate for a log economy at large `β` +# Rate uniqueness for normalized capped-log clearing -The chain of Light (2018) — individual saving rises with the rate (Theorem 1), so aggregate -saving does (Theorem 2), so it meets a strictly decreasing demand once (Theorem 3) — assembled -for LOG utility with the constants that survive `β = 0.96`. +This file combines rate monotonicity of household saving, monotonicity of stationary mean +assets and a strictly decreasing prescribed demand schedule. The log-household constants +support discount `24/25`. -## The normalised firm +## The prescribed normalized schedule -Aiyagari's firm is Cobb--Douglas with capital share `α`: `k(r) = (α/(r+δ))^(1/(1-α))` and -`w(r) = (1-α) k(r)^α`. Light's Theorem 3 works with the ratio, and the ratio needs no root: +`normalisedDemand α δ r = α / ((1 - α) * (r + δ))` is the capital-to-wage ratio for a +Cobb–Douglas firm per unit of effective labor. The clearing equation used here equates this +schedule to mean household assets. No mean-one endowment condition or wage/asset-cap scaling +bridge to full competitive equilibrium is supplied. The result is uniqueness for this +explicit unit-wage normalized clearing problem. - `k(r) / w(r) = α / ((1-α)(r+δ))`, +## Household conditions -strictly decreasing in `r`. Capital supply is homogeneous of degree one in the wage (Açıkgöz -Proposition 7, `Equilibrium/Homothetic`), so Aiyagari's steady-state condition `K(r) = Ea(r)` at -wage `w(r)` is the condition `k(r)/w(r) = Ea₁(r)` for the unit-wage economy. This file proves -that the latter has at most one solution. - -## What is assumed of the income process - -That its transitions are MONOTONE — a higher-income state's row first-order stochastically -dominates a lower one's (`MonotoneTransitions`; iid is the trivial case) — and that the worst -state has income `minIncome` and is reachable from everywhere. Nothing else: no transition -probabilities are named. That is why the theorem can be stated for Aiyagari's calibration -without reproducing his seven-state Tauchen chains: a Tauchen discretisation of an AR(1) with -positive persistence has monotone transitions by construction, so at `μ = 1` every column of his -Table II is covered, `ρ = 0` and `ρ > 0` alike. - -## What is assumed of the cap - -One inequality, `2β · maxIncome < (1 - β(1+rhi)) · cap`. The asset cap is bookkeeping imposed for -compactness; this says it was set large enough not to bind, and it is the only place the cap -enters. +Transitions are monotone in income, and a lowest-income state is reached with positive +probability in one step from every state. The Tauchen construction verifies these properties +for its arbitrary positive earnings scale. The cap inequality +`2β * maxIncome < (1 - β * (1 + rhi)) * cap` guarantees the required saving bound across +the rate interval; the asset bounds are fixed across that family. -/ @[expose] public section @@ -53,9 +41,9 @@ open Set Filter Topology MeasureTheory BoundedContinuousFunction namespace LeanEconomics -/-! ### The normalised Cobb--Douglas demand -/ +/-! ### The prescribed normalized demand -/ -/-- `k(r)/w(r)` for a Cobb--Douglas firm with capital share `α` and depreciation `δ`. -/ +/-- The prescribed unit-wage normalized demand schedule, per unit of effective labor. -/ noncomputable def normalisedDemand (α δ r : ℝ) : ℝ := α / ((1 - α) * (r + δ)) theorem normalisedDemand_strictAntiOn {α δ rlo rhi : ℝ} (hα0 : 0 < α) (hα1 : α < 1) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean index 1d982e36b..6448a0a7e 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -8,7 +8,7 @@ module public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness /-! -# Bisection finds the equilibrium rate, and the log Aiyagari instance +# Bisection for a monotone zero and normalized clearing Aiyagari (1994) computed his equilibrium by bisection on the interest rate: evaluate excess supply at the midpoint of a bracket and keep the half on which it still changes sign. Hajek and @@ -27,12 +27,21 @@ monotonicity alone decides which half the zero is in. The midpoint after `n` ste ## The instance -For Aiyagari's log calibration the supply schedule along any selection of stationary -distributions is monotone (`log_monotoneOn_capitalSupply`) and Cobb--Douglas demand is strictly -decreasing, so excess supply is strictly increasing. `aiyagari1994_log_bisection` therefore says: -whatever equilibrium rate there is on `[rlo, rhi] ⊂ (-δ, λ)`, the bisection on excess supply -brackets it at every step and converges to it — the numerical procedure is certified to find the -rate that `aiyagari1994_log_equilibriumRate_unique` says is the only one. +For the capped log-household calibration, stationary mean assets are monotone in the rate +under the stated hypotheses, and the prescribed normalized demand is strictly decreasing. +Thus normalized excess supply is strictly increasing. Given a stationary distribution and a +rate solving that explicit normalized clearing equation, `aiyagari1994_log_bisection` proves +that bisection brackets the rate and converges to it. Existence of such a solution is an input. + +## Normalized clearing scope + +The economic endpoints use the prescribed equation +`aggregateCapital μ = normalisedDemand (9/25) (2/25) r`, where the right side is +`(9/25) / ((1 - 9/25) * (r + 2/25))`. This is a unit-wage normalized clearing problem. +The household earnings scale is arbitrary; no mean-one endowment condition or bridge from +these units and the fixed asset cap to full competitive market clearing is established here. +The uniqueness and bisection claims concern this explicit normalized equation. + -/ @[expose] public section diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean index 96760eda8..e1f4d33ad 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Firm.lean @@ -36,17 +36,13 @@ would change nothing conceptually. Equilibrium needs excess demand to change sign across `[rlo, rhi]`. The LOW end is now a theorem: capital demand diverges as `r` approaches `-δ`, while capital supply can never exceed -the asset cap, so `capitalDemand δ rlo ≥ assetCap ≥ S rlo` for `rlo` close enough to `-δ`. - -The HIGH end is not, and the obstruction is economic rather than technical. It needs -`capitalDemand δ rhi ≤ S rhi`, and since Cobb–Douglas demand is strictly positive that forces -`S rhi > 0` -- households must hold capital at high interest rates. That is obvious -economically and unproved here. Worse, it is in tension with the only calibration whose -Doeblin hypotheses are verified: the impatient household of `NonVacuity` is driven to the -borrowing constraint at EVERY asset level, so its stationary distribution sits at zero assets -and its capital supply is identically zero. A calibration with a genuine equilibrium needs the -constraint to bind at the bad income state but not the good one, which the uniform corner -condition of `policy_eq_zero_of_corner` cannot express. +the asset cap, so `capitalDemand 1 δ rlo ≥ assetCap ≥ S rlo` for `rlo` close enough to `-δ`. + +The high-end sign inequality requires positive household capital supply. This module +exposes that condition; the later `PositiveCapital` and `SignChange` modules provide lower +bounds and calibrate the firm under their additional hypotheses. The uniformly impatient +`NonVacuity` household alone has zero stationary capital supply and does not discharge it. + -/ @[expose] public section @@ -55,7 +51,7 @@ open Set Filter Topology MeasureTheory namespace LeanEconomics -/-- Capital demand from a Cobb–Douglas firm with `α = 1/2`, unit labour and unit productivity: +/-- Capital demand from a Cobb–Douglas firm with `α = 1/2`, unit labour and productivity `A`: the capital stock at which the net marginal product equals `r`. -/ noncomputable def capitalDemand (A δ r : ℝ) : ℝ := A ^ 2 / (4 * (r + δ) ^ 2) diff --git a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean index e369f496a..bfc39920f 100644 --- a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean +++ b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean @@ -9,19 +9,21 @@ public import LeanPool.AiyagariEquilibrium.Models.Tauchen public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness /-! -# Aiyagari (1994) with his own earnings process +# Finite Tauchen earnings for normalized capped-log clearing -Aiyagari's labour endowment follows `log l' = ρ log l + σ √(1 - ρ²) ε`, `ε ~ N(0,1)`, discretised -by Tauchen's method on `[-3σ, 3σ]`, and scaled so that its mean is one. He used seven states, -`ρ ∈ {0, 0.3, 0.6, 0.9}` and `σ ∈ {0.2, 0.4}`. The economy here has `n + 2` states for any `n`, -any `ρ ∈ [0, 1)`, any `σ > 0`, and any scale `c > 0` — the normalising constant is whatever it -is; uniqueness does not depend on it except through the cap condition, where it enters via the -highest endowment `c · e^{3σ}`. +The earnings chain discretizes `log l' = ρ log l + σ √(1 - ρ²) ε`, with standard normal `ε`, +on `n + 2` grid points in `[-3σ, 3σ]`. Its earnings scale `c > 0` is arbitrary: no mean-one +endowment normalization is imposed. Monotone transitions and positive one-step access to the +lowest state follow from `monotoneTransitions_of_tauchen` and `tauchen_pos_zero`. -The two facts about the chain that the uniqueness theorem needs — monotone transitions and a -lowest state reachable from everywhere — are `monotoneTransitions_of_tauchen` and -`tauchen_pos_zero`, both consequences of `Φ` being monotone and positive. Everything else is -`rfl` and `norm_num`, as for the two-state witnesses. +The household has discount `24/25`, log utility, no borrowing and a fixed finite asset cap. +The rate-uniqueness endpoint uses the prescribed unit-wage normalized clearing equation +`aggregateCapital μ = normalisedDemand (9/25) (2/25) r`, with demand `9 / (16 * (r + 2/25))`, +and an explicit cap-slack inequality at the larger rate. + +A bridge from these household units and the fixed cap to full competitive market clearing +for arbitrary earnings scales is not established here. The theorem concerns this explicit +normalized clearing equation. -/ @[expose] public section diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 4af2628d5..2701d946d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -228,54 +228,13 @@ argument extracts. For CES with `γ < 1` the uniform `m = maxConsumption ^ (-γ) and it is enough. -/ theorem policy_le_of_marginal_bound {m : ℝ} (hm : 0 < m) (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) - (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : + {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) ≤ 2 * P.deviationGap / m := by - have hG : 0 ≤ P.deviationGap := P.deviationGap_nonneg - set b : ℝ := P.policy (a, z) with hbdef - set c : ℝ := P.consumptionFn z a with hcdef - rcases eq_or_lt_of_le (P.policy_mem_region (a, z)).1 with hzero | hbpos - · rw [← hbdef] at hzero - rw [← hzero] - positivity - rw [← hbdef] at hbpos - have hcpos : 0 < c := P.consumptionFn_pos hpc ha z - have hres : P.resources (a, z) = c + b := by simp only [hcdef, consumptionFn, consumption]; ring - -- the deviation: save half as much - have hhalf0 : 0 < b / 2 := by linarith - have hhalfb : b / 2 < b := by linarith - have hbreg : b ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ - have hmem0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ - have hfeas : b / 2 ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨hhalf0.le, by linarith [(P.policy_mem (a, z)).2]⟩ - have hcons : P.consumption (a, z) (b / 2) = c + b / 2 := by - simp only [consumption]; rw [hres]; ring - have hdev : 0 < P.consumption (a, z) (b / 2) := by rw [hcons]; linarith - have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hdev) - simp only [objR, hcons] at hopt - rw [show P.consumption (a, z) (P.policy (a, z)) = c from rfl, - show P.policy (a, z) = b from rfl] at hopt - -- concavity of the continuation caps the forgone repayment - have hslope := (P.concaveOn_cont z).slope_anti_adjacent hmem0 hbreg hhalf0 hhalfb - have hb1 := abs_le.mp (P.abs_cont_le z (b / 2)) - have hb0 := abs_le.mp (P.abs_cont_le z 0) - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - have hloss : P.cont z b - P.cont z (b / 2) ≤ 2 * ‖P.toExtended.valueFunction‖ := by - rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope - nlinarith [hslope, hb1.1, hb0.2] - have hscaled : (P.discount : ℝ) * (P.cont z b - P.cont z (b / 2)) ≤ P.deviationGap := by - simp only [deviationGap] - nlinarith [hloss, hβ] - -- the marginal gain from the extra consumption - have hcmax : c + b / 2 ≤ P.maxConsumption := by - have := P.consumption_le_maxConsumption (s := (a, z)) ha (le_refl (0 : ℝ)) - simp only [consumption, sub_zero] at this - rw [hres] at this - linarith - have hmg := hmarg (c + b / 2) c (P.mem_dom_of_pos hcpos) (by linarith) hcmax - rw [show c + b / 2 - c = b / 2 from by ring] at hmg - rw [le_div_iff₀ hm] - nlinarith [hopt, hscaled, hmg] + have h := P.policyOf_le_of_marginal_bound P.concaveOn_valueFunction hm hmarg ha z + rw [P.policyOf_valueFunction, zero_add] at h + convert h using 1 + dsimp [deviationGap] + ring /-! ### The oscillation gap @@ -377,7 +336,7 @@ theorem crra_policy_eq_zero_of_resources {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < /-- **The CES saving bound in closed form.** Saving never exceeds `2 · deviationGap · maxConsumption ^ γ`, whatever the state. -/ theorem crra_policy_le {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounded) - (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) + (hu : P.u = crraUtility γ) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) ≤ 2 * P.deviationGap * P.maxConsumption ^ γ := by have hmc := P.maxConsumption_pos have hm : (0 : ℝ) < P.maxConsumption ^ (-γ) := Real.rpow_pos_of_pos hmc _ @@ -385,7 +344,7 @@ theorem crra_policy_le {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounde (fun c d hd hdc hc => by rw [hu] exact crra_marginal_bound_Ici hγ0 hγ1 (by rw [hb] at hd; exact hd) hdc hc) - hpc ha z + ha z have hinv : P.maxConsumption ^ (-γ) = (P.maxConsumption ^ γ)⁻¹ := Real.rpow_neg hmc.le γ rw [hinv, div_eq_mul_inv, inv_inv] at hkey exact hkey @@ -393,10 +352,10 @@ theorem crra_policy_le {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounde /-- **The natural asset bound for CES.** A uniform cap on saving is a decline condition outright: above it, assets fall. No linear consumption bound and no `exp` in sight. -/ theorem crra_exists_decline {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.Bounded) - (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z : Z) : + (hu : P.u = crraUtility γ) (z : Z) : ∃ ā : ℝ, ∀ a ∈ Icc (0 : ℝ) assetCap, ā < a → P.policy (a, z) < a := ⟨2 * P.deviationGap * P.maxConsumption ^ γ, fun _ ha hlt => - lt_of_le_of_lt (P.crra_policy_le hγ0 hγ1 hb hu hpc ha _) hlt⟩ + lt_of_le_of_lt (P.crra_policy_le hγ0 hγ1 hb hu ha _) hlt⟩ /-- **The continuation's gain between two saving levels, CES version.** Where the log form carries `log (1 + R·(y-x) / (income + R·x))`, this carries `(income + R·y) ^ (-γ) · R·(y-x)`: the extra diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean index ff95915c4..47f1185f7 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean @@ -54,7 +54,7 @@ they do not require a new household type or a divergence assumption. @[expose] public section -open Set Filter Topology +open Set Filter Topology BoundedContinuousFunction namespace LeanEconomics @@ -119,6 +119,40 @@ variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopo variable {assetFloor assetCap : ℝ} variable (P : IncomeFluctuation Z assetFloor assetCap) +/-- The state-independent bound on the continuation's slope, read against `v`. -/ +noncomputable def contSlopeConstOf (v : (ℝ × Z) →ᵇ ℝ) : ℝ := 4 * ‖v‖ / P.minConsumption + +theorem contSlopeConstOf_nonneg (v : (ℝ × Z) →ᵇ ℝ) : 0 ≤ P.contSlopeConstOf v := + div_nonneg (by positivity) P.minConsumption_pos.le + +/-- **The continuation's slope is bounded by a state-independent constant**, above +`assetFloor + minConsumption / 2`, whatever the continuation. -/ +theorem contOf_sub_le {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) (z : Z) + {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.contOf v z y - P.contOf v z x ≤ P.contSlopeConstOf v * (y - x) := by + have hmin := P.minConsumption_pos + have hx0 : assetFloor < x := by linarith + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy + have hb1 := abs_le.mp (P.abs_contOf_le v z x) + have hb2 := abs_le.mp (P.abs_contOf_le v z assetFloor) + have hhalf : P.contSlopeConstOf v * (P.minConsumption / 2) = 2 * ‖v‖ := by + rw [contSlopeConstOf]; field_simp; ring + have hbig : P.contOf v z x - P.contOf v z assetFloor + ≤ P.contSlopeConstOf v * (x - assetFloor) := by + have hstep : P.contSlopeConstOf v * (P.minConsumption / 2) + ≤ P.contSlopeConstOf v * (x - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) (P.contSlopeConstOf_nonneg v) + rw [hhalf] at hstep + linarith [hb1.2, hb2.1] + rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope + have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) + ≤ (P.contSlopeConstOf v * (x - assetFloor)) * (y - x) := by + nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] + nlinarith [hprod, hx0] + /-- The state-independent bound on the continuation's slope, which is where the positive income floor does its work. -/ noncomputable def contSlopeConst : ℝ := 4 * ‖P.toExtended.valueFunction‖ / P.minConsumption @@ -135,81 +169,117 @@ argument lives. -/ theorem cont_sub_le (z : Z) {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) (hy : y ∈ Icc assetFloor assetCap) : P.cont z y - P.cont z x ≤ P.contSlopeConst * (y - x) := by - have hmin := P.minConsumption_pos - have hx0 : assetFloor < x := by linarith - have hxmem : x ∈ Icc assetFloor assetCap := ⟨hx0.le, le_trans hxy.le hy.2⟩ - have hslope := (P.concaveOn_cont z).slope_anti_adjacent - (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy - have hb1 := abs_le.mp (P.abs_cont_le z x) - have hb2 := abs_le.mp (P.abs_cont_le z assetFloor) - have hhalf : P.contSlopeConst * (P.minConsumption / 2) - = 2 * ‖P.toExtended.valueFunction‖ := by - rw [contSlopeConst]; field_simp; ring - have hbig : P.cont z x - P.cont z assetFloor ≤ P.contSlopeConst * (x - assetFloor) := by - have hstep : P.contSlopeConst * (P.minConsumption / 2) - ≤ P.contSlopeConst * (x - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) P.contSlopeConst_nonneg - rw [hhalf] at hstep - linarith [hb1.2, hb2.1] - rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope - have hprod : (P.cont z y - P.cont z x) * (x - assetFloor) - ≤ (P.contSlopeConst * (x - assetFloor)) * (y - x) := by - nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] - nlinarith [hprod, hx0] + simpa only [cont, contOf, contSlopeConst, contSlopeConstOf] using + P.contOf_sub_le P.concaveOn_valueFunction z hx hxy hy -/-- **A consumption floor from the margin.** No use is made of `tendsto_atBot_u`: the argument -runs on concavity and boundedness of the continuation, a positive income floor, and unbounded -marginal value at zero consumption. -/ -theorem exists_consumption_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : - ∃ δ > 0, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → δ ≤ P.consumption s (P.policy s) := by +/-- **A consumption floor from the margin, against an arbitrary continuation.** -/ +theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) : + ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by have hmin := P.minConsumption_pos - have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg - obtain ⟨δ₀, hδ₀, hδ⟩ := hu (P.discount * P.contSlopeConst + 1) - refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun s hs => ?_⟩ + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + obtain ⟨δ₀, hδ₀, hδ⟩ := hu ((P.discount : ℝ) * P.contSlopeConstOf v + 1) + refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ by_contra hlt rw [not_le] at hlt set d : ℝ := min δ₀ (P.minConsumption / 4) with hd - set a : ℝ := P.policy s with hadef - set c : ℝ := P.consumption s a with hcdef + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumption (a, z) b with hcdef + have hltc : c < d := hlt have hdδ : d ≤ δ₀ := min_le_left _ _ have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ - have hcmem : c ∈ P.dom := P.consumption_policy_mem_dom hs + have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem - have hres : P.minConsumption ≤ P.resources s - assetFloor := - P.minConsumption_le_consumption_floor s - have hca : c = P.resources s - a := rfl - -- consumption small forces saving large, which is what bounds the continuation's slope - have ha : assetFloor + 3 * P.minConsumption / 4 ≤ a := by rw [hca] at hlt; linarith + have hres : P.minConsumption ≤ P.resources (a, z) - assetFloor := + P.minConsumption_le_consumption_floor (a, z) + have hcb : c = P.resources (a, z) - b := rfl + have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith set h : ℝ := d - c with hhdef have hh0 : 0 < h := by rw [hhdef]; linarith have hhle : h ≤ P.minConsumption / 4 := by rw [hhdef]; linarith - have hlow : assetFloor + P.minConsumption / 2 ≤ a - h := by linarith - have hamem : a ∈ Icc assetFloor assetCap := P.policy_mem_region s - -- the deviation is feasible and its consumption is `d` - have hfeas : a - h ∈ P.toExtended.feasible s := by + have hlow : assetFloor + P.minConsumption / 2 ≤ b - h := by linarith + have hbmem : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hfeas : b - h ∈ P.toExtended.feasible (a, z) := by rw [P.feasible_eq] - exact ⟨by linarith [hamem.1], by linarith [(P.policy_mem s).2]⟩ - have hcd : P.consumption s (a - h) = d := by - simp only [consumption] at hca ⊢; linarith - have hdpos : 0 < P.consumption s (a - h) := by rw [hcd]; linarith - -- optimality of `a` against the deviation - have hopt := P.objR_le_of_mem hs hfeas (P.mem_dom_of_pos hdpos) - simp only [objR, hcd] at hopt - -- the continuation cannot repay more than `L * h` - have hslope := P.cont_sub_le s.2 hlow (by linarith) hamem - rw [show a - (a - h) = h from by ring] at hslope + exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcd : P.consumption (a, z) (b - h) = d := by + simp only [consumption] at hcb ⊢; linarith + have hdpos : 0 < P.consumption (a, z) (b - h) := by rw [hcd]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) + simp only [objROf, hcd] at hopt + have hslope := P.contOf_sub_le hv z hlow (by linarith) hbmem + rw [show b - (b - h) = h from by ring] at hslope have hscaled := mul_le_mul_of_nonneg_left hslope hβ - have hutil : P.u d - P.u c ≤ (P.discount * P.contSlopeConst) * (d - c) := by - have e : (P.discount : ℝ) * (P.contSlopeConst * h) - = (P.discount * P.contSlopeConst) * (d - c) := by rw [hhdef]; ring + have hutil : P.u d - P.u c + ≤ ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by + have e : (P.discount : ℝ) * (P.contSlopeConstOf v * h) + = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by rw [hhdef]; ring linarith [hopt, hscaled, e.le, e.ge] - -- but the marginal condition says it must repay strictly more have hmarg := hδ c d hcmem (by linarith) hdδ - rw [show ((P.discount : ℝ) * P.contSlopeConst + 1) * (d - c) - = (P.discount * P.contSlopeConst) * (d - c) + (d - c) from by ring] at hmarg + rw [show ((P.discount : ℝ) * P.contSlopeConstOf v + 1) * (d - c) + = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) + (d - c) from by ring] at hmarg rw [hhdef] at hh0 linarith [hmarg, hutil, hh0] +/-- **A consumption floor from the margin.** No use is made of `tendsto_atBot_u`: the argument +runs on concavity and boundedness of the continuation, a positive income floor, and unbounded +marginal value at zero consumption. -/ +theorem exists_consumption_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : + ∃ δ > 0, ∀ s : ℝ × Z, s.1 ∈ Icc assetFloor assetCap → δ ≤ P.consumption s (P.policy s) := by + obtain ⟨δ, hδ, hspec⟩ := + P.exists_consumptionFnOf_floor_of_marginalInada hu P.concaveOn_valueFunction + exact ⟨δ, hδ, fun s hs => hspec s.2 s.1 hs⟩ + +/-- **A uniform bound on saving from a uniform marginal bound**, against any continuation. -/ +theorem policyOf_le_of_marginal_bound {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) {m : ℝ} (hm : 0 < m) + (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : + P.policyOf v (a, z) ≤ assetFloor + 4 * (P.discount : ℝ) * ‖v‖ / m := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + have hnv : (0 : ℝ) ≤ ‖v‖ := norm_nonneg _ + have hbound : (0 : ℝ) ≤ 4 * (P.discount : ℝ) * ‖v‖ / m := by positivity + set b : ℝ := P.policyOf v (a, z) with hbdef + have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos + · rw [← hzero]; linarith + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hcdom : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hcpos : 0 ≤ c := P.nonneg_of_mem_dom hcdom + have hres : P.resources (a, z) = c + b := by + simp only [hcdef, consumptionFnOf, consumption]; ring + set w : ℝ := (b + assetFloor) / 2 with hwdef + have hw0 : assetFloor < w := by rw [hwdef]; linarith + have hwb : w < b := by rw [hwdef]; linarith + have heq : b - w = w - assetFloor := by rw [hwdef]; ring + have hfeas : w ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ + have hcons : P.consumption (a, z) w = c + (b - assetFloor) / 2 := by + simp only [consumption, hres, hwdef]; ring + have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) + simp only [objROf, hcons] at hopt + rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, + show P.policyOf v (a, z) = b from rfl] at hopt + -- concavity of the continuation caps what the forgone saving could have repaid + have hfloormem : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent hfloormem hbreg hw0 hwb + have hb1 := abs_le.mp (P.abs_contOf_le v z w) + have hb0 := abs_le.mp (P.abs_contOf_le v z assetFloor) + have hloss : P.contOf v z b - P.contOf v z w ≤ 2 * ‖v‖ := by + rw [div_le_div_iff₀ (by linarith) (by linarith), ← heq] at hslope + have := le_of_mul_le_mul_right hslope (by linarith : (0 : ℝ) < b - w) + linarith [hb1.2, hb0.1] + have hcmax : c + (b - assetFloor) / 2 ≤ P.maxConsumption := by + have hcm := P.consumption_le_maxConsumption (s := (a, z)) ha hw0.le + rwa [hcons] at hcm + have hmg := hmarg (c + (b - assetFloor) / 2) c hcdom (by linarith) hcmax + rw [show c + (b - assetFloor) / 2 - c = (b - assetFloor) / 2 from by ring] at hmg + rw [← sub_le_iff_le_add', le_div_iff₀ hm] + nlinarith only [hopt, hmg, mul_le_mul_of_nonneg_left hloss hβ] + /-- **The second route to positive consumption.** When utility is unbounded below the reward is `⊥` at zero consumption and positivity is free; when it is bounded — CES with `γ < 1` — it has to be earned, and this is where. The condition is on MARGINS, which is why it survives the loss of diff --git a/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean index a6b61b64e..3c9a5616f 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ImpatientDecline.lean @@ -9,7 +9,7 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationIterate public import LeanPool.AiyagariEquilibrium.Distribution.Uniqueness /-! -# Açıkgöz (2018) Proposition 4: the natural asset bound from impatience alone +# Worst-state asset decline from impatience `NaturalAssetBound` gets assets to decline from a LINEAR lower bound on consumption, which it can supply only for log utility, and with the very lossy constant `exp (-2 β ‖V‖)`. Its own @@ -38,6 +38,16 @@ Only the Euler inequality's own side conditions: consumption positive at every o saving strictly below its cap (`hslack`), which is `policyOf_lt_maxSaving` once the asset cap is slack. Note the direction — `euler_ge` comes from the deviation "save LESS", so it needs room BELOW, which is exactly what `A ≥ a > assetFloor` provides. + +## Source and scope + +Related source: Açıkgöz, April 2016 author draft, Proposition 3, printed page 8 +(https://mpra.ub.uni-muenchen.de/71066/1/MPRA_paper_71066.pdf#page=9). +That proposition concerns worst-state decline; Proposition 4 on the same page concerns a +finite asset bound. The capped variants here have their own explicit positivity, cap-slack +and iid or monotone-transition hypotheses; they do not restate the uncapped proposition. + + -/ @[expose] public section @@ -109,60 +119,11 @@ theorem consumptionFn_mono_of_cont_eq {a a' : ℝ} {z₁ z₂ : Z} (hcont : P.co (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hres : P.resources (a, z₁) ≤ P.resources (a', z₂)) : P.consumptionFn z₁ a ≤ P.consumptionFn z₂ a' := by - set Δ : ℝ := P.resources (a', z₂) - P.resources (a, z₁) with hΔdef - have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith - set b : ℝ := P.policy (a, z₁) with hbdef - set b' : ℝ := P.policy (a', z₂) with hb'def - by_contra hcon - rw [not_le] at hcon - have hgap : b + Δ < b' := by - simp only [consumptionFn, consumption] at hcon - rw [hΔdef]; linarith - have hbmem : b ∈ P.toExtended.feasible (a, z₁) := P.policy_mem _ - have hb'mem : b' ∈ P.toExtended.feasible (a', z₂) := P.policy_mem _ - rw [P.feasible_eq] at hbmem hb'mem - -- the saving cap cannot grow by more than resources do, across states as within one - have hms : P.maxSaving (a', z₂) ≤ P.maxSaving (a, z₁) + Δ := by - have hmin : min assetCap (P.resources (a', z₂)) - ≤ min assetCap (P.resources (a, z₁)) + Δ := by - rcases le_total assetCap (P.resources (a, z₁)) with h | h - · rw [min_eq_left h] - exact le_trans (min_le_left _ _) (by linarith) - · rw [min_eq_right h] - exact le_trans (min_le_right _ _) (by rw [hΔdef]; linarith) - rw [P.maxSaving_eq, P.maxSaving_eq] - linarith - have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z₂) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z₁) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hc1 : P.consumption (a, z₁) b ∈ P.dom := P.consumption_policy_mem_dom (s := (a, z₁)) ha - have hc2 : P.consumption (a', z₂) b' ∈ P.dom := P.consumption_policy_mem_dom (s := (a', z₂)) ha' - have he1 : P.consumption (a', z₂) (b + Δ) = P.consumption (a, z₁) b := by - simp only [consumption, hΔdef]; ring - have he2 : P.consumption (a, z₁) (b' - Δ) = P.consumption (a', z₂) b' := by - simp only [consumption, hΔdef]; ring - have hI := P.objR_le_of_mem ha hshiftdown (by rw [he2]; exact hc2) - have hII := P.objR_le_of_mem ha' hshiftup (by rw [he1]; exact hc1) - have hbreg : b ∈ Icc assetFloor assetCap := P.policy_mem_region _ - have hb'reg : b' ∈ Icc assetFloor assetCap := P.policy_mem_region _ - have hshift := (P.concaveOn_cont z₁).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) - hbreg (by simpa using hb'reg) (by linarith) hΔ - rw [sub_add_cancel] at hshift - have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg - have hscaled := mul_le_mul_of_nonneg_left hshift hβ - simp only [objR, he1, he2, ← hcont] at hI hII - have hIIeq : P.objR (a', z₂) (b + Δ) = P.objR (a', z₂) b' := by - simp only [objR, he1, ← hcont] - nlinarith [hI, hII, hscaled] - have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z₂) (b + Δ) - = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z₂) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe ha' hshiftup (by rw [he1]; exact hc1), hIIeq, - ← P.objectiveE_eq_coe ha' (P.policy_mem _) hc2] - exact P.policy_optimal _ - exact absurd (P.eq_policy_of_optimal ha' hshiftup hbell) (by rw [← hb'def]; linarith) + simpa only [P.consumptionFnOf_valueFunction] using + P.consumptionFnOf_le_across_states P.concaveOn_valueFunction + (z₁ := z₁) (z₂ := z₂) (fun x y _ _ _ => by + change P.cont z₂ y - P.cont z₂ x ≤ P.cont z₁ y - P.cont z₁ x + rw [← hcont]) ha ha' hres /-- Income is iid: the transition probabilities do not depend on today's state. -/ def IidIncome : Prop := ∀ z₁ z₂ z' : Z, P.transitionMatrix z₁ z' = P.transitionMatrix z₂ z' @@ -190,7 +151,8 @@ sequence, and the Doeblin argument needs an ATOM — the constraint has to be re approached. That is a statement about the bottom of the state space, where the Euler equation does not hold, and it stays a calibration. -/ -/-- **Açıkgöz (2018) Proposition 4.** With iid income and `β(1+r) < 1`, assets strictly decline +/-- **Worst-state decline in the capped iid model.** With iid income and `β(1+r) < 1`, +assets strictly decline at the lowest income state, at every asset level above the borrowing constraint. -/ theorem policy_lt_self_of_impatient_iid (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) @@ -222,7 +184,8 @@ theorem exists_exhaust_of_impatient /-! ### CRRA, with the marginal-utility hypotheses discharged -/ -/-- **Açıkgöz Proposition 4 for CRRA.** Marginal utility `c ^ (-γ)` is positive and decreasing +/-- **Worst-state decline for capped CRRA households.** Marginal utility `c ^ (-γ)` is positive +and decreasing for every `γ > 0`, so impatience is the whole hypothesis. -/ theorem crra_exists_exhaust_of_impatient {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean index e395828b8..0b2171842 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -148,13 +148,6 @@ theorem optimal_action_unique_of_concaveSlices {v : (ℝ × Z) →ᵇ ℝ} a₀ = a₁ := by exact P.optimal_action_unique_of_concave hv hs h₀ h₁ hm₀ hm₁ -/-- The consumption function implied by an arbitrary continuation. -/ -noncomputable def consumptionFnOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a : ℝ) : ℝ := - P.consumption (a, z) (P.policyOf v (a, z)) - -theorem consumptionFnOf_valueFunction : - P.consumptionFnOf P.toExtended.valueFunction = P.consumptionFn := rfl - /-! ### The agent with no future eats everything The iteration starts at the zero continuation, where saving buys nothing and the optimal plan @@ -292,6 +285,31 @@ theorem tendsto_policyOf {v : ℕ → (ℝ × Z) →ᵇ ℝ} {w : (ℝ × Z) → (P.policyOf_optimal w s) exact ⟨φ, by rw [← hLeq]; exact hlim'⟩ +/-- The limit step, split off from `concaveOn_consumptionFn_of_preserves`: concavity of the +consumption functions of the iterates passes to the fixed point, because optimal actions survive +a uniform limit. -/ +theorem concaveOn_consumptionFn_of_iterates + (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) + (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by + set T := P.toExtended.bellman with hTdef + have hlim : Tendsto (fun n => T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) atTop + (𝓝 P.toExtended.valueFunction) := P.toExtended.tendsto_iterate_valueFunction 0 + have hpt : ∀ a ∈ Icc assetFloor assetCap, + Tendsto (fun n => P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (P.consumptionFn z a)) := by + intro a ha + have hp := P.tendsto_policyOf hlim P.concaveSlices_valueFunction (s := (a, z)) ha + have he : P.consumptionFn z a + = P.resources (a, z) - P.policyOf P.toExtended.valueFunction (a, z) := rfl + rw [he] + simpa only [consumptionFnOf, consumption] using tendsto_const_nhds.sub hp + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ + refine le_of_tendsto_of_tendsto + (((hpt x hx).const_smul θ).add ((hpt y hy).const_smul φ)) (hpt _ hm) + (Eventually.of_forall fun n => (hcons n z).2 hx hy hθ hφ hθφ) + /-! ### Carroll and Kimball, reduced to one step -/ /-- **The consumption function of the fixed point is concave whenever the Bellman operator @@ -316,22 +334,7 @@ theorem concaveOn_consumptionFn_of_preserves induction n with | zero => exact P.concaveOn_consumptionFnOf_zero | succ k ih => rw [Function.iterate_succ_apply']; exact hT _ (hslices k) ih - have hlim : Tendsto (fun n => T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 P.toExtended.valueFunction) := - P.toExtended.tendsto_iterate_valueFunction 0 - have hpt : ∀ a ∈ Icc assetFloor assetCap, - Tendsto (fun n => P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop - (𝓝 (P.consumptionFn z a)) := by - intro a ha - have hp := P.tendsto_policyOf hlim P.concaveSlices_valueFunction (s := (a, z)) ha - have he : P.consumptionFn z a - = P.resources (a, z) - P.policyOf P.toExtended.valueFunction (a, z) := rfl - rw [he] - simpa only [consumptionFnOf, consumption] using tendsto_const_nhds.sub hp - refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ - have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ - refine le_of_tendsto_of_tendsto - (((hpt x hx).const_smul θ).add ((hpt y hy).const_smul φ)) (hpt _ hm) - (Eventually.of_forall fun n => (hcons n z).2 hx hy hθ hφ hθφ) + exact P.concaveOn_consumptionFn_of_iterates hcons z /-! ### The endogenous-gridpoint transfer diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean index 0a6cccef1..c05a26a1a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean @@ -51,7 +51,7 @@ else that theorem needs about the consumption function is discharged here. @[expose] public section -open Set Filter Topology +open Set Filter Topology BoundedContinuousFunction namespace LeanEconomics @@ -64,18 +64,26 @@ variable (P : IncomeFluctuation Z assetFloor assetCap) /-- **The continuation value is concave in savings**, being a non-negative combination of the value function's concave slices. -/ theorem concaveOn_cont (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.cont z) := by - refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ - simp only [cont, smul_eq_mul] - rw [Finset.mul_sum, Finset.mul_sum, ← Finset.sum_add_distrib] - refine Finset.sum_le_sum fun z' _ => ?_ - have hvz := (P.concaveOn_valueFunction z').2 hx hy hθ hφ hθφ - simp only [smul_eq_mul] at hvz - nlinarith [P.transitionMatrix_nonneg z z'] + exact P.concaveOn_contOf P.concaveOn_valueFunction z /-- Consumption at the optimum, as a function of assets. -/ noncomputable def consumptionFn (z : Z) (a : ℝ) : ℝ := P.consumption (a, z) (P.policy (a, z)) +/-- The consumption function implied by an arbitrary continuation. -/ +noncomputable def consumptionFnOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (a : ℝ) : ℝ := + P.consumption (a, z) (P.policyOf v (a, z)) + +theorem consumptionFnOf_valueFunction : + P.consumptionFnOf P.toExtended.valueFunction = P.consumptionFn := rfl + +/-- **Consumption at the optimum lies in the utility domain**, whatever the continuation. This +needs no positivity: the optimum's objective is a real number, so the reward is not `⊥`. -/ +theorem consumption_policyOf_mem_dom (v : (ℝ × Z) →ᵇ ℝ) {z : Z} {a : ℝ} + (ha : a ∈ Icc assetFloor assetCap) : P.consumptionFnOf v z a ∈ P.dom := + (P.bellmanFn_eq_of_optimal (s := (a, z)) ha (P.policyOf_mem v (a, z)) + (P.policyOf_optimal v (a, z))).1 + theorem consumptionFn_pos (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : 0 < P.consumptionFn z a := @@ -97,69 +105,92 @@ theorem maxSaving_le_add_sub {a a' : ℝ} {z : Z} (hle : a ≤ a') : rw [P.maxSaving_eq, P.maxSaving_eq] linarith -/-- **Consumption rises with assets.** The two households swap savings plans shifted by their -resource gap, which leaves consumption alone and moves only the continuation — so the -comparison is increasing differences of the continuation, exactly as `policy_mono` is -increasing differences of `u`. -/ -theorem consumptionFn_mono {a a' : ℝ} {z : Z} - (ha : a ∈ Icc assetFloor assetCap) - (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : - P.consumptionFn z a ≤ P.consumptionFn z a' := by - set Δ : ℝ := P.resources (a', z) - P.resources (a, z) with hΔdef - have hΔ : 0 ≤ Δ := by - rw [hΔdef]; linarith [P.resources_mono (z := z) hle] - set b : ℝ := P.policy (a, z) with hbdef - set b' : ℝ := P.policy (a', z) with hb'def - -- the goal is `b' ≤ b + Δ` +/-- **Consumption is ordered across states** against a continuation whose increments are +antitone in income. -/ +theorem consumptionFnOf_le_across_states {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) {z₁ z₂ : Z} + (hid : ∀ x y : ℝ, x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → + P.contOf v z₂ y - P.contOf v z₂ x ≤ P.contOf v z₁ y - P.contOf v z₁ x) + {a a' : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) + (hres : P.resources (a, z₁) ≤ P.resources (a', z₂)) : + P.consumptionFnOf v z₁ a ≤ P.consumptionFnOf v z₂ a' := by + set Δ : ℝ := P.resources (a', z₂) - P.resources (a, z₁) with hΔdef + have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith + set b : ℝ := P.policyOf v (a, z₁) with hbdef + set b' : ℝ := P.policyOf v (a', z₂) with hb'def by_contra hcon rw [not_le] at hcon have hgap : b + Δ < b' := by - simp only [consumptionFn, consumption] at hcon + simp only [consumptionFnOf, consumption] at hcon rw [hΔdef]; linarith - have hbmem : b ∈ P.toExtended.feasible (a, z) := P.policy_mem _ - have hb'mem : b' ∈ P.toExtended.feasible (a', z) := P.policy_mem _ + have hbmem : b ∈ P.toExtended.feasible (a, z₁) := P.policyOf_mem v (a, z₁) + have hb'mem : b' ∈ P.toExtended.feasible (a', z₂) := P.policyOf_mem v (a', z₂) rw [P.feasible_eq] at hbmem hb'mem - -- both shifted plans are feasible - have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z) := by + have hms : P.maxSaving (a', z₂) ≤ P.maxSaving (a, z₁) + Δ := by + have hmin : min assetCap (P.resources (a', z₂)) + ≤ min assetCap (P.resources (a, z₁)) + Δ := by + rcases le_total assetCap (P.resources (a, z₁)) with h | h + · rw [min_eq_left h] + exact le_trans (min_le_left _ _) (by linarith) + · rw [min_eq_right h] + exact le_trans (min_le_right _ _) (by rw [hΔdef]; linarith) + rw [P.maxSaving_eq, P.maxSaving_eq] + linarith + have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z₂) := by rw [P.feasible_eq] exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z) := by + have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z₁) := by rw [P.feasible_eq] - refine ⟨by linarith [hbmem.1], ?_⟩ - have := P.maxSaving_le_add_sub (z := z) hle - rw [← hΔdef] at this - linarith [hb'mem.2] - -- the shift is consumption-neutral, so only two consumption levels appear - have hc1 : P.consumption (a, z) b ∈ P.dom := P.consumption_policy_mem_dom (s := (a, z)) ha - have hc2 : P.consumption (a', z) b' ∈ P.dom := - P.consumption_policy_mem_dom (s := (a', z)) ha' - have he1 : P.consumption (a', z) (b + Δ) = P.consumption (a, z) b := by + exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ + have hc1 : P.consumption (a, z₁) b ∈ P.dom := P.consumption_policyOf_mem_dom v ha + have hc2 : P.consumption (a', z₂) b' ∈ P.dom := P.consumption_policyOf_mem_dom v ha' + have he1 : P.consumption (a', z₂) (b + Δ) = P.consumption (a, z₁) b := by simp only [consumption, hΔdef]; ring - have he2 : P.consumption (a, z) (b' - Δ) = P.consumption (a', z) b' := by + have he2 : P.consumption (a, z₁) (b' - Δ) = P.consumption (a', z₂) b' := by simp only [consumption, hΔdef]; ring - -- optimality at each state, against the other's shifted plan - have hI := P.objR_le_of_mem ha hshiftdown (by rw [he2]; exact hc2) - have hII := P.objR_le_of_mem ha' hshiftup (by rw [he1]; exact hc1) - -- increasing differences of the continuation - have hbreg : b ∈ Icc assetFloor assetCap := P.policy_mem_region _ - have hb'reg : b' ∈ Icc assetFloor assetCap := P.policy_mem_region _ - have hshift := (P.concaveOn_cont z).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) + have hI := P.objROf_le_of_mem v ha hshiftdown (by rw [he2]; exact hc2) + have hII := P.objROf_le_of_mem v ha' hshiftup (by rw [he1]; exact hc1) + have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + have hb'reg : b' ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) + -- concavity of the poorer continuation, shifted by `Δ` + have hshift := (P.concaveOn_contOf hv z₁).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) hbreg (by simpa using hb'reg) (by linarith) hΔ rw [sub_add_cancel] at hshift - -- the two optimality inequalities therefore hold with equality + -- and the richer continuation is flatter still + have hacross := hid (b + Δ) b' ⟨by linarith [hbreg.1], by linarith [hb'reg.2]⟩ hb'reg hgap.le have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg - have hscaled := mul_le_mul_of_nonneg_left hshift hβ - simp only [objR, he1, he2] at hI hII - have hIIeq : P.objR (a', z) (b + Δ) = P.objR (a', z) b' := by - simp only [objR, he1] - nlinarith [hI, hII, hscaled] - -- so the shifted plan attains the optimum at the richer state, and uniqueness closes it - have hbell : P.toExtended.objectiveE P.toExtended.valueFunction (a', z) (b + Δ) - = ((P.toExtended.bellmanFn P.toExtended.valueFunction (a', z) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe ha' hshiftup (by rw [he1]; exact hc1), hIIeq, - ← P.objectiveE_eq_coe ha' (P.policy_mem _) hc2] - exact P.policy_optimal _ - exact absurd (P.eq_policy_of_optimal ha' hshiftup hbell) (by rw [← hb'def]; linarith) + have hchain := mul_le_mul_of_nonneg_left (le_trans hacross hshift) hβ + simp only [objROf, he1, he2] at hI hII + have hIIeq : P.objROf v (a', z₂) (b + Δ) = P.objROf v (a', z₂) b' := by + simp only [objROf, he1] + linarith [hI, hII, hchain] + have hbell : P.toExtended.objectiveE v (a', z₂) (b + Δ) + = ((P.toExtended.bellmanFn v (a', z₂) : ℝ) : EReal) := by + rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, + ← P.objectiveE_eq_coe_of v ha' (P.policyOf_mem v _) hc2] + exact P.policyOf_optimal v _ + exact absurd (P.optimal_action_unique_of_concave hv ha' hshiftup (P.policyOf_mem v _) + hbell (P.policyOf_optimal v _)) (by rw [← hb'def]; linarith) + +/-- **Consumption rises with assets, whatever the continuation.** The exchange argument of +`consumptionFn_mono`, run against `v`. -/ +theorem consumptionFnOf_mono {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.consumptionFnOf v z a ≤ P.consumptionFnOf v z a' := by + exact P.consumptionFnOf_le_across_states hv (fun _ _ _ _ _ => le_rfl) ha ha' + (P.resources_mono hle) + +/-- **Consumption rises with assets.** The two households swap savings plans shifted by their +resource gap, which leaves consumption alone and moves only the continuation — so the +comparison is increasing differences of the continuation, exactly as `policy_mono` is +increasing differences of `u`. -/ +theorem consumptionFn_mono {a a' : ℝ} {z : Z} + (ha : a ∈ Icc assetFloor assetCap) + (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : + P.consumptionFn z a ≤ P.consumptionFn z a' := by + simpa only [P.consumptionFnOf_valueFunction] using + P.consumptionFnOf_mono P.concaveOn_valueFunction ha ha' hle /-- **The marginal propensities are both non-negative.** Consumption rises with assets, and by no more than resources do — the second half is `policy_mono`. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index 05c44f4d1..5881159fe 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -606,7 +606,7 @@ theorem policy_mono_withRate_hara {assetCap : ℝ} (P : IncomeFluctuation Z 0 as (fun y hy => (hara_marginal_pos hη (mem_Ioi.mp hy)).le) hposv hposw hslackv hslackw hcons ha z -/-- **Açıkgöz Proposition 4 for shifted CRRA**: the decline condition is impatience. -/ +/-- **Worst-state decline for shifted CRRA**: the decline condition is impatience. -/ theorem hara_exists_exhaust_of_impatient {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hiid : P.IidIncome) @@ -635,7 +635,6 @@ theorem hara_cost_of_saving {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) {m h R (by linarith) (by linarith) hb0 hbh have e1 : η + R - b = η + (R - b) := by ring have e2 : η + R - h = η + (R - h) := by ring - have e3 : η + m - h = η + m - h := rfl rw [e1, e2] at hstep simpa only [haraUtility] using hstep diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index de96900bc..dc82edb0d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -35,15 +35,6 @@ variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCa /-! ### The objective against an arbitrary continuation -/ -theorem concaveOn_contOf {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (z : Z) : - ConcaveOn ℝ (Icc assetFloor assetCap) (P.contOf v z) := by - refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ - simp only [contOf, smul_eq_mul, Finset.mul_sum, ← Finset.sum_add_distrib] - refine Finset.sum_le_sum fun z' _ => ?_ - have hz' := (hv z').2 hx hy hθ hφ hθφ - simp only [smul_eq_mul] at hz' - nlinarith [mul_le_mul_of_nonneg_left hz' (P.transitionMatrix_nonneg z z')] - /-! ### Positivity -/ /-- **Consumption never vanishes at the optimum, whatever the continuation.** With utility @@ -59,13 +50,6 @@ theorem consumptionFnOf_pos (hd : P.Unbounded) (v : (ℝ × Z) →ᵇ ℝ) {z : rw [hd] at hmem exact hmem -/-- **Consumption at the optimum lies in the utility domain**, whatever the continuation. This -needs no positivity: the optimum's objective is a real number, so the reward is not `⊥`. -/ -theorem consumption_policyOf_mem_dom (v : (ℝ × Z) →ᵇ ℝ) {z : Z} {a : ℝ} - (ha : a ∈ Icc assetFloor assetCap) : P.consumptionFnOf v z a ∈ P.dom := - (P.bellmanFn_eq_of_optimal (s := (a, z)) ha (P.policyOf_mem v (a, z)) - (P.policyOf_optimal v (a, z))).1 - /-- **Consumption is positive at the optimum against every continuation with concave slices.** The induction sees the iterates, not the value function, so this is what it needs. With utility unbounded below it is automatic; for the bounded CES family it is earned from a marginal Inada @@ -96,60 +80,6 @@ theorem positiveConsumptionAll_of_rich (hrich : assetCap - assetFloor < P.minCon /-! ### Monotonicity of the consumption function -/ -/-- **Consumption rises with assets, whatever the continuation.** The exchange argument of -`consumptionFn_mono`, run against `v`. -/ -theorem consumptionFnOf_mono {v : (ℝ × Z) →ᵇ ℝ} - (hv : ConcaveSlices assetFloor assetCap v) {a a' : ℝ} {z : Z} - (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) (hle : a ≤ a') : - P.consumptionFnOf v z a ≤ P.consumptionFnOf v z a' := by - set Δ : ℝ := P.resources (a', z) - P.resources (a, z) with hΔdef - have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith [P.resources_mono (z := z) hle] - set b : ℝ := P.policyOf v (a, z) with hbdef - set b' : ℝ := P.policyOf v (a', z) with hb'def - by_contra hcon - rw [not_le] at hcon - have hgap : b + Δ < b' := by - simp only [consumptionFnOf, consumption] at hcon - rw [hΔdef]; linarith - have hbmem : b ∈ P.toExtended.feasible (a, z) := P.policyOf_mem v _ - have hb'mem : b' ∈ P.toExtended.feasible (a', z) := P.policyOf_mem v _ - rw [P.feasible_eq] at hbmem hb'mem - have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - refine ⟨by linarith [hbmem.1], ?_⟩ - have hms := P.maxSaving_le_add_sub (z := z) hle - rw [← hΔdef] at hms - linarith [hb'mem.2] - have hc1 : P.consumption (a, z) b ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have hc2 : P.consumption (a', z) b' ∈ P.dom := P.consumption_policyOf_mem_dom v ha' - have he1 : P.consumption (a', z) (b + Δ) = P.consumption (a, z) b := by - simp only [consumption, hΔdef]; ring - have he2 : P.consumption (a, z) (b' - Δ) = P.consumption (a', z) b' := by - simp only [consumption, hΔdef]; ring - have hI := P.objROf_le_of_mem v ha hshiftdown (by rw [he2]; exact hc2) - have hII := P.objROf_le_of_mem v ha' hshiftup (by rw [he1]; exact hc1) - have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) - have hb'reg : b' ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) - have hshift := (P.concaveOn_contOf hv z).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) - hbreg (by simpa using hb'reg) (by linarith) hΔ - rw [sub_add_cancel] at hshift - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - have hscaled := mul_le_mul_of_nonneg_left hshift hβ - simp only [objROf, he1, he2] at hI hII - have hIIeq : P.objROf v (a', z) (b + Δ) = P.objROf v (a', z) b' := by - simp only [objROf, he1] - nlinarith only [hI, hII, hscaled] - have hbell : P.toExtended.objectiveE v (a', z) (b + Δ) - = ((P.toExtended.bellmanFn v (a', z) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, - ← P.objectiveE_eq_coe_of v ha' (P.policyOf_mem v _) hc2] - exact P.policyOf_optimal v _ - exact absurd (P.optimal_action_unique_of_concaveSlices hv (s := (a', z)) ha' hshiftup - (P.policyOf_mem v _) hbell (P.policyOf_optimal v _)) (by rw [← hb'def]; linarith) - /-! ### Saving stays strictly inside the feasible set Positive consumption already keeps the saving below resources; what is left is the asset CAP, @@ -310,69 +240,6 @@ theorem oscOn_valueFunction : P.OscOn P.toExtended.valueFunction P.oscSpread := exact le_of_tendsto_of_tendsto ((hpt (x, z)).sub (hpt (y, z'))) tendsto_const_nhds (Eventually.of_forall fun n => P.oscOn_iterate n x hx y hy z z') -theorem abs_contOf_le (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : |P.contOf v z x| ≤ ‖v‖ := by - have h1 : |∑ z' : Z, P.transitionMatrix z z' * v (x, z')| - ≤ ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| := Finset.abs_sum_le_sum_abs _ _ - have h2 : ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| - ≤ ∑ z' : Z, P.transitionMatrix z z' * ‖v‖ := by - refine Finset.sum_le_sum fun z' _ => ?_ - rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] - exact mul_le_mul_of_nonneg_left - (by simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (x, z')) - (P.transitionMatrix_nonneg z z') - rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at h2 - simp only [contOf] - linarith - -/-- **A uniform bound on saving from a uniform marginal bound**, against any continuation. -/ -theorem policyOf_le_of_marginal_bound {v : (ℝ × Z) →ᵇ ℝ} - (hv : ConcaveSlices assetFloor assetCap v) {m : ℝ} (hm : 0 < m) - (hmarg : ∀ c d : ℝ, d ∈ P.dom → d ≤ c → c ≤ P.maxConsumption → m * (c - d) ≤ P.u c - P.u d) - {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : - P.policyOf v (a, z) ≤ assetFloor + 4 * (P.discount : ℝ) * ‖v‖ / m := by - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - have hnv : (0 : ℝ) ≤ ‖v‖ := norm_nonneg _ - have hbound : (0 : ℝ) ≤ 4 * (P.discount : ℝ) * ‖v‖ / m := by positivity - set b : ℝ := P.policyOf v (a, z) with hbdef - have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) - rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos - · rw [← hzero]; linarith - set c : ℝ := P.consumptionFnOf v z a with hcdef - have hcdom : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have hcpos : 0 ≤ c := P.nonneg_of_mem_dom hcdom - have hres : P.resources (a, z) = c + b := by - simp only [hcdef, consumptionFnOf, consumption]; ring - set w : ℝ := (b + assetFloor) / 2 with hwdef - have hw0 : assetFloor < w := by rw [hwdef]; linarith - have hwb : w < b := by rw [hwdef]; linarith - have heq : b - w = w - assetFloor := by rw [hwdef]; ring - have hfeas : w ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcons : P.consumption (a, z) w = c + (b - assetFloor) / 2 := by - simp only [consumption, hres, hwdef]; ring - have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith - have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) - simp only [objROf, hcons] at hopt - rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, - show P.policyOf v (a, z) = b from rfl] at hopt - -- concavity of the continuation caps what the forgone saving could have repaid - have hfloormem : assetFloor ∈ Icc assetFloor assetCap := ⟨le_rfl, P.assetFloor_le_assetCap⟩ - have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent hfloormem hbreg hw0 hwb - have hb1 := abs_le.mp (P.abs_contOf_le v z w) - have hb0 := abs_le.mp (P.abs_contOf_le v z assetFloor) - have hloss : P.contOf v z b - P.contOf v z w ≤ 2 * ‖v‖ := by - rw [div_le_div_iff₀ (by linarith) (by linarith), ← heq] at hslope - have := le_of_mul_le_mul_right hslope (by linarith : (0 : ℝ) < b - w) - linarith [hb1.2, hb0.1] - have hcmax : c + (b - assetFloor) / 2 ≤ P.maxConsumption := by - have hcm := P.consumption_le_maxConsumption (s := (a, z)) ha hw0.le - rwa [hcons] at hcm - have hmg := hmarg (c + (b - assetFloor) / 2) c hcdom (by linarith) hcmax - rw [show c + (b - assetFloor) / 2 - c = (b - assetFloor) / 2 from by ring] at hmg - rw [← sub_le_iff_le_add', le_div_iff₀ hm] - nlinarith only [hopt, hmg, mul_le_mul_of_nonneg_left hloss hβ] - /-- The Bellman operator adds at most the reward and contracts what it inherits. -/ theorem norm_bellman_le (v : (ℝ × Z) →ᵇ ℝ) : ‖P.toExtended.bellman v‖ @@ -422,31 +289,6 @@ theorem norm_iterate_le (n : ℕ) : /-! ### Carroll and Kimball, assembled along the iteration -/ -/-- The limit step, split off from `concaveOn_consumptionFn_of_preserves`: concavity of the -consumption functions of the iterates passes to the fixed point, because optimal actions survive -a uniform limit. -/ -theorem concaveOn_consumptionFn_of_iterates - (hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) - (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) (z : Z) : - ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by - set T := P.toExtended.bellman with hTdef - have hlim : Tendsto (fun n => T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) atTop - (𝓝 P.toExtended.valueFunction) := P.toExtended.tendsto_iterate_valueFunction 0 - have hpt : ∀ a ∈ Icc assetFloor assetCap, - Tendsto (fun n => P.consumptionFnOf (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop - (𝓝 (P.consumptionFn z a)) := by - intro a ha - have hp := P.tendsto_policyOf hlim P.concaveSlices_valueFunction (s := (a, z)) ha - have he : P.consumptionFn z a - = P.resources (a, z) - P.policyOf P.toExtended.valueFunction (a, z) := rfl - rw [he] - simpa only [consumptionFnOf, consumption] using tendsto_const_nhds.sub hp - refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ - have hm : θ • x + φ • y ∈ Icc assetFloor assetCap := convex_Icc _ _ hx hy hθ hφ hθφ - refine le_of_tendsto_of_tendsto - (((hpt x hx).const_smul θ).add ((hpt y hy).const_smul φ)) (hpt _ hm) - (Eventually.of_forall fun n => (hcons n z).2 hx hy hθ hφ hθφ) - /-- **Carroll and Kimball (1996) for CRRA.** The consumption function is concave. Everything is now proved except the asset cap: `hslack` says the household never wants to save @@ -532,89 +374,6 @@ from the MARGIN, which is what `ConsumptionFloor` does at the fixed point. The i the iterates, so the same argument is run against an arbitrary continuation — and it goes through unchanged, because it only ever used concavity and boundedness of the continuation. -/ -/-- The state-independent bound on the continuation's slope, read against `v`. -/ -noncomputable def contSlopeConstOf (v : (ℝ × Z) →ᵇ ℝ) : ℝ := 4 * ‖v‖ / P.minConsumption - -theorem contSlopeConstOf_nonneg (v : (ℝ × Z) →ᵇ ℝ) : 0 ≤ P.contSlopeConstOf v := - div_nonneg (by positivity) P.minConsumption_pos.le - -/-- **The continuation's slope is bounded by a state-independent constant**, above -`assetFloor + minConsumption / 2`, whatever the continuation. -/ -theorem contOf_sub_le {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (z : Z) - {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) - (hy : y ∈ Icc assetFloor assetCap) : - P.contOf v z y - P.contOf v z x ≤ P.contSlopeConstOf v * (y - x) := by - have hmin := P.minConsumption_pos - have hx0 : assetFloor < x := by linarith - have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent - (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy - have hb1 := abs_le.mp (P.abs_contOf_le v z x) - have hb2 := abs_le.mp (P.abs_contOf_le v z assetFloor) - have hhalf : P.contSlopeConstOf v * (P.minConsumption / 2) = 2 * ‖v‖ := by - rw [contSlopeConstOf]; field_simp; ring - have hbig : P.contOf v z x - P.contOf v z assetFloor - ≤ P.contSlopeConstOf v * (x - assetFloor) := by - have hstep : P.contSlopeConstOf v * (P.minConsumption / 2) - ≤ P.contSlopeConstOf v * (x - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) (P.contSlopeConstOf_nonneg v) - rw [hhalf] at hstep - linarith [hb1.2, hb2.1] - rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope - have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) - ≤ (P.contSlopeConstOf v * (x - assetFloor)) * (y - x) := by - nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] - nlinarith [hprod, hx0] - -/-- **A consumption floor from the margin, against an arbitrary continuation.** -/ -theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) - {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) : - ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by - have hmin := P.minConsumption_pos - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - obtain ⟨δ₀, hδ₀, hδ⟩ := hu ((P.discount : ℝ) * P.contSlopeConstOf v + 1) - refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ - by_contra hlt - rw [not_le] at hlt - set d : ℝ := min δ₀ (P.minConsumption / 4) with hd - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumption (a, z) b with hcdef - have hltc : c < d := hlt - have hdδ : d ≤ δ₀ := min_le_left _ _ - have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ - have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem - have hres : P.minConsumption ≤ P.resources (a, z) - assetFloor := - P.minConsumption_le_consumption_floor (a, z) - have hcb : c = P.resources (a, z) - b := rfl - have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith - set h : ℝ := d - c with hhdef - have hh0 : 0 < h := by rw [hhdef]; linarith - have hhle : h ≤ P.minConsumption / 4 := by rw [hhdef]; linarith - have hlow : assetFloor + P.minConsumption / 2 ≤ b - h := by linarith - have hbmem : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hfeas : b - h ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcd : P.consumption (a, z) (b - h) = d := by - simp only [consumption] at hcb ⊢; linarith - have hdpos : 0 < P.consumption (a, z) (b - h) := by rw [hcd]; linarith - have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) - simp only [objROf, hcd] at hopt - have hslope := P.contOf_sub_le hv z hlow (by linarith) hbmem - rw [show b - (b - h) = h from by ring] at hslope - have hscaled := mul_le_mul_of_nonneg_left hslope hβ - have hutil : P.u d - P.u c - ≤ ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by - have e : (P.discount : ℝ) * (P.contSlopeConstOf v * h) - = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by rw [hhdef]; ring - linarith [hopt, hscaled, e.le, e.ge] - have hmarg := hδ c d hcmem (by linarith) hdδ - rw [show ((P.discount : ℝ) * P.contSlopeConstOf v + 1) * (d - c) - = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) + (d - c) from by ring] at hmarg - rw [hhdef] at hh0 - linarith [hmarg, hutil, hh0] - /-- **Positive consumption against every continuation, for the bounded family.** -/ theorem positiveConsumptionAll_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : P.PositiveConsumptionAll := by diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean index 3c6b5f32e..502dd5605 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -99,6 +99,30 @@ noncomputable def contOf (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : ℝ := noncomputable def objROf (v : (ℝ × Z) →ᵇ ℝ) (s : ℝ × Z) (x : ℝ) : ℝ := P.u (P.consumption s x) + P.discount * P.contOf v s.2 x +theorem concaveOn_contOf {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) (z : Z) : + ConcaveOn ℝ (Icc assetFloor assetCap) (P.contOf v z) := by + refine ⟨convex_Icc _ _, fun x hx y hy θ φ hθ hφ hθφ => ?_⟩ + simp only [contOf, smul_eq_mul, Finset.mul_sum, ← Finset.sum_add_distrib] + refine Finset.sum_le_sum fun z' _ => ?_ + have hz' := (hv z').2 hx hy hθ hφ hθφ + simp only [smul_eq_mul] at hz' + nlinarith [mul_le_mul_of_nonneg_left hz' (P.transitionMatrix_nonneg z z')] + +theorem abs_contOf_le (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : |P.contOf v z x| ≤ ‖v‖ := by + have h1 : |∑ z' : Z, P.transitionMatrix z z' * v (x, z')| + ≤ ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| := Finset.abs_sum_le_sum_abs _ _ + have h2 : ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| + ≤ ∑ z' : Z, P.transitionMatrix z z' * ‖v‖ := by + refine Finset.sum_le_sum fun z' _ => ?_ + rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] + exact mul_le_mul_of_nonneg_left + (by simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (x, z')) + (P.transitionMatrix_nonneg z z') + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at h2 + simp only [contOf] + linarith + /-- The objective in the reals agrees with the extended objective where consumption is positive. -/ theorem objectiveE_eq_coe_of (v : (ℝ × Z) →ᵇ ℝ) {a : ℝ} {z : Z} {x : ℝ} diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean index 369fa6402..152992d8a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean @@ -119,7 +119,6 @@ theorem bellmanFn_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r have hscale : (1 + r₁) / (1 + r₂) * a ∈ Icc (0 : ℝ) assetCap := by refine ⟨mul_nonneg (le_of_lt (div_pos hR₁ hR₂)) ha.1, ?_⟩ nlinarith [ha.1, ha.2, div_pos hR₁ hR₂] - have hback : (1 + r₂) / (1 + r₁) * ((1 + r₁) / (1 + r₂) * a) = a := by field_simp refine le_antisymm ?_ ?_ · set b := (P.withRate r₁ h₁).policyOf v (a, z) with hb have heq := (P.withRate r₁ h₁).lazyValue_eq_dom v z ha diff --git a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean index 5e565c9f2..f06b36a36 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean @@ -20,7 +20,7 @@ route that does not carry `1/(1-β)` or `1/(1-βR)` in a constant. * The corner from the Euler inequality (`crra_policy_eq_zero_of_euler`), whose test is `βR · minIncome^(-γ) < (income z₀ + R a₀)^(-γ)` — one inequality, no Lipschitz constant. -* Decline from Açıkgöz Proposition 4 (`policy_lt_self_of_impatient_iid`), which holds at every +* Euler-based worst-state decline (`policy_lt_self_of_impatient_iid`), which holds at every asset level above the constraint given `βR < 1` and iid income. * Cap slack at the fixed point from the minimal MPC (`crra_policy_lt_cap_of_minMPC`). diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index 90cf94a02..5e638645a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -227,17 +227,7 @@ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by subst hfl - have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one - have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR - set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef - have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ - have hκ0 : 0 < P.minMPC γ := hκ have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ - have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) - -- `Þ ^ (-γ) = 1/(βR)`, the one rpow identity the step needs - have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by - rw [hTdef, ← Real.rpow_mul hβR0.le, - show (1 / γ) * (-γ) = -1 from by field_simp, Real.rpow_neg_one] intro n induction n with | zero => @@ -644,7 +634,8 @@ theorem hara_exists_exhaust_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ /-! ### The exhaustion data, without `IidIncome` -`exists_exhaust_of_impatient` (Açıkgöz Proposition 4) also supplies the decline half from +`exists_exhaust_of_impatient` (the capped worst-state decline argument) also supplies +the decline half from impatience, but it gets there by comparing income states: consumption is lowest where income is lowest, which needs `IidIncome` so that the continuation does not depend on today's state. The minimal-MPC route compares nothing. It is a statement about the budget and the Euler inequality diff --git a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean index a7c1c0b67..2d98c1e4b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean @@ -99,73 +99,6 @@ The exchange argument of `consumptionFn_mono_of_cont_eq`, with the shared contin by an ordered pair: the richer state has a flatter continuation as well as more resources, and both push consumption up. -/ -/-- **Consumption is ordered across states** against a continuation whose increments are -antitone in income. -/ -theorem consumptionFnOf_le_across_states {v : (ℝ × Z) →ᵇ ℝ} - (hv : ConcaveSlices assetFloor assetCap v) {z₁ z₂ : Z} - (hid : ∀ x y : ℝ, x ∈ Icc assetFloor assetCap → y ∈ Icc assetFloor assetCap → x ≤ y → - P.contOf v z₂ y - P.contOf v z₂ x ≤ P.contOf v z₁ y - P.contOf v z₁ x) - {a a' : ℝ} (ha : a ∈ Icc assetFloor assetCap) (ha' : a' ∈ Icc assetFloor assetCap) - (hres : P.resources (a, z₁) ≤ P.resources (a', z₂)) : - P.consumptionFnOf v z₁ a ≤ P.consumptionFnOf v z₂ a' := by - set Δ : ℝ := P.resources (a', z₂) - P.resources (a, z₁) with hΔdef - have hΔ : 0 ≤ Δ := by rw [hΔdef]; linarith - set b : ℝ := P.policyOf v (a, z₁) with hbdef - set b' : ℝ := P.policyOf v (a', z₂) with hb'def - by_contra hcon - rw [not_le] at hcon - have hgap : b + Δ < b' := by - simp only [consumptionFnOf, consumption] at hcon - rw [hΔdef]; linarith - have hbmem : b ∈ P.toExtended.feasible (a, z₁) := P.policyOf_mem v (a, z₁) - have hb'mem : b' ∈ P.toExtended.feasible (a', z₂) := P.policyOf_mem v (a', z₂) - rw [P.feasible_eq] at hbmem hb'mem - have hms : P.maxSaving (a', z₂) ≤ P.maxSaving (a, z₁) + Δ := by - have hmin : min assetCap (P.resources (a', z₂)) - ≤ min assetCap (P.resources (a, z₁)) + Δ := by - rcases le_total assetCap (P.resources (a, z₁)) with h | h - · rw [min_eq_left h] - exact le_trans (min_le_left _ _) (by linarith) - · rw [min_eq_right h] - exact le_trans (min_le_right _ _) (by rw [hΔdef]; linarith) - rw [P.maxSaving_eq, P.maxSaving_eq] - linarith - have hshiftup : b + Δ ∈ P.toExtended.feasible (a', z₂) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hshiftdown : b' - Δ ∈ P.toExtended.feasible (a, z₁) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [hb'mem.2]⟩ - have hc1 : P.consumption (a, z₁) b ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have hc2 : P.consumption (a', z₂) b' ∈ P.dom := P.consumption_policyOf_mem_dom v ha' - have he1 : P.consumption (a', z₂) (b + Δ) = P.consumption (a, z₁) b := by - simp only [consumption, hΔdef]; ring - have he2 : P.consumption (a, z₁) (b' - Δ) = P.consumption (a', z₂) b' := by - simp only [consumption, hΔdef]; ring - have hI := P.objROf_le_of_mem v ha hshiftdown (by rw [he2]; exact hc2) - have hII := P.objROf_le_of_mem v ha' hshiftup (by rw [he1]; exact hc1) - have hbreg : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) - have hb'reg : b' ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v _) - -- concavity of the poorer continuation, shifted by `Δ` - have hshift := (P.concaveOn_contOf hv z₁).sub_le_sub_of_shift (c₁ := b) (c₂ := b' - Δ) (Δ := Δ) - hbreg (by simpa using hb'reg) (by linarith) hΔ - rw [sub_add_cancel] at hshift - -- and the richer continuation is flatter still - have hacross := hid (b + Δ) b' ⟨by linarith [hbreg.1], by linarith [hb'reg.2]⟩ hb'reg hgap.le - have hβ : (0 : ℝ) ≤ P.discount := P.discount.coe_nonneg - have hchain := mul_le_mul_of_nonneg_left (le_trans hacross hshift) hβ - simp only [objROf, he1, he2] at hI hII - have hIIeq : P.objROf v (a', z₂) (b + Δ) = P.objROf v (a', z₂) b' := by - simp only [objROf, he1] - linarith [hI, hII, hchain] - have hbell : P.toExtended.objectiveE v (a', z₂) (b + Δ) - = ((P.toExtended.bellmanFn v (a', z₂) : ℝ) : EReal) := by - rw [P.objectiveE_eq_coe_of v ha' hshiftup (by rw [he1]; exact hc1), hIIeq, - ← P.objectiveE_eq_coe_of v ha' (P.policyOf_mem v _) hc2] - exact P.policyOf_optimal v _ - exact absurd (P.optimal_action_unique_of_concaveSlices hv ha' hshiftup (P.policyOf_mem v _) - hbell (P.policyOf_optimal v _)) (by rw [← hb'def]; linarith) - /-! ### The Bellman step, by the envelope -/ /-- **Huggett's Lemma 1, one step.** If `v` has income-antitone increments then so does @@ -267,7 +200,7 @@ theorem consumptionFn_le_of_income_le_monotone (hmono : P.MonotoneTransitions) { (P.incDiffAcrossStates_valueFunction hmono hderiv hanti hpc hslack) hinc hx hy hxy) ha ha (by simp only [resources]; linarith) -/-- **Açıkgöz Proposition 4 with persistent income.** Assets strictly decline at the lowest +/-- **Worst-state decline with persistent income.** Assets strictly decline at the lowest income state, at every asset level above the borrowing constraint, given `β(1+r) < 1` and monotone transitions. -/ theorem policy_lt_self_of_impatient_monotone diff --git a/LeanPool/projects/aiyagari-equilibrium.yaml b/LeanPool/projects/aiyagari-equilibrium.yaml index 49b967ecc..115627a7c 100644 --- a/LeanPool/projects/aiyagari-equilibrium.yaml +++ b/LeanPool/projects/aiyagari-equilibrium.yaml @@ -1,21 +1,18 @@ slug: aiyagari-equilibrium -title: Capped log Aiyagari equilibrium and dynamic programming -summary: >- - Proves uniqueness of the equilibrium interest rate for capped log-utility - households with Aiyagari's discount and firm parameters, no borrowing, - and monotone finite-state earnings transitions with positive one-step - access to a lowest-income state, subject to an explicit cap-slack - inequality at the larger rate. Instantiates the earnings - process with arbitrary finite Tauchen chains. Bisection brackets a given - equilibrium and converges along a stationary-distribution selection; - existence of that equilibrium is a hypothesis of the bisection result. - Preserves Bellman contraction theory, real and extended-real Berge - maximum theorems, stationary Markov distributions, consumption concavity, - Euler inequalities, and marginal-propensity bounds used in the proofs. +title: Normalized capped log-household clearing and dynamic programming +summary: 'Proves conditional uniqueness of a rate solving the unit-wage normalized clearing + equation: mean household assets equal α / ((1 - α) * (r + δ)), with α = 9/25 and δ = 2/25. + Households have capped log utility, discount 24/25, no borrowing, and monotone finite + earnings transitions with positive one-step access to a lowest-income state; an explicit + cap-slack inequality is required. Finite Tauchen chains instantiate the household model. + Bisection converges to a given solution of this normalized equation along a stationary-distribution + selection. Equivalence to full competitive market clearing for arbitrary earnings scales + is not established. Preserves Bellman contraction, Berge maximum theorems, stationary + Markov theory, Euler inequalities, consumption concavity and marginal-propensity bounds.' branch: mathematical economics entry_module: LeanPool.AiyagariEquilibrium authors: - - Robert Kirkby +- Robert Kirkby source: url: https://github.com/LeanEconomics/LeanEconomics github_repo: LeanEconomics/LeanEconomics @@ -26,38 +23,33 @@ license: Apache-2.0 status: verified provenance: AI main_declarations: - - LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo +- LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo main_results: - - declaration: LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo - informal: >- - For log households with discount 24/25, capital share 9/25, - depreciation 2/25, no borrowing, and a finite Tauchen earnings chain - with nonnegative persistence below one, two stationary equilibria - with rates in (-2/25, 1/24) coincide, provided the explicit asset-cap - inequality holds at their larger rate. - - declaration: LeanEconomics.IncomeFluctuation.aiyagari1994_log_bisection - informal: >- - Given an equilibrium rate in a compact interval inside (-2/25, 1/24), - a stationary-distribution selection and the cap and monotonicity - hypotheses, bisection on excess supply brackets that rate at every - step, bounds the midpoint error by the initial width divided by - 2^(n+1), and has left endpoints converging to the rate. - - declaration: LeanEconomics.continuousAt_maxValue - informal: >- - A continuous objective maximized over a nonempty compact-valued - correspondence that is upper and lower hemicontinuous has a - continuous maximum value at the parameter. - - declaration: LeanEconomics.Blackwell.contractingWith - informal: >- - A monotone operator on bounded continuous real functions that - discounts constants by a nonnegative factor below one is a - contraction with that factor. +- declaration: LeanEconomics.aiyagariTauchen_equilibriumRate_unique_Ioo + informal: For capped log households with discount 24/25, no borrowing and a finite Tauchen + chain with persistence in [0,1), two rates in (-2/25, 1/24) whose stationary household + distributions have mean assets equal to 9 / (16 * (r + 2/25)) coincide, provided the + explicit asset-cap inequality holds at their larger rate. This is the prescribed unit-wage + normalized clearing equation; a bridge to full competitive equilibrium for arbitrary + earnings scales is not supplied. +- declaration: LeanEconomics.IncomeFluctuation.aiyagari1994_log_bisection + informal: Given a rate satisfying the same unit-wage normalized clearing equation in a + compact interval inside (-2/25, 1/24), a stationary-distribution selection and the stated + cap, monotonicity and reachability hypotheses, bisection on normalized excess supply + brackets that rate, bounds midpoint error by the initial width divided by 2^(n+1), and + has left endpoints converging to it. Existence of a solution to this equation is assumed. +- declaration: LeanEconomics.continuousAt_maxValue + informal: A continuous objective maximized over a nonempty compact-valued correspondence + that is upper and lower hemicontinuous has a continuous maximum value at the parameter. +- declaration: LeanEconomics.Blackwell.contractingWith + informal: A monotone operator on bounded continuous real functions that discounts constants + by a nonnegative factor below one is a contraction with that factor. tags: - - mathematical-economics - - dynamic-programming - - stationary-equilibrium - - markov-processes +- mathematical-economics +- dynamic-programming +- stationary-equilibrium +- markov-processes msc: - - 91B50 - - 90C39 - - 60J05 +- 91B50 +- 90C39 +- 60J05 From 3c6b0008bc12c27adcfc6e688c1e56f9436f40b1 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 13:01:28 +0000 Subject: [PATCH 08/19] Align endpoint documentation with normalized clearing scope --- .../Equilibrium/AiyagariUniqueness.lean | 51 ++++++++----------- .../Models/AiyagariTauchenWitness.lean | 12 ++--- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index f840ef551..199b9dfc5 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -62,7 +62,7 @@ variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) /-! ### Theorem 1 on the interval, by chaining -/ -/-- **Saving rises with the rate on `[0, rhi]`**, for log utility. Local comparisons from +/-- **Saving rises with the rate on `[rlo, rhi]`**, for log utility. Local comparisons from `crra_policy_mono_withRate_of_minMPC` chained by `monotoneOn_of_local`. For log the minimal MPC is `1 - β` at every rate, so every cap condition collapses to the one on `hcap`. -/ theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) @@ -238,9 +238,9 @@ theorem log_monotoneOn_capitalSupply {rlo rhi ε a₀ : ℝ} (hu : P.u = crraUti /-! ### Theorem 3: single crossing -/ -/-- **The equilibrium rate is unique on `[0, rhi]`**, for a log economy against the normalised -Cobb--Douglas firm. Two equilibria — a rate and a stationary distribution whose aggregate -capital is `k(r)/w(r)` — have the same rate. -/ +/-- **The normalized clearing rate is unique on `[rlo, rhi]`.** Two rates with stationary +household distributions whose mean assets equal the prescribed `normalisedDemand α δ r` +have the same rate, under the stated cap, transition and impatience hypotheses. -/ theorem log_equilibriumRate_unique {rlo rhi ε a₀ α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) (hδ : 0 < rlo + δ) (hu : P.u = crraUtility 1) (hunb : P.Unbounded) @@ -401,20 +401,17 @@ theorem log_no_equilibrium_of_ceiling {α δ r : ℝ} (hu : P.u = crraUtility 1) have := P.log_aggregateCapital_le hu hunb hβ hβ1 hrr hβR hthr hμ linarith -/-! ### Aiyagari (1994), `μ = 1` +/-! ### Normalized clearing with the Aiyagari (1994) log calibration -`β = 0.96`, `α = 0.36`, `δ = 0.08`, log utility, no borrowing, and a labour endowment process -with monotone transitions and a lowest-income state reached with positive probability from -every state in one step. The time-preference rate is -`λ = 1/0.96 - 1 = 1/24`, and the theorem covers every interval `[rlo, rhi]` with -`-δ < rlo ≤ rhi < 1/24` — so the whole of `(-δ, λ)`, the range on which Cobb--Douglas demand is -finite and the household impatient; his reported equilibrium rates at `μ = 1` lie between `3.3%` -and `4.17%`. -/ +The constants are `β = 0.96`, `α = 0.36` and `δ = 0.08`, with log utility, no borrowing, +and monotone earnings transitions reaching a lowest-income state with positive probability +in one step. The time-preference rate is `λ = 1/0.96 - 1 = 1/24`. The theorems concern the +prescribed normalized equation on intervals `-δ < rlo ≤ rhi < λ`; they do not identify it +with full competitive market clearing for arbitrary household earnings scales. -/ -/-- **Aiyagari (1994) at `μ = 1`: the equilibrium interest rate is unique** on `[rlo, rhi]` for -every `-δ < rlo ≤ rhi` below the time-preference rate `1/24`, for any economy with his -preferences and technology, monotone earnings transitions and positive one-step access to a -lowest-income state, provided the asset cap clears +/-- **The calibrated normalized clearing rate is unique on `[rlo, rhi]`.** The interval is +above `-δ` and below the time-preference rate `1/24`. Earnings transitions are monotone and +reach a lowest-income state in one step; the asset cap must exceed `(48/25) · maxIncome / (1 - (24/25)(1+rhi))`. -/ theorem aiyagari1994_log_equilibriumRate_unique {rlo rhi : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) @@ -432,11 +429,9 @@ theorem aiyagari1994_log_equilibriumRate_unique {rlo rhi : ℝ} (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) (by rw [hβ]; exact hcap) hmono hz₀ hmin hreach h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ -/-- **Aiyagari (1994) at `μ = 1`: two equilibrium rates in `(-δ, λ)` coincide.** The interval -version applied to `[min r₁ r₂, max r₁ r₂]`; the cap condition is the one at the larger rate. -Together with `aiyagari1994_log_no_equilibrium_of_ceiling` below `-δ + ε` and -`log_no_equilibrium_of_patient_risk` above `λ + ε`, this is uniqueness of the equilibrium rate -for his log calibration, up to two thin bands the cap forces. -/ +/-- **Two normalized clearing rates in `(-δ, λ)` coincide.** The interval version is applied +to `[min r₁ r₂, max r₁ r₂]`; the cap condition is required at the larger rate. The clearing +hypotheses equate stationary mean household assets to the prescribed normalized demand. -/ theorem aiyagari1994_log_equilibriumRate_unique_Ioo (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -453,9 +448,9 @@ theorem aiyagari1994_log_equilibriumRate_unique_Ioo ⟨min_le_left _ _, le_max_left _ _⟩ ⟨min_le_right _ _, le_max_right _ _⟩ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ -/-- **Aiyagari (1994) at `μ = 1`: no equilibrium at a negative rate where the ceiling is below -demand.** The condition is the ceiling–demand inequality written out with his numbers: it is -polynomial in `r` and `maxIncome`, so it can be checked for any earnings grid. -/ +/-- **No normalized clearing at a negative rate where the saving ceiling is below demand.** +The ceiling–demand inequality uses the calibrated constants and is polynomial in `r` and +`maxIncome`, so it can be checked for any earnings grid. -/ theorem aiyagari1994_log_no_equilibrium_of_ceiling {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) (hcap : 24 * P.maxIncome < assetCap) (hlo : -2 / 25 < r) (hr0 : r ≤ 0) (hrr : P.RateOK r) @@ -476,11 +471,9 @@ theorem aiyagari1994_log_no_equilibrium_of_ceiling {r : ℝ} rw [div_lt_div_iff₀ h1 h2] nlinarith [hD] -/-- **Aiyagari (1994) at `μ = 1`: no equilibrium on `(-δ, -1/20]`** whenever the highest -endowment is at most `3/2` (in wage units, as his mean-one normalisation has it). The bound is -not sharp — the ceiling–demand crossing sits near `-4.7%` — and the band `(-4.7%, 0)` stays -open: there the minimal-MPC ceiling `β · maxIncome/(1 - β(1+r))` exceeds Cobb--Douglas demand, -and excluding it needs a bound on capital supply that the household side does not yet give. -/ +/-- **No normalized clearing on `(-δ, -1/20]`** when household income is at most `3/2` and +the stated asset-cap bound holds. The income bound is an explicit hypothesis in household +units; it is not inferred from a mean-one endowment normalization. -/ theorem aiyagari1994_log_no_equilibrium_below {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : (P.discount : ℝ) = 24 / 25) (hcap : 24 * P.maxIncome < assetCap) (hmax : P.maxIncome ≤ 3 / 2) diff --git a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean index bfc39920f..1147a421d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean +++ b/LeanPool/AiyagariEquilibrium/Models/AiyagariTauchenWitness.lean @@ -32,9 +32,9 @@ open Set MeasureTheory namespace LeanEconomics -/-- **Aiyagari (1994)'s economy with a Tauchen earnings chain**: `β = 0.96`, log utility, no -borrowing, `n + 2` grid points on `[-3σ, 3σ]` for the AR(1) with persistence `ρ` and -unconditional standard deviation `σ`, endowment scaled by `c`. -/ +/-- A capped log household with a Tauchen earnings chain: `β = 0.96`, no borrowing, +`n + 2` grid points on `[-3σ, 3σ]`, persistence `ρ`, unconditional standard deviation `σ` +and arbitrary positive earnings scale `c`. -/ noncomputable def aiyagariTauchen (n : ℕ) (ρ σ c cap : ℝ) (hρ0 : 0 ≤ ρ) (hρ1 : ρ < 1) (hσ : 0 < σ) (hc : 0 < c) (hcap : 0 ≤ cap) : IncomeFluctuation (Fin (n + 2)) 0 cap where income i := c * Real.exp (-3 * σ + i * (6 * σ / (n + 1))) @@ -106,9 +106,9 @@ theorem aiyagariTauchen_monotone : monotoneTransitions_of_tauchen _ rfl aiyagariTauchen_income_strictMono hρ0 (mul_pos hσ (Real.sqrt_pos.mpr (by nlinarith))) (by positivity) -/-- **Aiyagari (1994) at `μ = 1` with his Tauchen earnings process: two equilibrium rates in -`(-δ, λ)` coincide**, for any number of states, any `ρ ∈ [0, 1)`, any `σ > 0` and any scale, -given the cap condition at the larger rate. -/ +/-- **Two normalized clearing rates in `(-δ, λ)` coincide** for the finite Tauchen household, +for any number of states, `ρ ∈ [0, 1)`, `σ > 0` and positive earnings scale, given the cap +condition at the larger rate. The hypotheses use the explicit prescribed normalized demand. -/ theorem aiyagariTauchen_equilibriumRate_unique_Ioo {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) (h₂ : r₂ ∈ Ioo (-2 / 25 : ℝ) (1 / 24)) (hcapc : 2 * (24 / 25) * (c * Real.exp (3 * σ)) < (1 - 24 / 25 * (1 + max r₁ r₂)) * cap) From 3e7616b8d96961872d625786c588309519e439d8 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 15:05:38 +0000 Subject: [PATCH 09/19] Share household deviation estimates and correct source citations --- .../Analysis/DecreasingIncrements.lean | 7 +- .../AiyagariEquilibrium/Analysis/HARA.lean | 5 + .../Analysis/RelativeRiskAversion.lean | 7 +- .../Models/BoundedIncomeFluctuation.lean | 4 +- .../Models/CRRAConstants.lean | 158 ++-------- .../Models/ConsumptionFloor.lean | 270 +++++++++++++----- .../Models/IncomeFluctuationConsumption.lean | 7 +- .../Models/IncomeFluctuationHARA.lean | 109 +------ .../Models/IncomeFluctuationIterate.lean | 257 +---------------- .../Models/IncomeFluctuationMonotone.lean | 85 ++++++ .../Models/IncomeFluctuationRateMonotone.lean | 7 +- .../Models/LogAiyagari.lean | 2 +- .../Models/MinimalMPC.lean | 156 ++++------ .../Models/MonotoneTransitions.lean | 13 +- 14 files changed, 430 insertions(+), 657 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean index 0fa6d665e..e25de2159 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean @@ -10,6 +10,9 @@ public import Mathlib.Analysis.Convex.SpecificFunctions.Basic /-! # Decreasing increments of a concave function +Numbered Light references use the October 31, 2018 author draft, printed page 13: +https://www.researchgate.net/publication/314238962 + A concave function's increment over an interval of fixed length shrinks as the interval slides right. In economics this is the increasing-differences (supermodularity) condition that monotone-comparative-statics arguments run on, usually stated as a cross partial @@ -116,7 +119,7 @@ theorem ConcaveOn.abs_sub_le_slopeBound_mul {u : ℝ → ℝ} (hu : ConcaveOn /-! ### Scaling a concave function -For a concave, strictly positive `z`, the ratio `α / z (α x)` rises with `α`. This is Lemma 3 +For a concave, strictly positive `z`, the ratio `α / z (α x)` rises with `α`. This is Lemma 4 of Light (2018), one of the three ingredients of his Theorem 1 (savings rise with the interest rate when relative risk aversion is at most one). @@ -125,7 +128,7 @@ and `α₂ x` with weight `α₁ / α₂`, so concavity gives the inequality dir positivity at the origin is what turns it from weak into the stated form. -/ -/-- **Light (2018), Lemma 3**, on any convex set containing the origin. In the application `z` +/-- **Light (2018), Lemma 4**, on any convex set containing the origin. In the application `z` is the consumption function, which is concave on the asset region and nowhere else, so the original statement over `[0, ∞)` would not reach it. Positivity is needed only at the origin and at the far point: concavity carries it to everything in between. -/ diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean index d6766688e..88ebd297e 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -66,6 +66,11 @@ namespace LeanEconomics /-- Shifted CRRA: the `b ≠ 0` branch of HARA, with subsistence level `a`. -/ noncomputable def haraUtility (γ a c : ℝ) : ℝ := crraUtility γ (a + c) +/-- A zero shift recovers ordinary CRRA utility. -/ +theorem haraUtility_zero (γ : ℝ) : haraUtility γ 0 = crraUtility γ := by + funext c + simp only [haraUtility, zero_add] + @[simp] theorem haraUtility_zero_shift (γ : ℝ) : haraUtility γ 0 = crraUtility γ := by funext c; simp [haraUtility] diff --git a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean index d4cd2a3c9..8126776c8 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean @@ -12,6 +12,9 @@ public import Mathlib.Analysis.SpecialFunctions.Pow.Deriv /-! # Relative risk aversion at most one +Numbered Light references use the October 31, 2018 author draft, printed page 13: +https://www.researchgate.net/publication/314238962 + Light (2018) proves that individual saving rises with the interest rate — his Theorem 1, the household half of the uniqueness argument — under the assumption that `u'(c) · c` is non-decreasing. With `u' > 0` that is exactly relative risk aversion `-c u''/u' ≤ 1`, because @@ -202,12 +205,12 @@ Light's Theorem 1 compares a household facing gross return `α₁` with one faci its pivotal inequality is that the marginal value of wealth, `α · u'(c)`, is larger at the higher return. Written out, `α u'(c) = (α / c) · (u'(c) · c)`, and the two factors are exactly the two conditions: `α / c(α x)` rises with `α` because the consumption function is CONCAVE -(Lemma 3, `ConcaveOn.div_le_div_of_scale`), and `u'(c) c` rises with `c` because relative risk +(Lemma 4, `ConcaveOn.div_le_div_of_scale`), and `u'(c) c` rises with `c` because relative risk aversion is at most one. Neither factor alone signs the product; splitting it this way is the whole trick. Note what the consumption function has to supply: concavity, positivity at zero assets, and -monotonicity. The first is Light's Lemma 4 and is the deep one — he cites Jensen (2017), and it +monotonicity. The first is Light's Lemma 5 and is the deep one — he cites Jensen (2017), and it is a Carroll–Kimball result that needs more than concavity of `u` and of the value function. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean index 3a028bbfd..ac92e7b7b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean +++ b/LeanPool/AiyagariEquilibrium/Models/BoundedIncomeFluctuation.lean @@ -220,8 +220,8 @@ theorem sqrtCES_optimal_action_unique {s : ℝ × Fin 2} (hs : s.1 ∈ Icc (0 : a₀ = a₁ := sqrtCES.optimal_action_unique hs h₀ h₁ hm₀ hm₁ -/-- **Light's Theorem 1 holds for it**: the policy is increasing in assets. This is a result -about `IncomeFluctuation`, applied with no porting at all. -/ +/-- **The policy is increasing in assets at a fixed interest rate.** This specializes the +asset-monotonicity theorem for `IncomeFluctuation`. -/ theorem sqrtCES_policy_mono {a a' : ℝ} {z : Fin 2} (ha : a ∈ Icc (0 : ℝ) 1) (ha' : a' ∈ Icc (0 : ℝ) 1) (hle : a ≤ a') : sqrtCES.policy (a, z) ≤ sqrtCES.policy (a', z) := diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 2701d946d..793b7222b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -249,30 +249,6 @@ DIFFERENCES of continuation values, so an oscillation bound avoids this normaliz noncomputable def oscGap : ℝ := (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) -theorem valueFunction_le_oscBound (s : ℝ × Z) : - P.toExtended.valueFunction s ≤ P.u P.maxConsumption / (1 - P.discount) := by - have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one - refine P.toExtended.valueFunction_le_const ?_ s - have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith - have key : P.u P.maxConsumption - + (P.discount : ℝ) * (P.u P.maxConsumption / (1 - P.discount)) - = P.u P.maxConsumption / (1 - P.discount) := by field_simp; ring - rw [show P.toExtended.rewardMax = P.u P.maxConsumption from rfl, - show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] - exact le_of_eq key - -theorem oscBound_le_valueFunction (s : ℝ × Z) : - P.u P.minConsumption / (1 - P.discount) ≤ P.toExtended.valueFunction s := by - have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one - refine P.toExtended.const_le_valueFunction ?_ s - have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith - have key : P.u P.minConsumption / (1 - P.discount) - = P.u P.minConsumption + (P.discount : ℝ) * (P.u P.minConsumption / (1 - P.discount)) := by - field_simp; ring - rw [show P.toExtended.rewardMin = P.u P.minConsumption from rfl, - show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] - exact le_of_eq key - theorem oscGap_nonneg : 0 ≤ P.oscGap := by have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) @@ -431,74 +407,15 @@ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : P.policy (a, z) ≤ (P.discount * P.oscGap / θ) * (P.consumption (a, z) (P.policy (a, z)) + (1 - θ) * P.policy (a, z)) ^ γ := by - have hosc := P.oscGap_nonneg - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policy (a, z) with hbdef - set c : ℝ := P.consumption (a, z) b with hcdef - have hcpos : 0 < c := by - rw [hcdef, hbdef]; exact P.consumption_policy_pos hpc ha - have hbase : (0 : ℝ) < c + (1 - θ) * b := by - have : 0 ≤ (1 - θ) * b := mul_nonneg (by linarith) (P.policy_mem_region (a, z)).1 - linarith - have hrpow : (0 : ℝ) < (c + (1 - θ) * b) ^ γ := Real.rpow_pos_of_pos hbase _ - rcases eq_or_lt_of_le (P.policy_mem_region (a, z)).1 with hzero | hbpos - · rw [← hbdef] at hzero - rw [← hzero] - positivity - rw [← hbdef] at hbpos - -- the deviation: save `θ` times as much - have hθb0 : 0 < θ * b := mul_pos hθ0 hbpos - have hθbb : θ * b < b := by nlinarith - have hbreg : b ∈ Icc (0 : ℝ) assetCap := P.policy_mem_region _ - have hmem0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ - have hfeas : θ * b ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨hθb0.le, by linarith [(P.policy_mem (a, z)).2]⟩ - have hcons : P.consumption (a, z) (θ * b) = c + (1 - θ) * b := by - simp only [hcdef, consumption]; ring - have hdev : 0 < P.consumption (a, z) (θ * b) := by rw [hcons]; linarith - have hopt := P.objR_le_of_mem ha hfeas (P.mem_dom_of_pos hdev) - simp only [objR, hcons] at hopt - rw [show P.consumption (a, z) (P.policy (a, z)) = c from rfl, - show P.policy (a, z) = b from rfl] at hopt - -- concavity of the continuation caps what the forgone saving could have repaid - have hslope := (P.concaveOn_cont z).slope_anti_adjacent hmem0 hbreg hθb0 hθbb - have hloss : P.cont z b - P.cont z (θ * b) ≤ (1 - θ) / θ * P.oscGap := by - have hgap := P.cont_sub_le_oscGap z (θ * b) 0 - rw [div_le_div_iff₀ (show (0:ℝ) < b - θ * b by nlinarith) - (show (0:ℝ) < θ * b - 0 by linarith)] at hslope - have h2 : (P.cont z b - P.cont z (θ * b)) * (θ * b) ≤ P.oscGap * ((1 - θ) * b) := by - nlinarith [hslope, hgap, hbpos, hθ0] - rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] - nlinarith [h2, hbpos] - -- the marginal gain from the extra consumption, priced where it lands - have hmg : (c + (1 - θ) * b) ^ (-γ) * ((1 - θ) * b) - ≤ P.u (c + (1 - θ) * b) - P.u c := by - rw [hu] - have := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hcpos.le) - (le_add_of_nonneg_right (mul_nonneg (by linarith) hbpos.le)) (le_refl (c + (1 - θ) * b)) - simpa using this - have hmid : P.u (c + (1 - θ) * b) - P.u c - ≤ (P.discount : ℝ) * (P.cont z b - P.cont z (θ * b)) := by - have hd : (P.discount : ℝ) * (P.cont z b - P.cont z (θ * b)) - = (P.discount : ℝ) * P.cont z b - (P.discount : ℝ) * P.cont z (θ * b) := by ring - linarith [hopt, hd.le, hd.ge] - have hkey : (c + (1 - θ) * b) ^ (-γ) * ((1 - θ) * b) - ≤ (P.discount : ℝ) * ((1 - θ) / θ * P.oscGap) := - le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) - -- divide out `1 - θ` and clear the negative power - have hinv : (c + (1 - θ) * b) ^ (-γ) = ((c + (1 - θ) * b) ^ γ)⁻¹ := - Real.rpow_neg hbase.le γ - rw [hinv] at hkey - have h1θ : (0 : ℝ) < 1 - θ := by linarith - have hexp : ((c + (1 - θ) * b) ^ γ)⁻¹ * ((1 - θ) * b) - = (1 - θ) * (b / (c + (1 - θ) * b) ^ γ) := by field_simp - rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * P.oscGap) - = (1 - θ) * (P.discount * P.oscGap / θ) from by ring] at hkey - have hdiv : b / (c + (1 - θ) * b) ^ γ ≤ P.discount * P.oscGap / θ := - le_of_mul_le_mul_left hkey h1θ - rw [div_le_iff₀ hrpow] at hdiv - linarith + have h := P.policyOf_le_mul_of_power_marginal (γ := γ) (η := 0) hθ0 hθ1 + P.concaveOn_valueFunction P.oscGap_nonneg + (by simpa only [oscSpread, oscGap] using P.oscOn_valueFunction) + (fun c d hc hcd _ => by + simpa only [zero_add, hu] using + crra_marginal_bound_Ici hγ0 hγ1 (P.nonneg_of_mem_dom hc) hcd (le_refl d)) ha z + (by simpa only [zero_add, P.consumptionFnOf_valueFunction] using + P.consumptionFn_pos hpc ha z) + simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using h /-- **Assets fall above an explicit threshold**, in the income state that matters. The threshold is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`; with `θ` near one the second @@ -556,16 +473,10 @@ the cap always eventually works. A cruder estimate that keeps `maxConsumption ^ grows on both sides and closes the window for `γ` near one, which is an artefact of the estimate. -This is Marcet, Obiols-Homs and Weil (2007), Remark R4: "the upper bound on capital that was -introduced to obtain existence and uniqueness of the value function is, in fact, not binding". -Their route is different -- with endogenous labour supply, savings have an absorbing point `k_bar` -below the bound, so the bound is slack for any initial condition below it -- but the purpose is -identical, and it is reassuring that the standard treatment regards the cap as an artefact to be -discharged rather than a modelling choice to be defended. - -The converse is `policy_assetCap_of_patient` (their Proposition 3, in `Impatience`): without -impatience the cap BINDS. So the cap is an artefact precisely when `β (1 + r) < 1`, and the -hypothesis this file carries everywhere is exactly the one that makes it one. -/ +Related literature: Marcet, Obiols-Homs and Weil (2007), *Incomplete Markets, Labor Supply +and Capital Accumulation*. The sufficient criterion below is specific to this capped household +model: it uses the displayed quantitative inequality, bounded utility and positive optimal +consumption. Its fixed-point and iterative versions share the same comparison argument. -/ /-- **Saving never reaches the asset cap.** -/ theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) @@ -575,39 +486,16 @@ theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) < assetCap := by - by_contra hcon - rw [not_lt] at hcon - have hcap := P.assetCap_nonneg - have hosc := P.oscGap_nonneg - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policy (a, z) with hbdef - set c : ℝ := P.consumption (a, z) b with hcdef - have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z - rw [← hbdef, ← hcdef] at hkey - have hc0 : 0 < c := by rw [hcdef, hbdef]; exact P.consumption_policy_pos hpc ha - -- if saving reached the cap, what is left to consume is small - have hres : P.resources (a, z) ≤ P.maxConsumption := by - have := P.consumption_le_maxConsumption (s := (a, z)) ha (le_refl (0 : ℝ)) - simpa only [IncomeFluctuation.consumption, sub_zero] using this - have hsum : c + (1 - θ) * b = P.resources (a, z) - θ * b := by - simp only [hcdef, IncomeFluctuation.consumption]; ring - have hle : c + (1 - θ) * b - ≤ P.maxIncome + (1 + P.interest - θ) * assetCap := by - rw [hsum] - have hθb : θ * assetCap ≤ θ * b := mul_le_mul_of_nonneg_left hcon hθ0.le - simp only [IncomeFluctuation.maxConsumption] at hres - nlinarith [hres, hθb] - have hnn : (0 : ℝ) ≤ c + (1 - θ) * b := by - have : 0 ≤ (1 - θ) * b := mul_nonneg (by linarith) (P.policy_mem_region (a, z)).1 - linarith - have hmono : (c + (1 - θ) * b) ^ γ - ≤ (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ := - Real.rpow_le_rpow hnn hle hγ0.le - have hscale : (P.discount * P.oscGap / θ) * (c + (1 - θ) * b) ^ γ - ≤ (P.discount * P.oscGap / θ) - * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ := - mul_le_mul_of_nonneg_left hmono (by positivity) - linarith + have h := P.policyOf_lt_cap_of_power_bound (η := 0) hγ0.le hθ0.le hθ1.le + (show 0 ≤ (P.discount : ℝ) * P.oscGap / θ by + exact div_nonneg (mul_nonneg P.discount.coe_nonneg P.oscGap_nonneg) hθ0.le) + (v := P.toExtended.valueFunction) ha z + (by simpa only [zero_add, P.consumptionFnOf_valueFunction] using + (P.consumptionFn_pos hpc ha z).le) + (by simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using + P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z) + (by simpa only [zero_add, mul_zero, sub_zero] using hlt) + simpa only [P.policyOf_valueFunction] using h /-- **The disproof of Carroll and Kimball is disarmed.** Where saving never reaches the cap, the hypothesis of `not_concaveOn_consumptionFn_of_cap_binds` -- a flat stretch of the policy at the diff --git a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean index 47f1185f7..68f376c37 100644 --- a/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean +++ b/LeanPool/AiyagariEquilibrium/Models/ConsumptionFloor.lean @@ -119,73 +119,59 @@ variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopo variable {assetFloor assetCap : ℝ} variable (P : IncomeFluctuation Z assetFloor assetCap) -/-- The state-independent bound on the continuation's slope, read against `v`. -/ -noncomputable def contSlopeConstOf (v : (ℝ × Z) →ᵇ ℝ) : ℝ := 4 * ‖v‖ / P.minConsumption - -theorem contSlopeConstOf_nonneg (v : (ℝ × Z) →ᵇ ℝ) : 0 ≤ P.contSlopeConstOf v := - div_nonneg (by positivity) P.minConsumption_pos.le - -/-- **The continuation's slope is bounded by a state-independent constant**, above -`assetFloor + minConsumption / 2`, whatever the continuation. -/ -theorem contOf_sub_le {v : (ℝ × Z) →ᵇ ℝ} - (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) (z : Z) - {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) +/-- The slope constant an oscillation bound `G` gives. -/ +noncomputable def oscSlopeConst (G : ℝ) : ℝ := 2 * G / P.minConsumption + +theorem oscSlopeConst_nonneg {G : ℝ} (hG : 0 ≤ G) : 0 ≤ P.oscSlopeConst G := + div_nonneg (by linarith) P.minConsumption_pos.le + +/-- **The continuation's slope, priced by its oscillation**, above +`assetFloor + minConsumption / 2`. -/ +theorem contOf_sub_le_of_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (hG : 0 ≤ G) + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) + (hosc : P.OscOn v G) (z : Z) {x y : ℝ} + (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) (hy : y ∈ Icc assetFloor assetCap) : - P.contOf v z y - P.contOf v z x ≤ P.contSlopeConstOf v * (y - x) := by + P.contOf v z y - P.contOf v z x ≤ P.oscSlopeConst G * (y - x) := by have hmin := P.minConsumption_pos have hx0 : assetFloor < x := by linarith + have hxmem : x ∈ Icc assetFloor assetCap := ⟨hx0.le, by linarith [hy.2]⟩ have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy - have hb1 := abs_le.mp (P.abs_contOf_le v z x) - have hb2 := abs_le.mp (P.abs_contOf_le v z assetFloor) - have hhalf : P.contSlopeConstOf v * (P.minConsumption / 2) = 2 * ‖v‖ := by - rw [contSlopeConstOf]; field_simp; ring + have hgap := P.contOf_sub_le_osc hosc hxmem + (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z + have hhalf : P.oscSlopeConst G * (P.minConsumption / 2) = G := by + rw [oscSlopeConst]; field_simp have hbig : P.contOf v z x - P.contOf v z assetFloor - ≤ P.contSlopeConstOf v * (x - assetFloor) := by - have hstep : P.contSlopeConstOf v * (P.minConsumption / 2) - ≤ P.contSlopeConstOf v * (x - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) (P.contSlopeConstOf_nonneg v) + ≤ P.oscSlopeConst G * (x - assetFloor) := by + have hstep : P.oscSlopeConst G * (P.minConsumption / 2) + ≤ P.oscSlopeConst G * (x - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith) (P.oscSlopeConst_nonneg hG) rw [hhalf] at hstep - linarith [hb1.2, hb2.1] + linarith rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) - ≤ (P.contSlopeConstOf v * (x - assetFloor)) * (y - x) := by + ≤ (P.oscSlopeConst G * (x - assetFloor)) * (y - x) := by nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] nlinarith [hprod, hx0] -/-- The state-independent bound on the continuation's slope, which is where the positive income -floor does its work. -/ -noncomputable def contSlopeConst : ℝ := 4 * ‖P.toExtended.valueFunction‖ / P.minConsumption - -theorem contSlopeConst_nonneg : 0 ≤ P.contSlopeConst := - div_nonneg (by positivity) P.minConsumption_pos.le - -theorem abs_cont_le (z : Z) (x : ℝ) : |P.cont z x| ≤ ‖P.toExtended.valueFunction‖ := - P.abs_expectation_le x z - -/-- **The continuation's slope is bounded by a state-independent constant**, provided we stay -above `assetFloor + minConsumption / 2` in savings — which is exactly where the deviation -argument lives. -/ -theorem cont_sub_le (z : Z) {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) - (hy : y ∈ Icc assetFloor assetCap) : - P.cont z y - P.cont z x ≤ P.contSlopeConst * (y - x) := by - simpa only [cont, contOf, contSlopeConst, contSlopeConstOf] using - P.contOf_sub_le P.concaveOn_valueFunction z hx hxy hy - -/-- **A consumption floor from the margin, against an arbitrary continuation.** -/ -theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) - {v : (ℝ × Z) →ᵇ ℝ} (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) : +/-- **The consumption floor, from a finite marginal bound.** -/ +theorem exists_consumptionFnOf_floor_of_marginal {M G : ℝ} + (hM : MarginalBoundOn P.dom P.u M) (hG : 0 ≤ G) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) + (hosc : P.OscOn v G) + (hlt : (P.discount : ℝ) * P.oscSlopeConst G < M) : ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by have hmin := P.minConsumption_pos have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - obtain ⟨δ₀, hδ₀, hδ⟩ := hu ((P.discount : ℝ) * P.contSlopeConstOf v + 1) + obtain ⟨δ₀, hδ₀, hδ⟩ := hM refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ - by_contra hlt - rw [not_le] at hlt + by_contra hlt' + rw [not_le] at hlt' set d : ℝ := min δ₀ (P.minConsumption / 4) with hd set b : ℝ := P.policyOf v (a, z) with hbdef set c : ℝ := P.consumption (a, z) b with hcdef - have hltc : c < d := hlt + have hltc : c < d := hlt' have hdδ : d ≤ δ₀ := min_le_left _ _ have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha @@ -194,33 +180,76 @@ theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.do P.minConsumption_le_consumption_floor (a, z) have hcb : c = P.resources (a, z) - b := rfl have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith - set h : ℝ := d - c with hhdef - have hh0 : 0 < h := by rw [hhdef]; linarith - have hhle : h ≤ P.minConsumption / 4 := by rw [hhdef]; linarith - have hlow : assetFloor + P.minConsumption / 2 ≤ b - h := by linarith + set hgap : ℝ := d - c with hhdef + have hh0 : 0 < hgap := by rw [hhdef]; linarith + have hhle : hgap ≤ P.minConsumption / 4 := by rw [hhdef]; linarith + have hlow : assetFloor + P.minConsumption / 2 ≤ b - hgap := by linarith have hbmem : b ∈ Icc assetFloor assetCap := P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hfeas : b - h ∈ P.toExtended.feasible (a, z) := by + have hfeas : b - hgap ∈ P.toExtended.feasible (a, z) := by rw [P.feasible_eq] exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcd : P.consumption (a, z) (b - h) = d := by + have hcd : P.consumption (a, z) (b - hgap) = d := by simp only [consumption] at hcb ⊢; linarith - have hdpos : 0 < P.consumption (a, z) (b - h) := by rw [hcd]; linarith + have hdpos : 0 < P.consumption (a, z) (b - hgap) := by rw [hcd]; linarith have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) simp only [objROf, hcd] at hopt - have hslope := P.contOf_sub_le hv z hlow (by linarith) hbmem - rw [show b - (b - h) = h from by ring] at hslope + have hslope := P.contOf_sub_le_of_osc hG hv hosc z hlow (by linarith) hbmem + rw [show b - (b - hgap) = hgap from by ring] at hslope have hscaled := mul_le_mul_of_nonneg_left hslope hβ - have hutil : P.u d - P.u c - ≤ ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by - have e : (P.discount : ℝ) * (P.contSlopeConstOf v * h) - = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) := by rw [hhdef]; ring + have hutil : P.u d - P.u c ≤ ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by + have e : (P.discount : ℝ) * (P.oscSlopeConst G * hgap) + = ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by rw [hhdef]; ring linarith [hopt, hscaled, e.le, e.ge] have hmarg := hδ c d hcmem (by linarith) hdδ - rw [show ((P.discount : ℝ) * P.contSlopeConstOf v + 1) * (d - c) - = ((P.discount : ℝ) * P.contSlopeConstOf v) * (d - c) + (d - c) from by ring] at hmarg rw [hhdef] at hh0 - linarith [hmarg, hutil, hh0] + nlinarith only [hmarg, hutil, hh0, hlt] + +/-- The state-independent bound on the continuation's slope, read against `v`. -/ +noncomputable def contSlopeConstOf (v : (ℝ × Z) →ᵇ ℝ) : ℝ := 4 * ‖v‖ / P.minConsumption + +theorem contSlopeConstOf_nonneg (v : (ℝ × Z) →ᵇ ℝ) : 0 ≤ P.contSlopeConstOf v := + div_nonneg (by positivity) P.minConsumption_pos.le + +/-- **The continuation's slope is bounded by a state-independent constant**, above +`assetFloor + minConsumption / 2`, whatever the continuation. -/ +theorem contOf_sub_le {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) (z : Z) + {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.contOf v z y - P.contOf v z x ≤ P.contSlopeConstOf v * (y - x) := by + have h := P.contOf_sub_le_of_osc (by positivity) hv (P.oscOn_two_norm v) z hx hxy hy + convert h using 1 + dsimp [oscSlopeConst, contSlopeConstOf] + ring + +/-- The state-independent bound on the continuation's slope, which is where the positive income +floor does its work. -/ +noncomputable def contSlopeConst : ℝ := 4 * ‖P.toExtended.valueFunction‖ / P.minConsumption + +theorem contSlopeConst_nonneg : 0 ≤ P.contSlopeConst := + div_nonneg (by positivity) P.minConsumption_pos.le + +theorem abs_cont_le (z : Z) (x : ℝ) : |P.cont z x| ≤ ‖P.toExtended.valueFunction‖ := + P.abs_expectation_le x z + +/-- **The continuation's slope is bounded by a state-independent constant**, provided we stay +above `assetFloor + minConsumption / 2` in savings — which is exactly where the deviation +argument lives. -/ +theorem cont_sub_le (z : Z) {x y : ℝ} (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) + (hy : y ∈ Icc assetFloor assetCap) : + P.cont z y - P.cont z x ≤ P.contSlopeConst * (y - x) := by + simpa only [cont, contOf, contSlopeConst, contSlopeConstOf] using + P.contOf_sub_le P.concaveOn_valueFunction z hx hxy hy + +/-- **A consumption floor from the margin, against an arbitrary continuation.** -/ +theorem exists_consumptionFnOf_floor_of_marginalInada (hu : MarginalInadaOn P.dom P.u) + {v : (ℝ × Z) →ᵇ ℝ} (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) : + ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by + refine P.exists_consumptionFnOf_floor_of_marginal + (hu ((P.discount : ℝ) * P.oscSlopeConst (2 * ‖v‖) + 1)) + (by positivity) hv (P.oscOn_two_norm v) ?_ + linarith /-- **A consumption floor from the margin.** No use is made of `tendsto_atBot_u`: the argument runs on concavity and boundedness of the continuation, a positive income floor, and unbounded @@ -289,6 +318,119 @@ theorem positiveConsumption_of_marginalInada (hu : MarginalInadaOn P.dom P.u) : obtain ⟨δ, hδ, hspec⟩ := P.exists_consumption_floor_of_marginalInada hu exact fun s hs => lt_of_lt_of_le hδ (hspec s hs) +/-- A power marginal bound prices the same proportional saving deviation for every utility. -/ +theorem policyOf_le_mul_of_power_marginal {γ η θ G : ℝ} (hθ0 : 0 < θ) (hθ1 : θ < 1) + {v : (ℝ × Z) →ᵇ ℝ} + (hv : ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) fun a => v (a, z)) + (hG : 0 ≤ G) (hosc : P.OscOn v G) + (hmarg : ∀ c d : ℝ, c ∈ P.dom → c ≤ d → 0 < η + c → + (η + d) ^ (-γ) * (d - c) ≤ P.u d - P.u c) + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) + (hcpos : 0 < η + P.consumptionFnOf v z a) : + P.policyOf v (a, z) - assetFloor + ≤ ((P.discount : ℝ) * G / θ) + * (η + P.consumptionFnOf v z a + + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by + have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg + set b : ℝ := P.policyOf v (a, z) with hbdef + set c : ℝ := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hbase : (0 : ℝ) < η + c + (1 - θ) * (b - assetFloor) := by + have := mul_nonneg (sub_pos.mpr hθ1).le (sub_nonneg.mpr hbreg.1) + linarith + have hrpow : (0 : ℝ) < (η + c + (1 - θ) * (b - assetFloor)) ^ γ := + Real.rpow_pos_of_pos hbase _ + rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos + · rw [← hzero, sub_self] + positivity + have hbf : (0 : ℝ) < b - assetFloor := sub_pos.mpr hbpos + set w : ℝ := assetFloor + θ * (b - assetFloor) with hwdef + have hw0 : assetFloor < w := by rw [hwdef]; nlinarith only [hθ0, hbf] + have hwb : w < b := by rw [hwdef]; nlinarith only [hθ1, hbf] + have hwmem : w ∈ Icc assetFloor assetCap := ⟨hw0.le, hwb.le.trans hbreg.2⟩ + have hfeas : w ∈ P.toExtended.feasible (a, z) := by + rw [P.feasible_eq] + exact ⟨hw0.le, hwb.le.trans (P.policyOf_mem v (a, z)).2⟩ + have hcb : c = P.resources (a, z) - b := rfl + have hcons : P.consumption (a, z) w = c + (1 - θ) * (b - assetFloor) := by + simp only [consumption, hwdef]; rw [hcb]; ring + have hdev : 0 < P.consumption (a, z) w := by + rw [hcons] + have hc : 0 ≤ c := P.nonneg_of_mem_dom (P.consumption_policyOf_mem_dom v ha) + nlinarith only [hc, hθ1, hbf] + have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) + simp only [objROf, hcons] at hopt + rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, + show P.policyOf v (a, z) = b from rfl] at hopt + have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent + (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hbreg hw0 hwb + have hgap := P.contOf_sub_le_osc hosc hwmem + (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z + have hbw : b - w = (1 - θ) * (b - assetFloor) := by rw [hwdef]; ring + have hwf : w - assetFloor = θ * (b - assetFloor) := by rw [hwdef]; ring + have hloss : P.contOf v z b - P.contOf v z w ≤ (1 - θ) / θ * G := by + rw [div_le_div_iff₀ (sub_pos.mpr hwb) (sub_pos.mpr hw0), hbw, hwf] at hslope + have h2 := hslope.trans (mul_le_mul_of_nonneg_right hgap + (mul_nonneg (sub_pos.mpr hθ1).le hbf.le)) + rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] + refine le_of_mul_le_mul_right (a := b - assetFloor) ?_ hbf + convert h2 using 1 <;> ring + have hmg : (η + c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) + ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by + have := hmarg c (c + (1 - θ) * (b - assetFloor)) + (P.consumption_policyOf_mem_dom v ha) + (le_add_of_nonneg_right (mul_nonneg (sub_pos.mpr hθ1).le hbf.le)) hcpos + simpa only [add_sub_cancel_left, add_assoc] using this + have hmid : P.u (c + (1 - θ) * (b - assetFloor)) - P.u c + ≤ (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) := by + have hd : (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) + = (P.discount : ℝ) * P.contOf v z b - (P.discount : ℝ) * P.contOf v z w := by ring + linarith only [hopt, hd] + have hkey := le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) + rw [Real.rpow_neg hbase.le γ] at hkey + have hexp : ((η + c + (1 - θ) * (b - assetFloor)) ^ γ)⁻¹ * ((1 - θ) * (b - assetFloor)) + = (1 - θ) * ((b - assetFloor) / (η + c + (1 - θ) * (b - assetFloor)) ^ γ) := by + field_simp + rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * G) + = (1 - θ) * ((P.discount : ℝ) * G / θ) from by ring] at hkey + have hdiv := le_of_mul_le_mul_left hkey (sub_pos.mpr hθ1) + rw [div_le_iff₀ hrpow] at hdiv + exact hdiv + +/-- A proportional power bound keeps saving below the cap under one primitive inequality. -/ +theorem policyOf_lt_cap_of_power_bound {γ η θ K : ℝ} (hγ : 0 ≤ γ) (hθ0 : 0 ≤ θ) + (hθ1 : θ ≤ 1) (hK : 0 ≤ K) {v : (ℝ × Z) →ᵇ ℝ} + {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) + (hc : 0 ≤ η + P.consumptionFnOf v z a) + (hbound : P.policyOf v (a, z) - assetFloor ≤ K * + (η + P.consumptionFnOf v z a + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ) + (hcap : K * (η + P.maxIncome + (1 + P.interest - θ) * assetCap + - (1 - θ) * assetFloor) ^ γ < assetCap - assetFloor) : + P.policyOf v (a, z) < assetCap := by + by_contra hcon + rw [not_lt] at hcon + set b := P.policyOf v (a, z) with hbdef + set c := P.consumptionFnOf v z a with hcdef + have hbreg : b ∈ Icc assetFloor assetCap := + P.feasible_subset_region (P.policyOf_mem v (a, z)) + have hres : P.resources (a, z) - assetFloor ≤ P.maxConsumption := + P.consumption_le_maxConsumption (s := (a, z)) ha le_rfl + have hsum : η + c + (1 - θ) * (b - assetFloor) + = η + (P.resources (a, z) - assetFloor) - θ * (b - assetFloor) := by + simp only [hcdef, consumptionFnOf, consumption, ← hbdef]; ring + have hle : η + c + (1 - θ) * (b - assetFloor) + ≤ η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor := by + rw [hsum] + have hθb : θ * (assetCap - assetFloor) ≤ θ * (b - assetFloor) := + mul_le_mul_of_nonneg_left (by linarith only [hcon]) hθ0 + simp only [maxConsumption] at hres + nlinarith only [hres, hθb] + have hnn : 0 ≤ η + c + (1 - θ) * (b - assetFloor) := + add_nonneg hc (mul_nonneg (sub_nonneg.mpr hθ1) (sub_nonneg.mpr hbreg.1)) + have hmono := mul_le_mul_of_nonneg_left (Real.rpow_le_rpow hnn hle hγ) hK + exact (not_lt_of_ge hcon) (by linarith only [hbound, hmono, hcap]) + end IncomeFluctuation end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean index c05a26a1a..679f7c943 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean @@ -11,9 +11,12 @@ public import LeanPool.AiyagariEquilibrium.Analysis.RelativeRiskAversion /-! # The consumption function +Numbered Light references use the October 31, 2018 author draft, printed page 13: +https://www.researchgate.net/publication/314238962 + `consumptionFn z a` is what the household actually eats at assets `a`: cash on hand less the optimal saving. Light (2018) Theorem 1 needs three things from it — positivity, monotonicity, -and CONCAVITY in assets (his Lemma 4). The first two are proved here. The third is not, and +and CONCAVITY in assets (his Lemma 5). The first two are proved here. The third is not, and this file says exactly why. ## Consumption rises with assets @@ -44,7 +47,7 @@ about the FIXED POINT rather than about one application of the operator. That is the Carroll and Kimball (1996) theorem — the Bellman operator preserves concavity of the consumption function when utility is HARA — and it is the shape of the closed-class arguments already used here for monotonicity, concavity, Lipschitz bounds and increasing -differences. Light states it as his Lemma 4 and cites Jensen (2017) rather than proving it. +differences. Light states it as his Lemma 5 and cites Jensen (2017) rather than proving it. It is left as a hypothesis: `mul_marginal_le_of_scale` takes it as an argument, and everything else that theorem needs about the consumption function is discharged here. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index 5881159fe..6b30aff82 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -359,77 +359,10 @@ theorem hara_policyOf_le_mul {γ η θ G : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) ≤ ((P.discount : ℝ) * G / θ) * (η + P.consumptionFnOf v z a + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumptionFnOf v z a with hcdef - have hbreg : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hbase : (0 : ℝ) < η + c + (1 - θ) * (b - assetFloor) := by - have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) - (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) - linarith - have hrpow : (0 : ℝ) < (η + c + (1 - θ) * (b - assetFloor)) ^ γ := Real.rpow_pos_of_pos hbase _ - rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos - · rw [← hzero, sub_self] - positivity - have hbf : (0 : ℝ) < b - assetFloor := by linarith - set w : ℝ := assetFloor + θ * (b - assetFloor) with hwdef - have hw0 : assetFloor < w := by rw [hwdef]; nlinarith - have hwb : w < b := by rw [hwdef]; nlinarith - have hwmem : w ∈ Icc assetFloor assetCap := ⟨hw0.le, by linarith [hbreg.2]⟩ - have hfeas : w ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcb : c = P.resources (a, z) - b := rfl - have hcons : P.consumption (a, z) w = c + (1 - θ) * (b - assetFloor) := by - simp only [consumption, hwdef]; rw [hcb]; ring - have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith - have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) - simp only [objROf, hcons] at hopt - rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, - show P.policyOf v (a, z) = b from rfl] at hopt - have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent - (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hbreg hw0 hwb - have hgap := P.contOf_sub_le_osc hosc hwmem - (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z - have hbw : b - w = (1 - θ) * (b - assetFloor) := by rw [hwdef]; ring - have hwf : w - assetFloor = θ * (b - assetFloor) := by rw [hwdef]; ring - have hloss : P.contOf v z b - P.contOf v z w ≤ (1 - θ) / θ * G := by - rw [div_le_div_iff₀ (show (0 : ℝ) < b - w by linarith) - (show (0 : ℝ) < w - assetFloor by linarith), hbw, hwf] at hslope - have h2 : (P.contOf v z b - P.contOf v z w) * (θ * (b - assetFloor)) - ≤ G * ((1 - θ) * (b - assetFloor)) := by - nlinarith [hslope, hgap, mul_pos (show (0:ℝ) < 1 - θ by linarith) hbf] - rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] - nlinarith [h2, hbf] - -- the marginal gain, priced where it lands: the CRRA step read at `η + c` - have hmg : (η + c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) - ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by - rw [hu] - have hbound := haraUtility_marginal_bound_pos (γ := γ) (η := η) - (R := c + (1 - θ) * (b - assetFloor)) hγ0 (d := c) - (c := c + (1 - θ) * (b - assetFloor)) (by linarith) - (le_add_of_nonneg_right (mul_nonneg (by linarith) hbf.le)) le_rfl - have he : η + (c + (1 - θ) * (b - assetFloor)) = η + c + (1 - θ) * (b - assetFloor) := by ring - rw [he] at hbound - have he2 : c + (1 - θ) * (b - assetFloor) - c = (1 - θ) * (b - assetFloor) := by ring - rw [he2] at hbound - exact hbound - have hmid : P.u (c + (1 - θ) * (b - assetFloor)) - P.u c - ≤ (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) := by - have hd : (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) - = (P.discount : ℝ) * P.contOf v z b - (P.discount : ℝ) * P.contOf v z w := by ring - linarith [hopt, hd.le, hd.ge] - have hkey := le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) - rw [Real.rpow_neg hbase.le γ] at hkey - have h1θ : (0 : ℝ) < 1 - θ := by linarith - have hexp : ((η + c + (1 - θ) * (b - assetFloor)) ^ γ)⁻¹ * ((1 - θ) * (b - assetFloor)) - = (1 - θ) * ((b - assetFloor) / (η + c + (1 - θ) * (b - assetFloor)) ^ γ) := by field_simp - rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * G) - = (1 - θ) * ((P.discount : ℝ) * G / θ) from by ring] at hkey - have hdiv := le_of_mul_le_mul_left hkey h1θ - rw [div_le_iff₀ hrpow] at hdiv - linarith + refine P.policyOf_le_mul_of_power_marginal hθ0 hθ1 hv hG hosc ?_ ha z (by linarith) + intro c d _ hcd hc + rw [hu] + exact haraUtility_marginal_bound_pos hγ0 hc hcd le_rfl /-- **Saving never reaches the asset cap**, for shifted CRRA. -/ theorem hara_policyOf_lt_assetCap {γ η θ G : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) @@ -442,36 +375,10 @@ theorem hara_policyOf_lt_assetCap {γ η θ G : ℝ} (hγ0 : 0 < γ) (hη : 0 < assetCap - assetFloor) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : P.policyOf v (a, z) < assetCap := by - by_contra hcon - rw [not_lt] at hcon - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumptionFnOf v z a with hcdef - have hbreg : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hkey := P.hara_policyOf_le_mul hγ0 hη hθ0 hθ1 hu hv hG hosc ha z (hcpos z a ha) - rw [← hbdef, ← hcdef] at hkey - have hcpos' : 0 < c := hcpos z a ha - have hres : P.resources (a, z) - assetFloor ≤ P.maxConsumption := - P.consumption_le_maxConsumption (s := (a, z)) ha le_rfl - have hsum : c + (1 - θ) * (b - assetFloor) - = (P.resources (a, z) - assetFloor) - θ * (b - assetFloor) := by - simp only [hcdef, consumptionFnOf, consumption, ← hbdef]; ring - have hle : η + c + (1 - θ) * (b - assetFloor) - ≤ η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor := by - have hθb : θ * (assetCap - assetFloor) ≤ θ * (b - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) hθ0.le - simp only [maxConsumption] at hres - nlinarith [hres, hθb, hsum] - have hnn : (0 : ℝ) ≤ η + c + (1 - θ) * (b - assetFloor) := by - have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) - (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) - linarith [hcpos'] - have hmono : (η + c + (1 - θ) * (b - assetFloor)) ^ γ - ≤ (η + P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ := - Real.rpow_le_rpow hnn hle hγ0.le - have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * G / θ := by positivity - nlinarith [hkey, mul_le_mul_of_nonneg_left hmono hcoef, hlt] + exact P.policyOf_lt_cap_of_power_bound hγ0.le hθ0.le hθ1.le + (div_nonneg (mul_nonneg P.discount.coe_nonneg hG) hθ0.le) ha z + (by linarith [hcpos z a ha]) + (P.hara_policyOf_le_mul hγ0 hη hθ0 hθ1 hu hv hG hosc ha z (hcpos z a ha)) hlt /-! ### The assembly -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index dc82edb0d..afedd6028 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -33,8 +33,6 @@ namespace IncomeFluctuation variable {Z : Type*} [Fintype Z] [Nonempty Z] [TopologicalSpace Z] [DiscreteTopology Z] variable {assetFloor assetCap : ℝ} (P : IncomeFluctuation Z assetFloor assetCap) -/-! ### The objective against an arbitrary continuation -/ - /-! ### Positivity -/ /-- **Consumption never vanishes at the optimum, whatever the continuation.** With utility @@ -113,55 +111,10 @@ for CES — where utility is bounded and the level of `v` is large compared with it is far too crude. `oscGap` is the right one, and what it bounds is the OSCILLATION. This section carries that along the iteration. -/ -/-- `v` varies by at most `G` over the asset region. -/ -def OscOn (_P : IncomeFluctuation Z assetFloor assetCap) (v : (ℝ × Z) →ᵇ ℝ) (G : ℝ) : Prop := - ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, ∀ z z' : Z, - v (x, z) - v (y, z') ≤ G - -theorem OscOn.mono {v : (ℝ × Z) →ᵇ ℝ} {G G' : ℝ} (h : P.OscOn v G) (hG : G ≤ G') : - P.OscOn v G' := fun x hx y hy z z' => (h x hx y hy z z').trans hG - -/-- The spread the iteration can produce: the reward's spread, amplified by `(1 - β)⁻¹`. At a -zero borrowing limit this is definitionally `CRRAConstants.oscGap`, so the witnesses' bounds on -that transfer unchanged. -/ -noncomputable def oscSpread : ℝ := - (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) - /-- At a zero borrowing limit the spread is `CRRAConstants.oscGap`. -/ theorem oscSpread_eq_oscGap {assetCap : ℝ} (Q : IncomeFluctuation Z 0 assetCap) : Q.oscSpread = Q.oscGap := rfl -theorem oscSpread_nonneg : 0 ≤ P.oscSpread := by - have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one - refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) - exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) - (P.mem_dom_of_pos P.maxConsumption_pos) P.minConsumption_le_maxConsumption - -/-- The continuation inherits the bound, even across different income states. -/ -theorem contOf_sub_le_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (h : P.OscOn v G) {x y : ℝ} - (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (z z' : Z) : - P.contOf v z x - P.contOf v z' y ≤ G := by - classical - obtain ⟨z₁, -, hz₁⟩ := Finset.exists_max_image Finset.univ (fun w : Z => v (x, w)) - ⟨Classical.ofNonempty, Finset.mem_univ _⟩ - obtain ⟨z₂, -, hz₂⟩ := Finset.exists_min_image Finset.univ (fun w : Z => v (y, w)) - ⟨Classical.ofNonempty, Finset.mem_univ _⟩ - have h1 : P.contOf v z x ≤ v (x, z₁) := by - have hle : ∑ w : Z, P.transitionMatrix z w * v (x, w) - ≤ ∑ w : Z, P.transitionMatrix z w * v (x, z₁) := - Finset.sum_le_sum fun w _ => - mul_le_mul_of_nonneg_left (hz₁ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z w) - rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle - exact hle - have h2 : v (y, z₂) ≤ P.contOf v z' y := by - have hle : ∑ w : Z, P.transitionMatrix z' w * v (y, z₂) - ≤ ∑ w : Z, P.transitionMatrix z' w * v (y, w) := - Finset.sum_le_sum fun w _ => - mul_le_mul_of_nonneg_left (hz₂ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z' w) - rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle - exact hle - linarith [h x hx y hy z₁ z₂] - theorem oscOn_zero : P.OscOn (0 : (ℝ × Z) →ᵇ ℝ) 0 := fun _ _ _ _ _ _ => by simp /-- **The Bellman operator adds the reward's spread and contracts the continuation's.** -/ @@ -229,17 +182,6 @@ half as far above the borrowing limit — works against any continuation, with ` `‖V‖`. The iterates are uniformly bounded because the Bellman operator adds at most the reward and contracts what it inherits, so one calibration inequality covers the whole iteration. -/ -/-- **The fixed point oscillates by no more than the iterates do**, being their uniform limit. -/ -theorem oscOn_valueFunction : P.OscOn P.toExtended.valueFunction P.oscSpread := by - intro x hx y hy z z' - have hpt : ∀ p : ℝ × Z, Tendsto - (fun n => ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) p) atTop - (𝓝 (P.toExtended.valueFunction p)) := fun p => - (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp - (P.toExtended.tendsto_iterate_valueFunction 0)).tendsto_at p - exact le_of_tendsto_of_tendsto ((hpt (x, z)).sub (hpt (y, z'))) tendsto_const_nhds - (Eventually.of_forall fun n => P.oscOn_iterate n x hx y hy z z') - /-- The Bellman operator adds at most the reward and contracts what it inherits. -/ theorem norm_bellman_le (v : (ℝ × Z) →ᵇ ℝ) : ‖P.toExtended.bellman v‖ @@ -413,76 +355,11 @@ theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h ≤ ((P.discount : ℝ) * G / θ) * (P.consumptionFnOf v z a + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumptionFnOf v z a with hcdef - have hbreg : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hcpos : 0 < c := hpc v hv z a ha - have hbase : (0 : ℝ) < c + (1 - θ) * (b - assetFloor) := by - have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) - (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) - linarith - have hrpow : (0 : ℝ) < (c + (1 - θ) * (b - assetFloor)) ^ γ := Real.rpow_pos_of_pos hbase _ - rcases eq_or_lt_of_le hbreg.1 with hzero | hbpos - · rw [← hzero, sub_self] - positivity - have hbf : (0 : ℝ) < b - assetFloor := by linarith - set w : ℝ := assetFloor + θ * (b - assetFloor) with hwdef - have hw0 : assetFloor < w := by rw [hwdef]; nlinarith - have hwb : w < b := by rw [hwdef]; nlinarith - have hwmem : w ∈ Icc assetFloor assetCap := ⟨hw0.le, by linarith [hbreg.2]⟩ - have hfeas : w ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨hw0.le, by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcb : c = P.resources (a, z) - b := rfl - have hcons : P.consumption (a, z) w = c + (1 - θ) * (b - assetFloor) := by - simp only [consumption, hwdef]; rw [hcb]; ring - have hdev : 0 < P.consumption (a, z) w := by rw [hcons]; linarith - have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdev) - simp only [objROf, hcons] at hopt - rw [show P.consumption (a, z) (P.policyOf v (a, z)) = c from rfl, - show P.policyOf v (a, z) = b from rfl] at hopt - -- concavity of the continuation, priced against its oscillation - have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent - (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hbreg hw0 hwb - have hgap := P.contOf_sub_le_osc hosc hwmem - (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z - have hbw : b - w = (1 - θ) * (b - assetFloor) := by rw [hwdef]; ring - have hwf : w - assetFloor = θ * (b - assetFloor) := by rw [hwdef]; ring - have hloss : P.contOf v z b - P.contOf v z w ≤ (1 - θ) / θ * G := by - rw [div_le_div_iff₀ (show (0 : ℝ) < b - w by linarith) - (show (0 : ℝ) < w - assetFloor by linarith), hbw, hwf] at hslope - have h2 : (P.contOf v z b - P.contOf v z w) * (θ * (b - assetFloor)) - ≤ G * ((1 - θ) * (b - assetFloor)) := - hslope.trans (mul_le_mul_of_nonneg_right hgap - (mul_nonneg (sub_pos.mpr hθ1).le hbf.le)) - rw [div_mul_eq_mul_div, le_div_iff₀ hθ0] - refine le_of_mul_le_mul_right (a := b - assetFloor) ?_ hbf - convert h2 using 1 <;> ring - -- the marginal gain, priced where it lands - have hmg : (c + (1 - θ) * (b - assetFloor)) ^ (-γ) * ((1 - θ) * (b - assetFloor)) - ≤ P.u (c + (1 - θ) * (b - assetFloor)) - P.u c := by - rw [hu] - have hbound := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hcpos.le) - (le_add_of_nonneg_right (mul_nonneg (by linarith) hbf.le)) - (le_refl (c + (1 - θ) * (b - assetFloor))) - simpa using hbound - have hmid : P.u (c + (1 - θ) * (b - assetFloor)) - P.u c - ≤ (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) := by - have hd : (P.discount : ℝ) * (P.contOf v z b - P.contOf v z w) - = (P.discount : ℝ) * P.contOf v z b - (P.discount : ℝ) * P.contOf v z w := by ring - linarith [hopt, hd.le, hd.ge] - have hkey := le_trans hmg (le_trans hmid (mul_le_mul_of_nonneg_left hloss hβ)) - rw [Real.rpow_neg hbase.le γ] at hkey - have h1θ : (0 : ℝ) < 1 - θ := by linarith - have hexp : ((c + (1 - θ) * (b - assetFloor)) ^ γ)⁻¹ * ((1 - θ) * (b - assetFloor)) - = (1 - θ) * ((b - assetFloor) / (c + (1 - θ) * (b - assetFloor)) ^ γ) := by field_simp - rw [hexp, show (P.discount : ℝ) * ((1 - θ) / θ * G) - = (1 - θ) * ((P.discount : ℝ) * G / θ) from by ring] at hkey - have hdiv := le_of_mul_le_mul_left hkey h1θ - rw [div_le_iff₀ hrpow] at hdiv - linarith + simpa only [zero_add] using P.policyOf_le_mul_of_power_marginal (γ := γ) (η := 0) + hθ0 hθ1 hv hG hosc (fun c d hc hcd _ => by + simpa only [zero_add, hu] using + crra_marginal_bound_Ici hγ0 hγ1 (P.nonneg_of_mem_dom hc) hcd (le_refl d)) ha z + (by simpa only [zero_add] using hpc v hv z a ha) /-- **Saving never reaches the asset cap, against an arbitrary continuation.** The hypothesis is the one `crra_policy_lt_assetCap` carries at the fixed point, so the calibrations already proved @@ -496,37 +373,12 @@ theorem crra_policyOf_lt_assetCap {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1 < assetCap - assetFloor) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : P.policyOf v (a, z) < assetCap := by - by_contra hcon - rw [not_lt] at hcon - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumptionFnOf v z a with hcdef - have hbreg : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hkey := P.crra_policyOf_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc hv hG hosc ha z - rw [← hbdef, ← hcdef] at hkey - have hcpos : 0 < c := hpc v hv z a ha - have hres : P.resources (a, z) - assetFloor ≤ P.maxConsumption := - P.consumption_le_maxConsumption (s := (a, z)) ha le_rfl - have hsum : c + (1 - θ) * (b - assetFloor) - = (P.resources (a, z) - assetFloor) - θ * (b - assetFloor) := by - simp only [hcdef, consumptionFnOf, consumption, ← hbdef]; ring - have hle : c + (1 - θ) * (b - assetFloor) - ≤ P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor := by - rw [hsum] - have hθb : θ * (assetCap - assetFloor) ≤ θ * (b - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) hθ0.le - simp only [maxConsumption] at hres - nlinarith [hres, hθb] - have hnn : (0 : ℝ) ≤ c + (1 - θ) * (b - assetFloor) := by - have h := mul_nonneg (show (0 : ℝ) ≤ 1 - θ by linarith) - (show (0 : ℝ) ≤ b - assetFloor by linarith [hbreg.1]) - linarith - have hmono : (c + (1 - θ) * (b - assetFloor)) ^ γ - ≤ (P.maxIncome + (1 + P.interest - θ) * assetCap - (1 - θ) * assetFloor) ^ γ := - Real.rpow_le_rpow hnn hle hγ0.le - have hcoef : (0 : ℝ) ≤ (P.discount : ℝ) * G / θ := by positivity - nlinarith [hkey, mul_le_mul_of_nonneg_left hmono hcoef, hlt] + refine P.policyOf_lt_cap_of_power_bound (η := 0) hγ0.le hθ0.le hθ1.le + (show 0 ≤ (P.discount : ℝ) * G / θ from + div_nonneg (mul_nonneg P.discount.coe_nonneg hG) hθ0.le) ha z + (by simpa only [zero_add] using (hpc v hv z a ha).le) ?_ ?_ + · simpa only [zero_add] using P.crra_policyOf_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc hv hG hosc ha z + · simpa only [zero_add] using hlt /-- **Carroll and Kimball for the bounded CES family, at the witnesses' own calibration.** The cap hypothesis is exactly `crra_policy_lt_assetCap`'s, with `oscSpread` for `oscGap` — the same @@ -559,42 +411,8 @@ theorem concaveOn_consumptionFn_of_oscSpread {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 had to hand. For utilities whose marginal value at zero is FINITE — CARA, and shifted CRRA with a subsistence level — the consumption floor has to beat that slope, and the level is far too crude a bound to beat. The oscillation is the right measure and it is the one the iterates -control (`oscOn_iterate`), so the same geometry is redone with `G` in place of `2‖v‖`. -/ - -/-- The slope constant an oscillation bound `G` gives. -/ -noncomputable def oscSlopeConst (G : ℝ) : ℝ := 2 * G / P.minConsumption - -theorem oscSlopeConst_nonneg {G : ℝ} (hG : 0 ≤ G) : 0 ≤ P.oscSlopeConst G := - div_nonneg (by linarith) P.minConsumption_pos.le - -/-- **The continuation's slope, priced by its oscillation**, above -`assetFloor + minConsumption / 2`. -/ -theorem contOf_sub_le_of_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (hG : 0 ≤ G) - (hv : ConcaveSlices assetFloor assetCap v) (hosc : P.OscOn v G) (z : Z) {x y : ℝ} - (hx : assetFloor + P.minConsumption / 2 ≤ x) (hxy : x < y) - (hy : y ∈ Icc assetFloor assetCap) : - P.contOf v z y - P.contOf v z x ≤ P.oscSlopeConst G * (y - x) := by - have hmin := P.minConsumption_pos - have hx0 : assetFloor < x := by linarith - have hxmem : x ∈ Icc assetFloor assetCap := ⟨hx0.le, by linarith [hy.2]⟩ - have hslope := (P.concaveOn_contOf hv z).slope_anti_adjacent - (mem_Icc.mpr ⟨le_rfl, P.assetFloor_le_assetCap⟩) hy hx0 hxy - have hgap := P.contOf_sub_le_osc hosc hxmem - (show assetFloor ∈ Icc assetFloor assetCap from ⟨le_rfl, P.assetFloor_le_assetCap⟩) z z - have hhalf : P.oscSlopeConst G * (P.minConsumption / 2) = G := by - rw [oscSlopeConst]; field_simp - have hbig : P.contOf v z x - P.contOf v z assetFloor - ≤ P.oscSlopeConst G * (x - assetFloor) := by - have hstep : P.oscSlopeConst G * (P.minConsumption / 2) - ≤ P.oscSlopeConst G * (x - assetFloor) := - mul_le_mul_of_nonneg_left (by linarith) (P.oscSlopeConst_nonneg hG) - rw [hhalf] at hstep - linarith - rw [div_le_div_iff₀ (by linarith) (by linarith)] at hslope - have hprod : (P.contOf v z y - P.contOf v z x) * (x - assetFloor) - ≤ (P.oscSlopeConst G * (x - assetFloor)) * (y - x) := by - nlinarith [hslope, hbig, (show (0 : ℝ) < y - x by linarith)] - nlinarith [hprod, hx0] +control (`oscOn_iterate`). The shared slope and consumption-floor arguments in +`ConsumptionFloor` use this oscillation directly; norm bounds are specializations. -/ /-! ### A consumption floor from a FINITE marginal bound @@ -603,55 +421,6 @@ excludes every HARA member except pure CRRA. What the argument actually uses is marginal value of consumption near zero beats the discounted slope of the continuation, and for a finite marginal bound `M` that is an inequality to check rather than a triviality. -/ -/-- **The consumption floor, from a finite marginal bound.** -/ -theorem exists_consumptionFnOf_floor_of_marginal {M G : ℝ} - (hM : MarginalBoundOn P.dom P.u M) (hG : 0 ≤ G) - {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hosc : P.OscOn v G) - (hlt : (P.discount : ℝ) * P.oscSlopeConst G < M) : - ∃ δ > 0, ∀ (z : Z), ∀ a ∈ Icc assetFloor assetCap, δ ≤ P.consumptionFnOf v z a := by - have hmin := P.minConsumption_pos - have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg - obtain ⟨δ₀, hδ₀, hδ⟩ := hM - refine ⟨min δ₀ (P.minConsumption / 4), lt_min hδ₀ (by linarith), fun z a ha => ?_⟩ - by_contra hlt' - rw [not_le] at hlt' - set d : ℝ := min δ₀ (P.minConsumption / 4) with hd - set b : ℝ := P.policyOf v (a, z) with hbdef - set c : ℝ := P.consumption (a, z) b with hcdef - have hltc : c < d := hlt' - have hdδ : d ≤ δ₀ := min_le_left _ _ - have hd4 : d ≤ P.minConsumption / 4 := min_le_right _ _ - have hcmem : c ∈ P.dom := P.consumption_policyOf_mem_dom v ha - have hc0 : 0 ≤ c := P.nonneg_of_mem_dom hcmem - have hres : P.minConsumption ≤ P.resources (a, z) - assetFloor := - P.minConsumption_le_consumption_floor (a, z) - have hcb : c = P.resources (a, z) - b := rfl - have hbig : assetFloor + 3 * P.minConsumption / 4 ≤ b := by rw [hcb] at hltc; linarith - set hgap : ℝ := d - c with hhdef - have hh0 : 0 < hgap := by rw [hhdef]; linarith - have hhle : hgap ≤ P.minConsumption / 4 := by rw [hhdef]; linarith - have hlow : assetFloor + P.minConsumption / 2 ≤ b - hgap := by linarith - have hbmem : b ∈ Icc assetFloor assetCap := - P.feasible_subset_region (P.policyOf_mem v (a, z)) - have hfeas : b - hgap ∈ P.toExtended.feasible (a, z) := by - rw [P.feasible_eq] - exact ⟨by linarith [hbmem.1], by linarith [(P.policyOf_mem v (a, z)).2]⟩ - have hcd : P.consumption (a, z) (b - hgap) = d := by - simp only [consumption] at hcb ⊢; linarith - have hdpos : 0 < P.consumption (a, z) (b - hgap) := by rw [hcd]; linarith - have hopt := P.objROf_le_of_mem v ha hfeas (P.mem_dom_of_pos hdpos) - simp only [objROf, hcd] at hopt - have hslope := P.contOf_sub_le_of_osc hG hv hosc z hlow (by linarith) hbmem - rw [show b - (b - hgap) = hgap from by ring] at hslope - have hscaled := mul_le_mul_of_nonneg_left hslope hβ - have hutil : P.u d - P.u c ≤ ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by - have e : (P.discount : ℝ) * (P.oscSlopeConst G * hgap) - = ((P.discount : ℝ) * P.oscSlopeConst G) * (d - c) := by rw [hhdef]; ring - linarith [hopt, hscaled, e.le, e.ge] - have hmarg := hδ c d hcmem (by linarith) hdδ - rw [hhdef] at hh0 - nlinarith only [hmarg, hutil, hh0, hlt] - /-- **Positive consumption from a finite marginal bound**, against every continuation with concave slices and oscillation at most `G`. -/ theorem consumptionFnOf_pos_of_marginal {M G : ℝ} diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean index 502dd5605..9de1994e3 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -220,6 +220,91 @@ theorem policy_mono {a a' : ℝ} {z : Z} (ha : a ∈ Icc assetFloor assetCap) simpa only [P.policyOf_valueFunction] using P.policyOf_mono P.concaveOn_valueFunction ha ha' hle +/-- `v` varies by at most `G` over the asset region. -/ +def OscOn (_P : IncomeFluctuation Z assetFloor assetCap) (v : (ℝ × Z) →ᵇ ℝ) (G : ℝ) : Prop := + ∀ x ∈ Icc assetFloor assetCap, ∀ y ∈ Icc assetFloor assetCap, ∀ z z' : Z, + v (x, z) - v (y, z') ≤ G + +theorem OscOn.mono {v : (ℝ × Z) →ᵇ ℝ} {G G' : ℝ} (h : P.OscOn v G) (hG : G ≤ G') : + P.OscOn v G' := fun x hx y hy z z' => (h x hx y hy z z').trans hG + +/-- The continuation inherits the bound, even across different income states. -/ +theorem contOf_sub_le_osc {v : (ℝ × Z) →ᵇ ℝ} {G : ℝ} (h : P.OscOn v G) {x y : ℝ} + (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) (z z' : Z) : + P.contOf v z x - P.contOf v z' y ≤ G := by + classical + obtain ⟨z₁, -, hz₁⟩ := Finset.exists_max_image Finset.univ (fun w : Z => v (x, w)) + ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + obtain ⟨z₂, -, hz₂⟩ := Finset.exists_min_image Finset.univ (fun w : Z => v (y, w)) + ⟨Classical.ofNonempty, Finset.mem_univ _⟩ + have h1 : P.contOf v z x ≤ v (x, z₁) := by + have hle : ∑ w : Z, P.transitionMatrix z w * v (x, w) + ≤ ∑ w : Z, P.transitionMatrix z w * v (x, z₁) := + Finset.sum_le_sum fun w _ => + mul_le_mul_of_nonneg_left (hz₁ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z w) + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle + exact hle + have h2 : v (y, z₂) ≤ P.contOf v z' y := by + have hle : ∑ w : Z, P.transitionMatrix z' w * v (y, z₂) + ≤ ∑ w : Z, P.transitionMatrix z' w * v (y, w) := + Finset.sum_le_sum fun w _ => + mul_le_mul_of_nonneg_left (hz₂ w (Finset.mem_univ _)) (P.transitionMatrix_nonneg z' w) + rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at hle + exact hle + linarith [h x hx y hy z₁ z₂] + +/-- A bounded continuation oscillates by at most twice its norm. -/ +theorem oscOn_two_norm (v : (ℝ × Z) →ᵇ ℝ) : P.OscOn v (2 * ‖v‖) := by + intro x _ y _ z z' + have hx := abs_le.mp (show |v (x, z)| ≤ ‖v‖ by + simpa only [Real.norm_eq_abs] using v.norm_coe_le_norm (x, z)) + have hy := abs_le.mp (show |v (y, z')| ≤ ‖v‖ by + simpa only [Real.norm_eq_abs] using v.norm_coe_le_norm (y, z')) + linarith + +theorem valueFunction_le_oscBound (s : ℝ × Z) : + P.toExtended.valueFunction s ≤ P.u P.maxConsumption / (1 - P.discount) := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine P.toExtended.valueFunction_le_const ?_ s + have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith + have key : P.u P.maxConsumption + + (P.discount : ℝ) * (P.u P.maxConsumption / (1 - P.discount)) + = P.u P.maxConsumption / (1 - P.discount) := by field_simp; ring + rw [show P.toExtended.rewardMax = P.u P.maxConsumption from rfl, + show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] + exact le_of_eq key + +theorem oscBound_le_valueFunction (s : ℝ × Z) : + P.u P.minConsumption / (1 - P.discount) ≤ P.toExtended.valueFunction s := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine P.toExtended.const_le_valueFunction ?_ s + have hne : (1 : ℝ) - P.discount ≠ 0 := by linarith + have key : P.u P.minConsumption / (1 - P.discount) + = P.u P.minConsumption + (P.discount : ℝ) * (P.u P.minConsumption / (1 - P.discount)) := by + field_simp; ring + rw [show P.toExtended.rewardMin = P.u P.minConsumption from rfl, + show ((P.toExtended.discount : ℝ)) = (P.discount : ℝ) from rfl] + exact le_of_eq key + +/-- The spread the iteration can produce: the reward's spread, amplified by `(1 - β)⁻¹`. At a +zero borrowing limit this is definitionally `CRRAConstants.oscGap`, so the witnesses' bounds on +that transfer unchanged. -/ +noncomputable def oscSpread : ℝ := + (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) + +theorem oscSpread_nonneg : 0 ≤ P.oscSpread := by + have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one + refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) + exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) + (P.mem_dom_of_pos P.maxConsumption_pos) P.minConsumption_le_maxConsumption + +/-- The fixed-point value function satisfies the oscillation premise used by saving bounds. -/ +theorem oscOn_valueFunction : P.OscOn P.toExtended.valueFunction P.oscSpread := by + intro x _ y _ z z' + dsimp [oscSpread] + rw [sub_div] + exact sub_le_sub (P.valueFunction_le_oscBound (x, z)) (P.oscBound_le_valueFunction (y, z')) + end IncomeFluctuation end LeanEconomics diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean index 4ce938713..fde47517c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean @@ -11,6 +11,9 @@ public import LeanPool.AiyagariEquilibrium.Models.IncomeFluctuationRate /-! # Light (2018) Theorem 1: saving rises with the interest rate +Numbered Light references use the October 31, 2018 author draft, printed page 13: +https://www.researchgate.net/publication/314238962 + This is the last link in the uniqueness chain. `CapitalSupplyMonotone` turns a uniformly larger policy into larger aggregate capital (Theorem 2), and `Equilibrium.Uniqueness` turns monotone capital supply into a unique equilibrium rate (Theorem 3). What is still needed is that the @@ -51,8 +54,8 @@ envelope equality it needs is `hasDerivAt_bellman`, Clausen–Strub's lazy agent input is the Carroll–Kimball concavity of the consumption function. Theorem 1 with `hid` discharged is `policy_mono_withRate'`. -Worth recording precisely: his step 5 applies Lemma 3 to `σ_f(·, R₂)` and needs it CONCAVE. That -is Light's Lemma 4, which is the Carroll and Kimball theorem — the hypothesis `hT` of +Worth recording precisely: his step 5 applies Lemma 4 to `σ_f(·, R₂)` and needs it CONCAVE. That +is Light's Lemma 5, which is the Carroll and Kimball theorem — the hypothesis `hT` of `IncomeFluctuationCarrollKimball`. So the residual hypothesis here and the residual hypothesis there are not two gaps but one: concavity of the consumption function, which by Toda (2021) is unavailable outside HARA. Closing it closes both. diff --git a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean index f06b36a36..5b89d1d1b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean @@ -105,7 +105,7 @@ theorem crra_exists_exhaust_of_euler_corner {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u (fun a ha z => P.consumptionFn_le_of_income_le hiid (hz₀ z) ha) ha₀ hle hcorner /-- **The exhaustion data with persistent income**: monotone transitions in place of iid, via -Huggett's Lemma 1 (`consumptionFn_le_of_income_le_monotone`). The extra iterate-level +the income-monotonicity lemma (`consumptionFn_le_of_income_le_monotone`). The extra iterate-level hypotheses that lemma needs are supplied by the minimal MPC. -/ theorem crra_exists_exhaust_of_euler_corner_monotone {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index 5e638645a..fca72a1cb 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -12,9 +12,12 @@ public import LeanPool.AiyagariEquilibrium.Analysis.HARA /-! # The minimal marginal propensity to consume -Carroll and Shanker (2026), Lemma 1, bound the consumption SHARE of cash on hand between two -constants — the maximal and minimal marginal propensities to consume. This file proves the -minimal one, +Carroll and Shanker's *Theoretical Foundations of Buffer Stock Saving*, draft dated +2025-12-05, Lemma 1 (Limiting MPCs), gives consumption-share bounds in their buffer-stock model: +https://econ-ark.github.io/BufferStockTheory/BufferStockTheory.pdf + +Here the minimal bound is proved for the finite capped-household model, under explicit +positivity and cap-slack hypotheses, `κ · m ≤ c(m)`, `κ = 1 - Þ/R`, `Þ = (βR)^(1/γ)`, @@ -138,21 +141,19 @@ theorem le_of_rpow_neg_le {x y γ : ℝ} (hx : 0 < x) (hy : 0 < y) (hγ : 0 < γ /-! ### The bound along the iteration -/ -/-- **One Euler step of the minimal-MPC bound.** If a continuation `v` satisfies -`κ m ≤ c_v(m)` everywhere, with consumption positive and the cap slack against it, then so does -`bellman v`. Extracted from the induction so that it can also drive a SIMULTANEOUS induction on -cap slack (`crra_minMPC_of_cap`), which is what large `β` needs. -/ -theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) +/-- One shifted-CRRA Euler step propagates the minimal consumption bound. -/ +theorem hara_minMPC_step {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) {v : (ℝ × Z) →ᵇ ℝ} + {v : (ℝ × Z) →ᵇ ℝ} (hposv : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf v z a) (hcapv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf v (a, z) < assetCap) (hposW : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) (ih : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, - P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf v z a) + P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFnOf v z a) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : - P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf (P.toExtended.bellman v) z a := by + P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFnOf (P.toExtended.bellman v) z a := by subst hfl have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR @@ -171,18 +172,22 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) have hcA : c = P.resources (a, z) - A := rfl have hc0 : 0 < c := hposW z a ha rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 - · nlinarith only [hκ1, hc0, hcA, hA0] + · nlinarith only [hκ1, hc0, hcA, hA0, hη] · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => P.policyOf_lt_maxSaving_of_pos (hposv z' A hAmem) (hcapv A hAmem z') have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hposv z' A hAmem - have hder : ∀ z' : Z, HasDerivAt P.u ((P.consumptionFnOf v z' A) ^ (-γ)) + have hder : ∀ z' : Z, HasDerivAt P.u ((η + P.consumptionFnOf v z' A) ^ (-γ)) (P.consumptionFnOf v z' A) := by - intro z'; rw [hu]; exact hasDerivAt_crraUtility γ (hc' z') + intro z'; rw [hu] + exact hasDerivAt_haraUtility γ (by linarith [hc' z']) have heuler := P.euler_ge (v := v) (z := z) (a := a) (A := A) ha rfl hA0 hslack hc0 - (by rw [hu]; exact hasDerivAt_crraUtility γ hc0) hc' hder - set X : ℝ := P.minMPC γ * ((1 + P.interest) * A) with hXdef - have hX0 : 0 < X := by rw [hXdef]; positivity - have hstep : ∀ z' : Z, X ≤ P.consumptionFnOf v z' A := by + (by rw [hu]; exact hasDerivAt_haraUtility γ (by linarith)) hc' hder + -- the continuation's shifted consumption is at least `κ (η + R A)` + set X : ℝ := P.minMPC γ * (η + (1 + P.interest) * A) with hXdef + have hX0 : 0 < X := by + have : (0 : ℝ) < (1 + P.interest) * A := by positivity + rw [hXdef]; nlinarith [hκ0, hη] + have hstep : ∀ z' : Z, X ≤ η + P.consumptionFnOf v z' A := by intro z' refine le_trans ?_ (ih z' A hAmem) have hres : P.resources (A, z') = P.income z' + (1 + P.interest) * A := by @@ -190,25 +195,47 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') rw [hres, hXdef] nlinarith only [hκ0, hinc] - have hsum : ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) - ≤ X ^ (-γ) := by - calc ∑ z' : Z, P.transitionMatrix z z' * (P.consumptionFnOf v z' A) ^ (-γ) + have hsum : ∑ z' : Z, P.transitionMatrix z z' + * (η + P.consumptionFnOf v z' A) ^ (-γ) ≤ X ^ (-γ) := by + calc ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) ≤ ∑ z' : Z, P.transitionMatrix z z' * X ^ (-γ) := Finset.sum_le_sum fun z' _ => mul_le_mul_of_nonneg_left (rpow_neg_antitone hγ0 hX0 (hstep z')) (P.transitionMatrix_nonneg z z') _ = X ^ (-γ) := by rw [← Finset.sum_mul, P.transitionMatrix_sum z, one_mul] - have hchain : c ^ (-γ) ≤ (P.discount : ℝ) * (1 + P.interest) * X ^ (-γ) := by + have hchain : (η + c) ^ (-γ) ≤ (P.discount : ℝ) * (1 + P.interest) * X ^ (-γ) := by have := le_trans heuler (mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hsum hR.le) hβ.le) linarith [this] - have hTc : (T * c) ^ (-γ) ≤ X ^ (-γ) := by - rw [Real.mul_rpow hT0.le hc0.le, hTneg, inv_mul_le_iff₀ hβR0] + have hTc : (T * (η + c)) ^ (-γ) ≤ X ^ (-γ) := by + rw [Real.mul_rpow hT0.le (by linarith), hTneg, inv_mul_le_iff₀ hβR0] linarith [hchain] - have hXTc : X ≤ T * c := le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc + have hXTc : X ≤ T * (η + c) := + le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc rw [hXdef] at hXTc + -- `κR + Þ = R`, and the extra `η` term has the right sign because `κ ≥ 0` refine le_of_mul_le_mul_right (a := 1 + P.interest) ?_ hR - linear_combination hXTc - c * hkey - (P.minMPC γ * (1 + P.interest)) * hcA + linear_combination hXTc - (η + c) * hkey + - (P.minMPC γ * (1 + P.interest)) * hcA + mul_nonneg hκ0.le hη + +/-- **One Euler step of the minimal-MPC bound.** If a continuation `v` satisfies +`κ m ≤ c_v(m)` everywhere, with consumption positive and the cap slack against it, then so does +`bellman v`. Extracted from the induction so that it can also drive a SIMULTANEOUS induction on +cap slack (`crra_minMPC_of_cap`), which is what large `β` needs. -/ +theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + {v : (ℝ × Z) →ᵇ ℝ} + (hposv : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf v z a) + (hcapv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf v (a, z) < assetCap) + (hposW : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf (P.toExtended.bellman v) z a) + (ih : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf v z a) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf (P.toExtended.bellman v) z a := by + simpa only [zero_add] using P.hara_minMPC_step hγ0 (le_refl (0 : ℝ)) + (hu.trans (haraUtility_zero γ).symm) hfl hκ hβ hposv hcapv hposW + (fun z' a' ha' => by simpa only [zero_add] using ih z' a' ha') z ha /-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, uniformly in the horizon — so it passes to the value function. @@ -246,7 +273,7 @@ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := Function.iterate_succ_apply' _ _ _ rw [hiter] - exact P.crra_minMPC_step hγ0 hu rfl hκ hβ hβR (hpos k) (hcap k) + exact P.crra_minMPC_step hγ0 hu rfl hκ hβ (hpos k) (hcap k) (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ih z ha /-! ### At the fixed point, and what it buys @@ -376,7 +403,7 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ ≤ P.consumptionFnOf ((P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by intro z a ha rw [hiter] - exact P.crra_minMPC_step hγ0 hu hfl hκ hβ hβR (hpos k) ihc + exact P.crra_minMPC_step hγ0 hu hfl hκ hβ (hpos k) ihc (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ihm z ha exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ @@ -431,16 +458,7 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by subst hfl - have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one - have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR - set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef - have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ - have hκ0 : 0 < P.minMPC γ := hκ have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ - have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) - have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by - rw [hTdef, ← Real.rpow_mul hβR0.le, - show (1 / γ) * (-γ) = -1 from by field_simp, Real.rpow_neg_one] intro n induction n with | zero => @@ -455,65 +473,11 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : nlinarith only [hres, hκ1, hη] | succ k ih => intro z a ha - have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) - = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := - Function.iterate_succ_apply' _ _ _ - set v : (ℝ × Z) →ᵇ ℝ := (P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ) with hvdef - rw [hiter] - set W : (ℝ × Z) →ᵇ ℝ := P.toExtended.bellman v with hWdef - set A : ℝ := P.policyOf W (a, z) with hAdef - have hAmem : A ∈ Icc (0 : ℝ) assetCap := P.feasible_subset_region (P.policyOf_mem W (a, z)) - set c : ℝ := P.consumptionFnOf W z a with hcdef - have hcA : c = P.resources (a, z) - A := rfl - have hc0 : 0 < c := by - have := hpos (k + 1) z a ha - rwa [hiter] at this - rcases eq_or_lt_of_le hAmem.1 with hA0 | hA0 - · nlinarith only [hκ1, hc0, hcA, hA0, hη] - · have hslack : ∀ z' : Z, P.policyOf v (A, z') < P.maxSaving (A, z') := fun z' => - P.policyOf_lt_maxSaving_of_pos (hpos k z' A hAmem) (hcap k A hAmem z') - have hc' : ∀ z' : Z, 0 < P.consumptionFnOf v z' A := fun z' => hpos k z' A hAmem - have hder : ∀ z' : Z, HasDerivAt P.u ((η + P.consumptionFnOf v z' A) ^ (-γ)) - (P.consumptionFnOf v z' A) := by - intro z'; rw [hu] - exact hasDerivAt_haraUtility γ (by linarith [hc' z']) - have heuler := P.euler_ge (v := v) (z := z) (a := a) (A := A) ha rfl hA0 hslack hc0 - (by rw [hu]; exact hasDerivAt_haraUtility γ (by linarith)) hc' hder - -- the continuation's shifted consumption is at least `κ (η + R A)` - set X : ℝ := P.minMPC γ * (η + (1 + P.interest) * A) with hXdef - have hX0 : 0 < X := by - have : (0 : ℝ) < (1 + P.interest) * A := by positivity - rw [hXdef]; nlinarith [hκ0, hη] - have hstep : ∀ z' : Z, X ≤ η + P.consumptionFnOf v z' A := by - intro z' - refine le_trans ?_ (ih z' A hAmem) - have hres : P.resources (A, z') = P.income z' + (1 + P.interest) * A := by - simp only [resources, max_eq_right hAmem.1] - have hinc : 0 < P.income z' := lt_of_lt_of_le P.minIncome_pos (P.minIncome_le z') - rw [hres, hXdef] - nlinarith only [hκ0, hinc] - have hsum : ∑ z' : Z, P.transitionMatrix z z' - * (η + P.consumptionFnOf v z' A) ^ (-γ) ≤ X ^ (-γ) := by - calc ∑ z' : Z, P.transitionMatrix z z' * (η + P.consumptionFnOf v z' A) ^ (-γ) - ≤ ∑ z' : Z, P.transitionMatrix z z' * X ^ (-γ) := - Finset.sum_le_sum fun z' _ => - mul_le_mul_of_nonneg_left (rpow_neg_antitone hγ0 hX0 (hstep z')) - (P.transitionMatrix_nonneg z z') - _ = X ^ (-γ) := by rw [← Finset.sum_mul, P.transitionMatrix_sum z, one_mul] - have hchain : (η + c) ^ (-γ) ≤ (P.discount : ℝ) * (1 + P.interest) * X ^ (-γ) := by - have := le_trans heuler - (mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left hsum hR.le) hβ.le) - linarith [this] - have hTc : (T * (η + c)) ^ (-γ) ≤ X ^ (-γ) := by - rw [Real.mul_rpow hT0.le (by linarith), hTneg, inv_mul_le_iff₀ hβR0] - linarith [hchain] - have hXTc : X ≤ T * (η + c) := - le_of_rpow_neg_le (by positivity) hX0 hγ0 hTc - rw [hXdef] at hXTc - -- `κR + Þ = R`, and the extra `η` term has the right sign because `κ ≥ 0` - refine le_of_mul_le_mul_right (a := 1 + P.interest) ?_ hR - linear_combination hXTc - (η + c) * hkey - - (P.minMPC γ * (1 + P.interest)) * hcA + mul_nonneg hκ0.le hη + rw [Function.iterate_succ_apply'] + exact P.hara_minMPC_step hγ0 hη hu rfl hκ hβ (hpos k) (hcap k) + (fun z' b hb => by + have h := hpos (k + 1) z' b hb + simpa only [Function.iterate_succ_apply'] using h) ih z ha /-- **The shifted-CRRA bound at the value function.** -/ theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) diff --git a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean index 2d98c1e4b..cfca1acf4 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MonotoneTransitions.lean @@ -19,15 +19,15 @@ continuations differ, and the lowest-income state is still the lowest-consumptio its continuation is the STEEPEST — a poor household today expects to be poor tomorrow, so wealth is worth more to it at the margin. -That is Huggett (1993) Lemma 1, `v'(a, e_h) ≤ v'(a, e_l)`, proved there by induction on the -Bellman iterates under `π(e_h | e_h) ≥ π(e_h | e_l)`. This file is that induction, in secant form -and for a finite income process ordered by income. +This file proves the property in secant form for a finite income process ordered by income, +using induction on Bellman iterates. Related literature: Huggett (1993), *The Risk-Free Rate +in Heterogeneous-Agent Incomplete-Insurance Economies*. ## Monotone transitions The condition is stated by its use: a state with higher income has a transition row that makes every income-antitone function of tomorrow's state smaller in expectation -(`MonotoneTransitions`). For two states it is exactly Huggett's `π(h|h) ≥ π(h|l)` +(`MonotoneTransitions`). For two states it is exactly `π(h|h) ≥ π(h|l)` (`monotoneTransitions_of_two_states`); for a Tauchen chain it is first-order stochastic dominance of the rows, which the chain has by construction. @@ -101,7 +101,8 @@ both push consumption up. -/ /-! ### The Bellman step, by the envelope -/ -/-- **Huggett's Lemma 1, one step.** If `v` has income-antitone increments then so does +/-- **Income-antitone increments, one Bellman step.** If `v` has income-antitone increments, +then so does `bellman v`: the derivative of `bellman v (·, z)` is `R · u'(c_v(·, z))`, and consumption at a fixed asset level rises with income. -/ theorem incDiffAcrossStates_bellman (hmono : P.MonotoneTransitions) @@ -155,7 +156,7 @@ theorem incDiffAcrossStates_bellman (hmono : P.MonotoneTransitions) /-! ### Along the iteration, and at the fixed point -/ -/-- **Huggett's Lemma 1 for the value function.** -/ +/-- **Income-antitone increments at the value function.** -/ theorem incDiffAcrossStates_valueFunction (hmono : P.MonotoneTransitions) {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hpc : P.PositiveConsumptionAll) From b6185faeeb293c91a48db1f7f4c70bdfc423f4fd Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 15:37:06 +0000 Subject: [PATCH 10/19] Reuse the existing zero-shift utility lemma --- LeanPool/AiyagariEquilibrium/Analysis/HARA.lean | 5 ----- LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean index 88ebd297e..d6766688e 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -66,11 +66,6 @@ namespace LeanEconomics /-- Shifted CRRA: the `b ≠ 0` branch of HARA, with subsistence level `a`. -/ noncomputable def haraUtility (γ a c : ℝ) : ℝ := crraUtility γ (a + c) -/-- A zero shift recovers ordinary CRRA utility. -/ -theorem haraUtility_zero (γ : ℝ) : haraUtility γ 0 = crraUtility γ := by - funext c - simp only [haraUtility, zero_add] - @[simp] theorem haraUtility_zero_shift (γ : ℝ) : haraUtility γ 0 = crraUtility γ := by funext c; simp [haraUtility] diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index fca72a1cb..4d15fb40b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -234,7 +234,7 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf (P.toExtended.bellman v) z a := by simpa only [zero_add] using P.hara_minMPC_step hγ0 (le_refl (0 : ℝ)) - (hu.trans (haraUtility_zero γ).symm) hfl hκ hβ hposv hcapv hposW + (hu.trans (haraUtility_zero_shift γ).symm) hfl hκ hβ hposv hcapv hposW (fun z' a' ha' => by simpa only [zero_add] using ih z' a' ha') z ha /-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, From acdafaef955796e7a7e7c7366dac0ed5ef298041 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 16:31:49 +0000 Subject: [PATCH 11/19] Remove redundant assumptions from household helper APIs --- .../Equilibrium/SignChange.lean | 4 +- .../Models/CRRAConstants.lean | 16 ++++---- .../Models/IncomeFluctuationHARA.lean | 2 +- .../Models/LargeBetaLight.lean | 2 +- .../Models/MinimalMPC.lean | 38 +++++++++---------- 5 files changed, 29 insertions(+), 33 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean index 2460933da..9d05ed8f1 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean @@ -376,7 +376,7 @@ theorem crra_aggregateCapital_le_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u P.aggregateCapital μ ≤ (1 - P.minMPC γ) * P.maxIncome / (1 - (1 - P.minMPC γ) * (1 + P.interest)) := by refine P.aggregateCapital_le_of_consumption_bound hμ - (IncomeFluctuation.minMPC_le_one hγ0 hβ) ?_ ?_ + IncomeFluctuation.minMPC_le_one ?_ ?_ · rw [IncomeFluctuation.one_sub_minMPC_mul] exact IncomeFluctuation.patience_lt_one hγ0 hβR · exact fun z a ha => P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hβR hpos hcapit z ha @@ -397,7 +397,7 @@ theorem hara_aggregateCapital_le_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : ≤ (1 - P.minMPCShare γ η) * P.maxIncome / (1 - (1 - P.minMPCShare γ η) * (1 + P.interest)) := by refine P.aggregateCapital_le_of_consumption_bound hμ ?_ hshare ?_ - · have hκ : P.minMPC γ ≤ 1 := IncomeFluctuation.minMPC_le_one hγ0 hβ + · have hκ : P.minMPC γ ≤ 1 := IncomeFluctuation.minMPC_le_one have hy : 0 < P.minIncome := P.minIncome_pos have : (0 : ℝ) ≤ (1 - P.minMPC γ) * η / P.minIncome := by apply div_nonneg (mul_nonneg (by linarith) hη) hy.le diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 793b7222b..6a09eff4f 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -402,7 +402,7 @@ consumption, which is the shape the decline condition needs. -/ /-- **The proportional CES bound on saving.** -/ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) - (_hb : P.Bounded) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) ≤ (P.discount * P.oscGap / θ) @@ -422,7 +422,7 @@ is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`; with `θ factor is close to `income z ^ γ`, so the threshold shrinks with the income of the state -- which is exactly what lets the corner condition reach it. -/ theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) - (hθr : θ ≤ 1 + P.interest) (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hθr : θ ≤ 1 + P.interest) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) (hlt : (P.discount * P.oscGap / θ) * (P.income z + (1 + P.interest - θ) * assetCap) ^ γ < a) : @@ -433,7 +433,7 @@ theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg set b : ℝ := P.policy (a, z) with hbdef set c : ℝ := P.consumption (a, z) b with hcdef - have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z + have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc ha z rw [← hbdef, ← hcdef] at hkey have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by simp only [resources, max_eq_right ha.1] @@ -475,12 +475,12 @@ estimate. Related literature: Marcet, Obiols-Homs and Weil (2007), *Incomplete Markets, Labor Supply and Capital Accumulation*. The sufficient criterion below is specific to this capped household -model: it uses the displayed quantitative inequality, bounded utility and positive optimal +model: it uses the displayed quantitative inequality and positive optimal consumption. Its fixed-point and iterative versions share the same comparison argument. -/ /-- **Saving never reaches the asset cap.** -/ theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) - (hθ1 : θ < 1) (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (hlt : (P.discount * P.oscGap / θ) * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) @@ -493,7 +493,7 @@ theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h (by simpa only [zero_add, P.consumptionFnOf_valueFunction] using (P.consumptionFn_pos hpc ha z).le) (by simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using - P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hb hu hpc ha z) + P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc ha z) (by simpa only [zero_add, mul_zero, sub_zero] using hlt) simpa only [P.policyOf_valueFunction] using h @@ -503,13 +503,13 @@ top of the asset region -- cannot occur, so nothing in this development contradi the consumption function. That is not a proof of concavity; it is the removal of the obstruction that made one impossible. -/ theorem no_flat_at_cap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) - (hb : P.Bounded) (hu : P.u = crraUtility γ) + (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (hlt : (P.discount * P.oscGap / θ) * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) ≠ assetCap := - ne_of_lt (P.crra_policy_lt_assetCap hγ0 hγ1 hθ0 hθ1 hb hu hpc hlt ha z) + ne_of_lt (P.crra_policy_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc hlt ha z) /-- **A quantitative CES saving floor**, the form the sign-change argument consumes. -/ theorem crra_policy_ge_of_gain {γ : ℝ} (hγ : 0 < γ) (hu : P.u = crraUtility γ) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index 6b30aff82..0a8cb72d3 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -683,7 +683,7 @@ theorem hara_valueFunction_lipschitz {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ /-- **The corner condition from primitives.** One inequality, `norm_num`-checkable. -/ theorem hara_policy_eq_zero_of_primitives {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 < η) (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) - (hβR : P.discount * (1 + P.interest) < 1) (z₀ : Z) {a₀ : ℝ} (_ha₀ : 0 ≤ a₀) + (hβR : P.discount * (1 + P.interest) < 1) (z₀ : Z) {a₀ : ℝ} (ha₀cap : a₀ ≤ assetCap) (hcond : P.discount * P.haraLipschitz γ η < (η + P.income z₀ + (1 + P.interest) * a₀) ^ (-γ)) diff --git a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean index ceb053e29..b6d429164 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean @@ -117,7 +117,7 @@ theorem crra_policyOf_withRate_lt_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 have hslices : ConcaveSlices (0 : ℝ) assetCap v := Q₁.concaveSlices_iterate_zero n - have hκ1 : κ ≤ 1 := minMPC_le_one (P := Q₁) hγ0 hβ + have hκ1 : κ ≤ 1 := minMPC_le_one (P := Q₁) -- economy 1's minimal-MPC bound at this iterate have hmpc : κ * Q₁.resources (a, z) ≤ Q₁.consumptionFnOf v z a := (Q₁.crra_minMPC_of_cap hγ0 (show Q₁.u = crraUtility γ from hu) rfl diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index 4d15fb40b..f8dee6b82 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -82,7 +82,7 @@ noncomputable def minMPC (γ : ℝ) : ℝ := variable {P} /-- The absolute patience factor is positive when the household discounts at all. -/ -theorem patience_pos {γ : ℝ} (_hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) : +theorem patience_pos {γ : ℝ} (hβ : 0 < (P.discount : ℝ)) : 0 < ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) := Real.rpow_pos_of_pos (mul_pos hβ P.interest_gt_neg_one) _ @@ -103,14 +103,11 @@ theorem one_sub_minMPC_mul {γ : ℝ} : field_simp ring -theorem minMPC_le_one {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) : - P.minMPC γ ≤ 1 := by - have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one - have := patience_pos (P := P) hγ0 hβ - simp only [minMPC] - have : (0 : ℝ) < ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) / (1 + P.interest) := by - positivity - linarith +theorem minMPC_le_one {γ : ℝ} : P.minMPC γ ≤ 1 := by + unfold minMPC + exact sub_le_self _ (div_nonneg + (Real.rpow_nonneg (mul_nonneg P.discount.coe_nonneg P.interest_gt_neg_one.le) _) + P.interest_gt_neg_one.le) /-- **At `γ = 1` the patience factor is `βR` itself**, so `κ = 1 - β`: log and log Stone--Geary need no root at all. -/ @@ -158,9 +155,9 @@ theorem hara_minMPC_step {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one have hβR0 : (0 : ℝ) < (P.discount : ℝ) * (1 + P.interest) := mul_pos hβ hR set T : ℝ := ((P.discount : ℝ) * (1 + P.interest)) ^ (1 / γ) with hTdef - have hT0 : 0 < T := patience_pos (P := P) hγ0 hβ + have hT0 : 0 < T := patience_pos (P := P) hβ have hκ0 : 0 < P.minMPC γ := hκ - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) have hkey : (1 - P.minMPC γ) * (1 + P.interest) = T := one_sub_minMPC_mul (P := P) have hTneg : T ^ (-γ) = ((P.discount : ℝ) * (1 + P.interest))⁻¹ := by rw [hTdef, ← Real.rpow_mul hβR0.le, @@ -254,7 +251,7 @@ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by subst hfl - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) intro n induction n with | zero => @@ -337,8 +334,7 @@ both. The cap is then bookkeeping again: any `cap > (1-κ)maxIncome / (1 - (1-κ /-- Saving under the minimal-MPC bound is at most `(1-κ) m`, hence below the cap once the cap clears `(1-κ)(maxIncome + R·cap)`. -/ -theorem crra_policyOf_lt_cap_of_minMPC_bound {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 < (P.discount : ℝ)) - (hfl : assetFloor = 0) +theorem crra_policyOf_lt_cap_of_minMPC_bound {γ : ℝ} (hfl : assetFloor = 0) (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) {v : (ℝ × Z) →ᵇ ℝ} (hv : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, @@ -346,7 +342,7 @@ theorem crra_policyOf_lt_cap_of_minMPC_bound {γ : ℝ} (hγ0 : 0 < γ) (hβ : 0 {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policyOf v (a, z) < assetCap := by subst hfl have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by simp only [resources, max_eq_right ha.1] have hbd : P.income z + (1 + P.interest) * a ≤ P.maxIncome + (1 + P.interest) * assetCap := by @@ -378,7 +374,7 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ intro n induction n with | zero => - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) have hm : ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, P.minMPC γ * P.resources (a, z) ≤ P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by @@ -392,7 +388,7 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ rw [hz]; simp [consumption] rw [heq] nlinarith only [hres, hκ1] - exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ + exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hfl hthr hm ha z⟩ | succ k ih => obtain ⟨ihm, ihc⟩ := ih have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) @@ -405,7 +401,7 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ rw [hiter] exact P.crra_minMPC_step hγ0 hu hfl hκ hβ (hpos k) ihc (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ihm z ha - exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr hm ha z⟩ + exact ⟨hm, fun a ha z => P.crra_policyOf_lt_cap_of_minMPC_bound hfl hthr hm ha z⟩ /-- **The minimal-MPC bound at the value function, with the cap from primitives.** -/ theorem crra_minMPC_mul_le_consumptionFn_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) @@ -427,7 +423,7 @@ theorem crra_policy_lt_cap_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crra 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) < assetCap := - P.crra_policyOf_lt_cap_of_minMPC_bound hγ0 hβ hfl hthr + P.crra_policyOf_lt_cap_of_minMPC_bound hfl hthr (fun z' _b hb => P.crra_minMPC_mul_le_consumptionFn_of_cap hγ0 hu hfl hκ hβ hβR hpos hthr z' hb) ha z @@ -458,7 +454,7 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by subst hfl - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) intro n induction n with | zero => @@ -524,7 +520,7 @@ theorem hara_minMPCShare_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPCShare γ η * P.resources (a, z) ≤ P.consumptionFn z a := by have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hβR hpos hcap z ha - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) hγ0 hβ + have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) have hy : 0 < P.minIncome := P.minIncome_pos have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by From 485ec3475b6b86494b0705d00d82384e9e5cde06 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 17:20:18 +0000 Subject: [PATCH 12/19] Qualify source comparisons and strengthen MPC helper APIs --- .../AiyagariEquilibrium/Analysis/HARA.lean | 6 ++-- .../DynamicProgramming/Blackwell.lean | 2 +- .../Equilibrium/AiyagariUniqueness.lean | 8 ++--- .../Equilibrium/Bisection.lean | 11 ++++--- .../Equilibrium/SignChange.lean | 5 ++-- .../Equilibrium/Uniqueness.lean | 11 +++++-- .../Models/EulerCorner.lean | 5 ++-- .../IncomeFluctuationCarrollKimball.lean | 11 ++++--- .../Models/IncomeFluctuationConsumption.lean | 19 ++++++------ .../Models/IncomeFluctuationRateMonotone.lean | 6 ++-- .../Models/LargeBetaLight.lean | 11 ++++--- .../Models/LogAiyagari.lean | 4 +-- .../Models/MinimalMPC.lean | 30 +++++++------------ .../AiyagariEquilibrium/Topology/Berge.lean | 2 +- 14 files changed, 63 insertions(+), 68 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean index d6766688e..68f436d83 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/HARA.lean @@ -12,9 +12,9 @@ public import LeanPool.AiyagariEquilibrium.Models.CRRA # HARA utility Hyperbolic absolute risk aversion: absolute risk tolerance `-u' / u''` is affine in consumption. -Toda (2021) shows HARA is NECESSARY for the consumption function to be concave under regularity -conditions, so it is the class in which Carroll and Kimball (1996) -- and hence Light's -uniqueness argument -- can possibly hold. +[Toda's November 2020 draft](https://arxiv.org/pdf/2009.13564v2) studies concavity uniformly +over arbitrary return and income laws, under its regularity hypotheses with borrowing permitted. +That necessity result does not exclude concavity outside HARA in a particular capped model. ## The definition diff --git a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean index 223120e33..6aaf27f93 100644 --- a/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean +++ b/LeanPool/AiyagariEquilibrium/DynamicProgramming/Blackwell.lean @@ -45,7 +45,7 @@ the Banach fixed point theorem needs. ## References -* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*, theorem 3.3. +* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*. * Blackwell, *Discounted dynamic programming*, Ann. Math. Statist. 36 (1965). -/ diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index 199b9dfc5..455e31459 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -67,7 +67,7 @@ variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) `1 - β` at every rate, so every cap condition collapses to the one on `hcap`. -/ theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) - (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hlohi : rlo ≤ rhi) (hε : 0 < ε) (hcap : (P.discount : ℝ) * P.maxIncome + ((P.discount : ℝ) * (1 + rhi) + ε) * assetCap < assetCap) : @@ -102,7 +102,7 @@ theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) have hεcap : (0 : ℝ) ≤ ε * assetCap := mul_nonneg hε.le hcap0 refine P.crra_policy_mono_withRate_of_minMPC (γ := 1) one_pos le_rfl hu hxok hyok hxy hκxpos hκypos hβ - (by linarith) ((P.withRate x hxok).positiveConsumptionAll_of_unbounded hunb) + ((P.withRate x hxok).positiveConsumptionAll_of_unbounded hunb) ((P.withRate y hyok).positiveConsumptionAll_of_unbounded hunb) ?_ ?_ ?_ hs z · rw [hκx] have := mul_le_mul_of_nonneg_left hx_le hcap0 @@ -228,7 +228,7 @@ theorem log_monotoneOn_capitalSupply {rlo rhi ε a₀ : ℝ} (hu : P.u = crraUti nlinarith refine P.monotoneOn_capitalSupply_of_policy_mono (P.rateFamily hrlo hlohi) (fun r r' z z' => rfl) - (P.log_policy_mono_rateFamily hu hunb hβ hβ1 hrlo hlohi hβR hε hcap) + (P.log_policy_mono_rateFamily hu hunb hβ hβ1 hrlo hlohi hε hcap) ⟨Measure.dirac (Classical.ofNonempty : P.State), inferInstance⟩ ν ?_ intro r hr have hrr : P.RateOK r := rateOK_of_floor_zero (by linarith [hr.1]) @@ -382,7 +382,7 @@ theorem log_aggregateCapital_le {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.U have hslackIt : ∀ n : ℕ, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r hrr).policyOf (((P.withRate r hrr).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap := - fun n x hx z => ((P.withRate r hrr).crra_minMPC_of_cap one_pos hu rfl hκpos hβ hβR' + fun n x hx z => ((P.withRate r hrr).crra_minMPC_of_cap one_pos hu rfl hκpos hβ hposIt hthr' n).2 x hx z have hkey := (P.withRate r hrr).crra_aggregateCapital_le_of_minMPC one_pos hu hκpos hβ hβR' hposIt hslackIt hμ diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean index 6448a0a7e..3e757e0b1 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -10,11 +10,10 @@ public import LeanPool.AiyagariEquilibrium.Equilibrium.AiyagariUniqueness /-! # Bisection for a monotone zero and normalized clearing -Aiyagari (1994) computed his equilibrium by bisection on the interest rate: evaluate excess -supply at the midpoint of a bracket and keep the half on which it still changes sign. Hajek and -Livesay (2019) ask, for mean field games, which equilibria are STABLE fixed points of the -best-response map; for the Bewley model the raw best-response iteration `r ↦ r_d(K_s(r))` has -negative slope and may oscillate, so the algorithm that is certified here is the bisection. +Bisection evaluates excess supply at the midpoint of a bracket and keeps the half containing +the zero. Hajek and Livesay (2019) discuss stability of best-response fixed points in mean field +games. Here, a decreasing inverse-demand map composed with an increasing supply map can reverse +order, so raw iteration `r ↦ r_d(K_s(r))` may oscillate. This file certifies bisection. ## The abstract statement @@ -253,7 +252,7 @@ theorem log_bisection_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < exact ⟨fun n => (bisect_mem_of_strictMonoOn hE hr hzero n).2.2, abs_bisect_mid_sub_le hE hr hzero, tendsto_bisect_fst hE hr hzero⟩ -/-- **Aiyagari (1994) at `μ = 1`: his bisection finds his equilibrium rate.** On any +/-- **Bisection for normalized clearing with the Aiyagari log calibration.** On any `[rlo, rhi] ⊂ (-δ, λ)` with the cap clearing `(48/25)·maxIncome/(1 - (24/25)(1+rhi))`, and along any selection of stationary distributions, the bisection on excess supply brackets the equilibrium rate at every step, its midpoint is within `(rhi - rlo)/2^(n+1)` after `n` steps, diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean index 9d05ed8f1..fe348d800 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/SignChange.lean @@ -379,14 +379,13 @@ theorem crra_aggregateCapital_le_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u IncomeFluctuation.minMPC_le_one ?_ ?_ · rw [IncomeFluctuation.one_sub_minMPC_mul] exact IncomeFluctuation.patience_lt_one hγ0 hβR - · exact fun z a ha => P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hβR hpos hcapit z ha + · exact fun z a ha => P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hpos hcapit z ha /-- **The ceiling for shifted CRRA.** The subsistence level enters through `minMPCShare`, which charges the constant in `κ(η+m) ≤ η+c` against the smallest cash on hand there can be. At `η = 0` it is the CRRA statement. -/ theorem hara_aggregateCapital_le_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hshare : (1 - P.minMPCShare γ η) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) @@ -404,7 +403,7 @@ theorem hara_aggregateCapital_le_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : simp only [IncomeFluctuation.minMPCShare] linarith · exact fun z a ha => - P.hara_minMPCShare_mul_le_consumptionFn hγ0 hη hu rfl hκ hβ hβR hpos hcapit z ha + P.hara_minMPCShare_mul_le_consumptionFn hγ0 hη hu rfl hκ hβ hpos hcapit z ha end IncomeFluctuation diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean index 2e028aa7d..2a5c2448c 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Uniqueness.lean @@ -28,20 +28,25 @@ rate raises the price of current consumption, but also makes existing wealth wor only when `γ ≤ 1` does the first dominate. For `γ > 1` supply need not rise. Açıkgöz (2018) exhibits multiple stationary equilibria at -`γ = 6.5`. Walsh and Young (2025) search 24,000 parameter draws and find multiplicity in 250 -of them — always exactly three equilibria — and report that they never see it with risk +`γ = 6.5`. Walsh and Young's +[November 19, 2025 draft](https://www.aeaweb.org/conference/2026/preliminary/paper/i57n5khB) +uses 24,000 parameter draws, retains 23,673 after numerical screening, and reports 250 cases +with three equilibria. They report that they never see multiplicity with risk aversion below about 1.49, depreciation below about 0.19, or income persistence below about 0.47, and only ever with a disaster state for income. Their mechanism is precautionary: with a disaster state, persistent income and high risk aversion, agents facing a low return still amass capital to insure against near-zero income, so capital supply bends back and can cross downward-sloping demand more than once. +These findings concern their competitive economies, not counterexamples instantiating this +capped household and its prescribed normalized demand. Kirkby (2018) approaches it computationally and finds both Aiyagari (1994) and Pijoan-Mas (2006) "almost certainly have a unique general equilibrium". So the honest formal target is CONDITIONAL uniqueness — uniqueness given monotone supply — plus, separately, whatever route establishes monotone supply for a restricted class. That is -what is proved here. An unconditional uniqueness theorem for this model would be false. +what is proved here; monotone supply remains an explicit input rather than a claim about every +calibration. -/ @[expose] public section diff --git a/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean index 3ac3a366e..5e8ff4c94 100644 --- a/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean +++ b/LeanPool/AiyagariEquilibrium/Models/EulerCorner.lean @@ -31,8 +31,9 @@ saving is not optimal: if it were, the Euler inequality and step one would give `u'(cash on hand) ≤ u'(c) ≤ βR u'(minIncome)`. This is the contrapositive of `euler_ge`. For log utility the test at the worst income state is `a < minIncome (1 - βR) / (βR²)`: a small -but positive corner region, at every `β < 1/R`. Compare Clarida (1987), who has the corner on -`[ε - rφ, w_bar]` under `u'(0) < ∞`; here nothing is assumed about `u'(0)`. +but positive corner region, at every `β < 1/R`, without assuming a value for `u'(0)`. +Related literature: Clarida (1987), *Consumption, Liquidity Constraints and Asset Accumulation +in the Presence of Random Income Fluctuations*. -/ @[expose] public section diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean index 0b2171842..f7948f01e 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -71,17 +71,16 @@ OSCILLATION rather than its level, and the hypothesis is character for character `nearLog_concaveOn_consumptionFn` is Carroll and Kimball for a concrete CES economy, at every rate the equilibrium argument uses. -**Toda (2021) still bounds what can be hoped for.** Under regularity conditions HARA is -NECESSARY for the consumption function to be concave, and the structure assumes of `u` only -strict concavity, so `hT` at that generality is FALSE and no proof of it can exist. That is why -the step above is stated for CRRA, which is what Light's published version (2020) assumes. +**Toda's necessity result is uniform**, across arbitrary return and income laws under its +regularity hypotheses with borrowing permitted; see `Analysis.HARA`. It does not establish that `hT` +is false for an individual capped household outside HARA. This file proves sufficient results +for the stated CRRA families. ## OWED: this is deferred, not abandoned The obstruction has moved four times. -* HARA was the first answer: Toda (2021) makes it NECESSARY, and `Analysis/HARA.lean` supplies - it. The utility class is no longer the blocker. +* Shifted CRRA was the first sufficient utility family; `Analysis.HARA` supplies this branch. * The asset cap was the second, and worse, because it made the conclusion outright FALSE -- `not_concaveOn_consumptionFn_of_cap_binds` below. `crra_policy_lt_assetCap` removes it by calibration, and `nearLog` is recalibrated so that saving provably never reaches the cap. diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean index 679f7c943..ad7750603 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationConsumption.lean @@ -214,23 +214,24 @@ theorem monotoneOn_consumptionFn (z : Z) : /-! ### A linear lower bound on consumption makes assets decline -Ma and Toda (2022) prove that the consumption function is asymptotically LINEAR whenever -marginal utility is regularly varying, and characterise the slope. Their Corollary 2.10 covers -this model — constant discount factor and return, bounded relative risk aversion, `R ≥ 1` — and -gives the asymptotic marginal propensity to consume in closed form, +Ma and Toda study asymptotic linearity in an UNCAPPED savings problem. Corollary 2.10 of their +[March 8, 2021 draft](https://qingyin-ma.github.io/papers/AL_10_arxiv.pdf) gives the following +asymptotic marginal propensity under its standing assumptions, bounded relative risk aversion, +constant discount and return, and `R ≥ 1`. The standing infinite-horizon assumptions include +`β R < 1` and regular variation of marginal utility with index `-γ`, where `γ > 0`: `c_bar = 1 - (β R^(1-γ))^(1/γ)`. -That result is about the UNCAPPED problem and is not proved here. What is proved here is the -consequence, which is what this development actually wants: a linear lower bound on consumption -forces assets to fall, above an explicit threshold. That is precisely the hypothesis `hdecl` of +No identification of that uncapped problem with this capped household is asserted. What is +proved here is a conditional implication: a linear lower bound on consumption forces assets +to fall above an explicit threshold. That is precisely the hypothesis `hdecl` of `exists_exhaust_of_decline`, on which the whole Doeblin uniqueness and convergence argument rests, and which has been a raw assumption until now. -The two fit together exactly. With Ma and Toda's slope the asymptotic growth factor of assets is +For the displayed candidate slope, the algebraic growth factor is `(1 - c_bar) R = (β R)^(1/γ)` — `one_sub_mpc_mul_of_asymptotic`, below — so the condition `(1 - ε) (1 + r) < 1` demanded by `policy_lt_self_of_consumption_lower_bound` is exactly `β R < 1`. -Impatience, and nothing else, is what bounds assets. -/ +The lower-bound premise remains explicit; the identity alone supplies no asset bound. -/ /-- **The asymptotic growth factor of assets is `(β R)^(1/γ)`.** With Ma and Toda's asymptotic MPC `c_bar = 1 - (β R^(1-γ))^(1/γ)`, the factor `(1 - c_bar) R` by which assets grow collapses to diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean index fde47517c..04a09d8e4 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateMonotone.lean @@ -57,8 +57,10 @@ discharged is `policy_mono_withRate'`. Worth recording precisely: his step 5 applies Lemma 4 to `σ_f(·, R₂)` and needs it CONCAVE. That is Light's Lemma 5, which is the Carroll and Kimball theorem — the hypothesis `hT` of `IncomeFluctuationCarrollKimball`. So the residual hypothesis here and the residual hypothesis -there are not two gaps but one: concavity of the consumption function, which by Toda (2021) is -unavailable outside HARA. Closing it closes both. +there are not two gaps but one: concavity of the consumption function. Closing it closes both. +Toda's necessity result concerns concavity uniformly across return and income laws under its +regularity hypotheses with borrowing permitted. It does not exclude concavity outside HARA +for this capped model; see `Analysis.HARA` for the source. The pivotal inequality that step 5 turns on, `mul_marginal_le_of_scale`, is already proved. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean index b6d429164..6c842ea0c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LargeBetaLight.lean @@ -100,7 +100,7 @@ explicitly. -/ theorem crra_policyOf_withRate_lt_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) (hκ₁ : 0 < (P.withRate r₁ h₁).minMPC γ) - (hβ : 0 < (P.discount : ℝ)) (hβR₁ : (P.discount : ℝ) * (1 + r₁) < 1) + (hβ : 0 < (P.discount : ℝ)) (hpos₁ : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < (P.withRate r₁ h₁).consumptionFnOf (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) @@ -121,7 +121,7 @@ theorem crra_policyOf_withRate_lt_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr -- economy 1's minimal-MPC bound at this iterate have hmpc : κ * Q₁.resources (a, z) ≤ Q₁.consumptionFnOf v z a := (Q₁.crra_minMPC_of_cap hγ0 (show Q₁.u = crraUtility γ from hu) rfl - hκ₁ hβ hβR₁ hpos₁ hthr₁ n).1 z a ha + hκ₁ hβ hpos₁ hthr₁ n).1 z a ha -- the rescale, and monotonicity of consumption in assets set t : ℝ := (1 + r₁) / (1 + r₂) with ht have ht0 : 0 < t := div_pos hR₁ hR₂ @@ -167,7 +167,7 @@ theorem crra_policy_mono_withRate_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : (hu : P.u = crraUtility γ) {r₁ r₂ : ℝ} (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) (hκ₁ : 0 < (P.withRate r₁ h₁).minMPC γ) (hκ₂ : 0 < (P.withRate r₂ h₂).minMPC γ) - (hβ : 0 < (P.discount : ℝ)) (hβR₂ : (P.discount : ℝ) * (1 + r₂) < 1) + (hβ : 0 < (P.discount : ℝ)) (hpc₁ : (P.withRate r₁ h₁).PositiveConsumptionAll) (hpc₂ : (P.withRate r₂ h₂).PositiveConsumptionAll) (hthr₁ : (1 - (P.withRate r₁ h₁).minMPC γ) * (P.maxIncome + (1 + r₁) * assetCap) < assetCap) @@ -176,7 +176,6 @@ theorem crra_policy_mono_withRate_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : + ((1 - (P.withRate r₁ h₁).minMPC γ) * (1 + r₁) + (r₂ - r₁)) * assetCap < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : (P.withRate r₁ h₁).policy (a, z) ≤ (P.withRate r₂ h₂).policy (a, z) := by - have hβR₁ : (P.discount : ℝ) * (1 + r₁) < 1 := by nlinarith [hβ] -- positivity at the iterates, read in either economy have hpos₁ : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < (P.withRate r₁ h₁).consumptionFnOf @@ -195,7 +194,7 @@ theorem crra_policy_mono_withRate_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : (P.withRate r₂ h₂).policyOf (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap := fun n a ha z => ((P.withRate r₂ h₂).crra_minMPC_of_cap hγ0 hu rfl - hκ₂ hβ hβR₂ hpos₂ hthr₂ n).2 a ha z + hκ₂ hβ hpos₂ hthr₂ n).2 a ha z -- Carroll--Kimball along economy 2's iteration have hcons : ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) ((P.withRate r₂ h₂).consumptionFnOf @@ -206,7 +205,7 @@ theorem crra_policy_mono_withRate_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : (hslackw n a ha z)) exact P.policy_mono_withRate_hara hγ0 hγ1 le_rfl (by rw [haraUtility_zero_shift]; exact hu) h₁ h₂ hr (fun n a ha z => hposv n z a ha) (fun n a ha z => hpos₂ n z a ha) - (fun n a ha z => P.crra_policyOf_withRate_lt_cap hγ0 hu h₁ h₂ hr hκ₁ hβ hβR₁ hpos₁ hthr₁ + (fun n a ha z => P.crra_policyOf_withRate_lt_cap hγ0 hu h₁ h₂ hr hκ₁ hβ hpos₁ hthr₁ hgap n ha z) hslackw hcons ha z diff --git a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean index 5b89d1d1b..c6e84a64d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean +++ b/LeanPool/AiyagariEquilibrium/Models/LogAiyagari.lean @@ -64,7 +64,7 @@ theorem crra_exists_exhaust_of_euler_corner_of_lowest {γ : ℝ} (hγ0 : 0 < γ) -- cap slack at the fixed point, from the minimal MPC have hslack : ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, P.policy (x, z) < P.maxSaving (x, z) := fun z x hx => P.policyOf_lt_maxSaving_of_pos (hpos z x hx) - (P.crra_policy_lt_cap_of_minMPC hγ0 hu rfl hκ hβ hβR hposIt hthr hx z) + (P.crra_policy_lt_cap_of_minMPC hγ0 hu rfl hκ hβ hposIt hthr hx z) -- the corner below `a₀`, from the Euler inequality have hzero : ∀ a ∈ Icc (0 : ℝ) a₀, P.policy (a, z₀) = 0 := by intro a ha @@ -124,7 +124,7 @@ theorem crra_exists_exhaust_of_euler_corner_monotone {γ : ℝ} (hγ0 : 0 < γ) fun n z a ha => hpc _ (P.concaveSlices_iterate_zero n) z a ha have hslackIt : ∀ n : ℕ, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (x, z) < assetCap := - fun n x hx z => (P.crra_minMPC_of_cap hγ0 hu rfl hκ hβ hβR hposIt hthr n).2 x hx z + fun n x hx z => (P.crra_minMPC_of_cap hγ0 hu rfl hκ hβ hposIt hthr n).2 x hx z refine P.crra_exists_exhaust_of_euler_corner_of_lowest hγ0 hu rfl hκ hβ hβR hpc hthr (fun a ha z => ?_) ha₀ hle hcorner exact P.consumptionFn_le_of_income_le_monotone hmono (du := fun c => c ^ (-γ)) diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index f8dee6b82..67ebe8c7a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -242,7 +242,6 @@ positive at each iterate, and the saving cap slack there. Both are fields (or im consequences of fields) of `Calibrated`. -/ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, @@ -281,14 +280,13 @@ follow uniform limits when the limit has concave slices, which the value functio /-- **The minimal-MPC bound at the value function.** -/ theorem crra_minMPC_mul_le_consumptionFn {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := by - have hiter := P.crra_minMPC_mul_le_consumptionFnOf hγ0 hu hfl hκ hβ hβR hpos hcap + have hiter := P.crra_minMPC_mul_le_consumptionFnOf hγ0 hu hfl hκ hβ hpos hcap have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha have htend : Tendsto (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop @@ -310,7 +308,6 @@ its assets down, because it consumes at least the share `κ` of cash on hand and development has carried as an assumption. -/ theorem crra_policy_lt_self_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, @@ -321,7 +318,7 @@ theorem crra_policy_lt_self_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr P.policy (a, z) < a := by subst hfl refine P.policy_lt_self_of_consumption_lower_bound (ε := P.minMPC γ) ha ?_ (by linarith) - simpa using P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hβR hpos hcap z ha + simpa using P.crra_minMPC_mul_le_consumptionFn hγ0 hu rfl hκ hβ hpos hcap z ha /-! ### Cap slack from the bound itself @@ -361,7 +358,6 @@ theorem crra_policyOf_lt_cap_of_minMPC_bound {γ : ℝ} (hfl : assetFloor = 0) beyond one inequality among the primitives. -/ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) : @@ -406,26 +402,24 @@ theorem crra_minMPC_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ /-- **The minimal-MPC bound at the value function, with the cap from primitives.** -/ theorem crra_minMPC_mul_le_consumptionFn_of_cap {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := - P.crra_minMPC_mul_le_consumptionFn hγ0 hu hfl hκ hβ hβR hpos - (fun n => (P.crra_minMPC_of_cap hγ0 hu hfl hκ hβ hβR hpos hthr n).2) z ha + P.crra_minMPC_mul_le_consumptionFn hγ0 hu hfl hκ hβ hpos + (fun n => (P.crra_minMPC_of_cap hγ0 hu hfl hκ hβ hpos hthr n).2) z ha /-- **The cap is slack at the fixed point**, from primitives. -/ theorem crra_policy_lt_cap_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hthr : (1 - P.minMPC γ) * (P.maxIncome + (1 + P.interest) * assetCap) < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) < assetCap := P.crra_policyOf_lt_cap_of_minMPC_bound hfl hthr (fun z' _b hb => - P.crra_minMPC_mul_le_consumptionFn_of_cap hγ0 hu hfl hκ hβ hβR hpos hthr z' hb) + P.crra_minMPC_mul_le_consumptionFn_of_cap hγ0 hu hfl hκ hβ hpos hthr z' hb) ha z /-! ### The shifted-CRRA version @@ -445,7 +439,6 @@ household with income raised by `η` — which is what a subsistence level is. - theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, @@ -479,14 +472,13 @@ theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFn z a := by - have hiter := P.hara_minMPC_mul_le_consumptionFnOf hγ0 hη hu hfl hκ hβ hβR hpos hcap + have hiter := P.hara_minMPC_mul_le_consumptionFnOf hγ0 hη hu hfl hκ hβ hpos hcap have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha have htend : Tendsto (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop @@ -512,14 +504,13 @@ noncomputable def minMPCShare (γ η : ℝ) : ℝ := theorem hara_minMPCShare_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : P.minMPCShare γ η * P.resources (a, z) ≤ P.consumptionFn z a := by - have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hβR hpos hcap z ha + have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hpos hcap z ha have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) have hy : 0 < P.minIncome := P.minIncome_pos have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one @@ -547,7 +538,6 @@ household behaves like a CRRA household whose income is raised by the subsistenc theorem hara_policy_lt_self_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hβR : (P.discount : ℝ) * (1 + P.interest) < 1) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, @@ -556,7 +546,7 @@ theorem hara_policy_lt_self_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ (hgt : (1 - P.minMPC γ) * (η + P.income z) < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a) : P.policy (a, z) < a := by - have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hβR hpos hcap z ha + have hbound := P.hara_minMPC_mul_le_consumptionFn hγ0 hη hu hfl hκ hβ hpos hcap z ha have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by simp only [resources, max_eq_right (hfl ▸ ha.1 : assetFloor ≤ a)] have hpol : P.policy (a, z) = P.resources (a, z) - P.consumptionFn z a := by @@ -585,7 +575,7 @@ theorem hara_exists_exhaust_of_minMPC {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ have := patience_lt_one (P := P) hγ0 hβR linarith refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ - refine P.hara_policy_lt_self_of_minMPC hγ0 hη hu rfl hκ hβ hβR hpos hcap z₀ + refine P.hara_policy_lt_self_of_minMPC hγ0 hη hu rfl hκ hβ hpos hcap z₀ ⟨le_trans ha₀.le ha.1, ha.2⟩ ?_ have hmono : (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀ ≤ (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a := @@ -627,7 +617,7 @@ theorem crra_exists_exhaust_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr have := patience_lt_one (P := P) hγ0 hβR linarith refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ - refine P.crra_policy_lt_self_of_minMPC hγ0 hu rfl hκ hβ hβR hpos hcap z₀ + refine P.crra_policy_lt_self_of_minMPC hγ0 hu rfl hκ hβ hpos hcap z₀ ⟨le_trans ha₀.le ha.1, ha.2⟩ ?_ have hmono : (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀ ≤ (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a := diff --git a/LeanPool/AiyagariEquilibrium/Topology/Berge.lean b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean index 56fb12178..85c26ef6c 100644 --- a/LeanPool/AiyagariEquilibrium/Topology/Berge.lean +++ b/LeanPool/AiyagariEquilibrium/Topology/Berge.lean @@ -55,7 +55,7 @@ serve the second purpose, where that correspondence may take empty values. ## References * Berge, *Espaces topologiques: Fonctions multivoques*, 1959. -* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*, theorem 3.6. +* Stokey, Lucas and Prescott, *Recursive Methods in Economic Dynamics*. -/ @[expose] public section From 9f2f92ee409357147f52582daa5fef8419d17f1c Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 18:08:46 +0000 Subject: [PATCH 13/19] Use the household discount bound from the model --- .../Equilibrium/AiyagariUniqueness.lean | 36 ++++++++++--------- .../Equilibrium/Bisection.lean | 12 +++---- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index 455e31459..a6ae8541f 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -66,7 +66,7 @@ variable {assetCap : ℝ} (P : IncomeFluctuation Z 0 assetCap) `crra_policy_mono_withRate_of_minMPC` chained by `monotoneOn_of_local`. For log the minimal MPC is `1 - β` at every rate, so every cap condition collapses to the one on `hcap`. -/ theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hε : 0 < ε) (hcap : (P.discount : ℝ) * P.maxIncome @@ -75,6 +75,7 @@ theorem log_policy_mono_rateFamily {rlo rhi ε : ℝ} (hu : P.u = crraUtility 1) ∀ s : ℝ × Z, s.1 ∈ Icc (0 : ℝ) assetCap → (P.rateFamily hrlo hlohi r).policy s ≤ (P.rateFamily hrlo hlohi r').policy s := by + have hβ1 : (P.discount : ℝ) < 1 := P.discount_lt_one intro r hr r' hr' hle s hs have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg have hmono : MonotoneOn (fun x => (P.rateFamily hrlo hlohi x).policy s) @@ -151,7 +152,7 @@ theorem log_corner_of_top {rlo rhi a₀ : ℝ} {z₀ : Z} (hβ : 0 < (P.discount /-- **A unique stationary distribution at every rate in `[0, rhi]`**, for log utility. -/ theorem log_existsUnique_isStationary {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hβ : 0 < (P.discount : ℝ)) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hcap : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -159,6 +160,7 @@ theorem log_existsUnique_isStationary {rlo rhi a₀ : ℝ} (hu : P.u = crraUtili (hcorn : (P.discount : ℝ) * (1 + rhi) * (P.income z₀ + (1 + rhi) * a₀) < P.minIncome) {r : ℝ} (hr : r ∈ Icc rlo rhi) (hrr : P.RateOK r) : ∃! μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ := by + have hβ1 : (P.discount : ℝ) < 1 := P.discount_lt_one have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring @@ -178,7 +180,7 @@ theorem log_existsUnique_isStationary {rlo rhi a₀ : ℝ} (hu : P.u = crraUtili /-- **Convergence to it from any start**, at every rate in `[0, rhi]`. -/ theorem log_tendsto_pushProb {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hβ : 0 < (P.discount : ℝ)) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hcap : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -188,6 +190,7 @@ theorem log_tendsto_pushProb {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) (hu (μ₀ : ProbabilityMeasure P.State) {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : Tendsto (fun m => (P.withRate r hrr).pushProb^[m] μ₀) atTop (𝓝 μ) := by + have hβ1 : (P.discount : ℝ) < 1 := P.discount_lt_one have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring @@ -211,7 +214,7 @@ theorem log_tendsto_pushProb {rlo rhi a₀ : ℝ} (hu : P.u = crraUtility 1) (hu stationary distributions. Light's Theorems 1 and 2. -/ theorem log_monotoneOn_capitalSupply {rlo rhi ε a₀ : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hε : 0 < ε) (hcap : (P.discount : ℝ) * P.maxIncome @@ -228,12 +231,12 @@ theorem log_monotoneOn_capitalSupply {rlo rhi ε a₀ : ℝ} (hu : P.u = crraUti nlinarith refine P.monotoneOn_capitalSupply_of_policy_mono (P.rateFamily hrlo hlohi) (fun r r' z z' => rfl) - (P.log_policy_mono_rateFamily hu hunb hβ hβ1 hrlo hlohi hε hcap) + (P.log_policy_mono_rateFamily hu hunb hβ hrlo hlohi hε hcap) ⟨Measure.dirac (Classical.ofNonempty : P.State), inferInstance⟩ ν ?_ intro r hr have hrr : P.RateOK r := rateOK_of_floor_zero (by linarith [hr.1]) rw [P.rateFamily_eq _ _ hr hrr] - exact P.log_tendsto_pushProb hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle hcorn hr hrr _ + exact P.log_tendsto_pushProb hu hunb hβ hβR hcap' hmono hz₀ hreach ha₀ hle hcorn hr hrr _ (hν r hr hrr) /-! ### Theorem 3: single crossing -/ @@ -244,7 +247,7 @@ have the same rate, under the stated cap, transition and impatience hypotheses. theorem log_equilibriumRate_unique {rlo rhi ε a₀ α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) (hδ : 0 < rlo + δ) (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hε : 0 < ε) (hcap : (P.discount : ℝ) * P.maxIncome @@ -267,7 +270,7 @@ theorem log_equilibriumRate_unique {rlo rhi ε a₀ α δ : ℝ} (hα0 : 0 < α) have hE : ∀ r : ℝ, ∃! μ : ProbabilityMeasure P.State, (P.withRate (clampRate rlo rhi r) (rateOK_of_floor_zero (one_add_clampRate_pos hrlo hlohi r))).IsStationary μ := - fun r => P.log_existsUnique_isStationary hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle + fun r => P.log_existsUnique_isStationary hu hunb hβ hβR hcap' hmono hz₀ hreach ha₀ hle hcorn (clampRate_mem hlohi r) _ set ν : ℝ → ProbabilityMeasure P.State := fun r => (hE r).choose with hνdef have hν : ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, (P.withRate r hrr).IsStationary (ν r) := by @@ -285,7 +288,7 @@ theorem log_equilibriumRate_unique {rlo rhi ε a₀ α δ : ℝ} (hα0 : 0 < α) rw [hid r₁ h₁ hrr₁ μ₁ hμ₁] at he₁ rw [hid r₂ h₂ hrr₂ μ₂ hμ₂] at he₂ exact eq_of_monotoneOn_of_strictAntiOn - (P.log_monotoneOn_capitalSupply hu hunb hβ hβ1 hrlo hlohi hβR hε hcap hmono hz₀ hreach ha₀ hle + (P.log_monotoneOn_capitalSupply hu hunb hβ hrlo hlohi hβR hε hcap hmono hz₀ hreach ha₀ hle hcorn ν hν) (normalisedDemand_strictAntiOn hα0 hα1 hδ) h₁ h₂ he₁ he₂ @@ -299,7 +302,7 @@ the corner level as small as the corner test needs. -/ theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) (hδ : 0 < rlo + δ) (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -345,7 +348,7 @@ theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring rw [this] nlinarith [hkey, hmono, hroom, hminpos] - exact P.log_equilibriumRate_unique hα0 hα1 hδ hu hunb hβ hβ1 hrlo hlohi hβR hε hcapε hmono hz₀ + exact P.log_equilibriumRate_unique hα0 hα1 hδ hu hunb hβ hrlo hlohi hβR hε hcapε hmono hz₀ hreach ha₀ hle hcorn h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ /-! ### Negative rates @@ -358,12 +361,13 @@ is no equilibrium. -/ /-- **The log capital ceiling at any admissible rate** with `β(1+r) < 1`: no sign restriction on `r`, because `κ = 1 - β` (`minMPC_one`). -/ theorem log_aggregateCapital_le {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrr : P.RateOK r) + (hβ : 0 < (P.discount : ℝ)) (hrr : P.RateOK r) (hβR : (P.discount : ℝ) * (1 + r) < 1) (hthr : (P.discount : ℝ) * (P.maxIncome + (1 + r) * assetCap) < assetCap) {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : P.aggregateCapital μ ≤ (P.discount : ℝ) * P.maxIncome / (1 - (P.discount : ℝ) * (1 + r)) := by + have hβ1 : (P.discount : ℝ) < 1 := P.discount_lt_one have hκ : 1 - (P.withRate r hrr).minMPC 1 = (P.discount : ℝ) := by rw [IncomeFluctuation.minMPC_one, IncomeFluctuation.withRate_discount]; ring have hκpos : 0 < (P.withRate r hrr).minMPC 1 := by @@ -391,14 +395,14 @@ theorem log_aggregateCapital_le {r : ℝ} (hu : P.u = crraUtility 1) (hunb : P.U /-- **No equilibrium where the ceiling is below demand.** -/ theorem log_no_equilibrium_of_ceiling {α δ r : ℝ} (hu : P.u = crraUtility 1) - (hunb : P.Unbounded) (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) + (hunb : P.Unbounded) (hβ : 0 < (P.discount : ℝ)) (hrr : P.RateOK r) (hβR : (P.discount : ℝ) * (1 + r) < 1) (hthr : (P.discount : ℝ) * (P.maxIncome + (1 + r) * assetCap) < assetCap) (hD : (P.discount : ℝ) * P.maxIncome / (1 - (P.discount : ℝ) * (1 + r)) < normalisedDemand α δ r) {μ : ProbabilityMeasure P.State} (hμ : (P.withRate r hrr).IsStationary μ) : P.aggregateCapital μ ≠ normalisedDemand α δ r := fun h => by - have := P.log_aggregateCapital_le hu hunb hβ hβ1 hrr hβR hthr hμ + have := P.log_aggregateCapital_le hu hunb hβ hrr hβR hthr hμ linarith /-! ### Normalized clearing with the Aiyagari (1994) log calibration @@ -426,7 +430,7 @@ theorem aiyagari1994_log_equilibriumRate_unique {rlo rhi : ℝ} (he₁ : P.aggregateCapital μ₁ = normalisedDemand (9 / 25) (2 / 25) r₁) (he₂ : P.aggregateCapital μ₂ = normalisedDemand (9 / 25) (2 / 25) r₂) : r₁ = r₂ := P.log_equilibriumRate_unique_of_cap (by norm_num) (by norm_num) (by linarith) hu hunb - (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) + (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) (by rw [hβ]; exact hcap) hmono hz₀ hmin hreach h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ /-- **Two normalized clearing rates in `(-δ, λ)` coincide.** The interval version is applied @@ -459,7 +463,7 @@ theorem aiyagari1994_log_no_equilibrium_of_ceiling {r : ℝ} P.aggregateCapital μ ≠ normalisedDemand (9 / 25) (2 / 25) r := by have hcap0 : (0 : ℝ) ≤ assetCap := P.assetCap_nonneg have hmax : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome - refine P.log_no_equilibrium_of_ceiling hu hunb (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) + refine P.log_no_equilibrium_of_ceiling hu hunb (by rw [hβ]; norm_num) hrr (by rw [hβ]; linarith) ?_ ?_ hμ · rw [hβ] have := mul_nonneg (neg_nonneg.mpr hr0) hcap0 diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean index 3e757e0b1..f1dd1bf00 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -173,7 +173,7 @@ supply along it is monotone, and it is the ONLY stationary distribution at each constants are chosen as in `log_equilibriumRate_unique_of_cap`. -/ theorem log_selection_of_cap {rlo rhi : ℝ} (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -216,16 +216,16 @@ theorem log_selection_of_cap {rlo rhi : ℝ} have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring rw [this] nlinarith [hkey, hmono, hroom, hminpos] - refine ⟨P.log_monotoneOn_capitalSupply hu hunb hβ hβ1 hrlo hlohi hβR hε hcapε hmono hz₀ hreach + refine ⟨P.log_monotoneOn_capitalSupply hu hunb hβ hrlo hlohi hβR hε hcapε hmono hz₀ hreach ha₀ hle hcorn ν hν, fun r hr hrr μ hμ => ?_⟩ - exact (P.log_existsUnique_isStationary hu hunb hβ hβ1 hβR hcap' hmono hz₀ hreach ha₀ hle hcorn + exact (P.log_existsUnique_isStationary hu hunb hβ hβR hcap' hmono hz₀ hreach ha₀ hle hcorn hr hrr).unique hμ (hν r hr hrr) /-- **Bisection on excess supply brackets and converges to the equilibrium rate of the log economy**, along any selection of stationary distributions. -/ theorem log_bisection_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) (hδ : 0 < rlo + δ) (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hβ1 : (P.discount : ℝ) < 1) (hrlo : 0 < 1 + rlo) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) @@ -242,7 +242,7 @@ theorem log_bisection_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < ≤ (rhi - rlo) / 2 ^ (n + 1)) ∧ Tendsto (fun n => (bisect (P.excessSupply ν (normalisedDemand α δ)) rlo rhi n).1) atTop (𝓝 r) := by - obtain ⟨hS, hid⟩ := P.log_selection_of_cap hu hunb hβ hβ1 hrlo hlohi hβR hcap hmono hz₀ hmin + obtain ⟨hS, hid⟩ := P.log_selection_of_cap hu hunb hβ hrlo hlohi hβR hcap hmono hz₀ hmin hreach ν hν have hE : StrictMonoOn (P.excessSupply ν (normalisedDemand α δ)) (Icc rlo rhi) := strictMonoOn_sub_of_monotoneOn_of_strictAntiOn hS (normalisedDemand_strictAntiOn hα0 hα1 hδ) @@ -276,7 +276,7 @@ theorem aiyagari1994_log_bisection {rlo rhi : ℝ} Tendsto (fun n => (bisect (P.excessSupply ν (normalisedDemand (9 / 25) (2 / 25))) rlo rhi n).1) atTop (𝓝 r) := P.log_bisection_of_cap (by norm_num) (by norm_num) (by linarith) hu hunb - (by rw [hβ]; norm_num) (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) + (by rw [hβ]; norm_num) (by linarith) hlohi (by rw [hβ]; linarith) (by rw [hβ]; exact hcap) hmono hz₀ hmin hreach ν hν hr hrr hμ he end IncomeFluctuation From 4b643b8b0476ef778400fb13ece50836f75457a5 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 18:51:20 +0000 Subject: [PATCH 14/19] Share cap calibration witnesses and fixed-point concavity --- .../Equilibrium/AiyagariUniqueness.lean | 54 +++++++++++-------- .../Equilibrium/Bisection.lean | 37 ++----------- .../Models/IncomeFluctuationEnvelope.lean | 4 +- 3 files changed, 36 insertions(+), 59 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean index a6ae8541f..87cad9754 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/AiyagariUniqueness.lean @@ -298,31 +298,20 @@ The interval, the step and the corner level are not primitives of the economy. G the cap clears `2β · maxIncome / (1 - β(1+rhi))`, choose the step as half the room under `1` and the corner level as small as the corner test needs. -/ -/-- **Uniqueness from one inequality on the cap.** -/ -theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) - (hδ : 0 < rlo + δ) - (hu : P.u = crraUtility 1) (hunb : P.Unbounded) - (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) - (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) - (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) - (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) - (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) - {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) - (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) - {μ₁ μ₂ : ProbabilityMeasure P.State} - (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) - (he₁ : P.aggregateCapital μ₁ = normalisedDemand α δ r₁) - (he₂ : P.aggregateCapital μ₂ = normalisedDemand α δ r₂) : r₁ = r₂ := by +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- A slack cap supplies a positive comparison step and a corner level meeting both bounds. -/ +theorem exists_log_cap_corner {R : ℝ} (hβ : 0 < (P.discount : ℝ)) (hR : 0 < R) + (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * R) * assetCap) + {z₀ : Z} (hmin : P.income z₀ = P.minIncome) : + ∃ ε > 0, ∃ a₀ > 0, a₀ ≤ assetCap ∧ + (P.discount : ℝ) * P.maxIncome + + ((P.discount : ℝ) * R + ε) * assetCap < assetCap ∧ + (P.discount : ℝ) * R * (P.income z₀ + R * a₀) < P.minIncome := by set β : ℝ := (P.discount : ℝ) with hβdef - set R : ℝ := 1 + rhi with hRdef - have hR : 0 < R := by rw [hRdef]; linarith [hrlo, hlohi] - have hroom : 0 < 1 - β * R := by rw [hβdef, hRdef]; linarith have hmaxpos : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome - have hcap0 : 0 < assetCap := by - by_contra h - have h' : assetCap ≤ 0 := not_lt.mp h - have : (1 - β * R) * assetCap ≤ 0 := mul_nonpos_of_nonneg_of_nonpos hroom.le h' - nlinarith [hcap, hβ, hmaxpos] + have hproduct : 0 < (1 - β * R) * assetCap := lt_trans (by positivity) hcap + have hroom : 0 < 1 - β * R := pos_of_mul_pos_left hproduct P.assetCap_nonneg + have hcap0 : 0 < assetCap := pos_of_mul_pos_right hproduct hroom.le have hminpos : 0 < P.minIncome := P.minIncome_pos -- the step: half the room under `1` set ε : ℝ := (1 - β * R) / 2 with hεdef @@ -348,6 +337,25 @@ theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring rw [this] nlinarith [hkey, hmono, hroom, hminpos] + exact ⟨ε, hε, a₀, ha₀, hle, hcapε, hcorn⟩ + +/-- **Uniqueness from one inequality on the cap.** -/ +theorem log_equilibriumRate_unique_of_cap {rlo rhi α δ : ℝ} (hα0 : 0 < α) (hα1 : α < 1) + (hδ : 0 < rlo + δ) + (hu : P.u = crraUtility 1) (hunb : P.Unbounded) + (hβ : 0 < (P.discount : ℝ)) (hrlo : 0 < 1 + rlo) + (hlohi : rlo ≤ rhi) (hβR : (P.discount : ℝ) * (1 + rhi) < 1) + (hcap : 2 * (P.discount : ℝ) * P.maxIncome < (1 - (P.discount : ℝ) * (1 + rhi)) * assetCap) + (hmono : P.MonotoneTransitions) {z₀ : Z} (hz₀ : ∀ z : Z, P.income z₀ ≤ P.income z) + (hmin : P.income z₀ = P.minIncome) (hreach : ∀ z, 0 < P.transitionMatrix z z₀) + {r₁ r₂ : ℝ} (h₁ : r₁ ∈ Icc rlo rhi) (h₂ : r₂ ∈ Icc rlo rhi) + (hrr₁ : P.RateOK r₁) (hrr₂ : P.RateOK r₂) + {μ₁ μ₂ : ProbabilityMeasure P.State} + (hμ₁ : (P.withRate r₁ hrr₁).IsStationary μ₁) (hμ₂ : (P.withRate r₂ hrr₂).IsStationary μ₂) + (he₁ : P.aggregateCapital μ₁ = normalisedDemand α δ r₁) + (he₂ : P.aggregateCapital μ₂ = normalisedDemand α δ r₂) : r₁ = r₂ := by + obtain ⟨ε, hε, a₀, ha₀, hle, hcapε, hcorn⟩ := + P.exists_log_cap_corner hβ (R := 1 + rhi) (by linarith only [hrlo, hlohi]) hcap hmin exact P.log_equilibriumRate_unique hα0 hα1 hδ hu hunb hβ hrlo hlohi hβR hε hcapε hmono hz₀ hreach ha₀ hle hcorn h₁ h₂ hrr₁ hrr₂ hμ₁ hμ₂ he₁ he₂ diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean index f1dd1bf00..b9e293668 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/Bisection.lean @@ -183,39 +183,10 @@ theorem log_selection_of_cap {rlo rhi : ℝ} MonotoneOn (fun r => P.aggregateCapital (ν r)) (Icc rlo rhi) ∧ ∀ r ∈ Icc rlo rhi, ∀ hrr : P.RateOK r, ∀ μ : ProbabilityMeasure P.State, (P.withRate r hrr).IsStationary μ → μ = ν r := by - set β : ℝ := (P.discount : ℝ) with hβdef - set R : ℝ := 1 + rhi with hRdef - have hR : 0 < R := by rw [hRdef]; linarith [hrlo, hlohi] - have hroom : 0 < 1 - β * R := by rw [hβdef, hRdef]; linarith - have hmaxpos : 0 < P.maxIncome := lt_of_lt_of_le P.minIncome_pos P.minIncome_le_maxIncome - have hcap0 : 0 < assetCap := by - by_contra h - have h' : assetCap ≤ 0 := not_lt.mp h - have : (1 - β * R) * assetCap ≤ 0 := mul_nonpos_of_nonneg_of_nonpos hroom.le h' - nlinarith [hcap, hβ, hmaxpos] - have hminpos : 0 < P.minIncome := P.minIncome_pos - set ε : ℝ := (1 - β * R) / 2 with hεdef - have hε : 0 < ε := by rw [hεdef]; linarith - set A : ℝ := P.minIncome * (1 - β * R) / (2 * β * R ^ 2) with hAdef - have hA : 0 < A := by rw [hAdef]; positivity - set a₀ : ℝ := min assetCap A with ha₀def - have ha₀ : 0 < a₀ := lt_min hcap0 hA - have hle : a₀ ≤ assetCap := min_le_left _ _ - have ha₀A : a₀ ≤ A := min_le_right _ _ - have hcapε : β * P.maxIncome + (β * R + ε) * assetCap < assetCap := by - rw [hεdef] - nlinarith [hcap] - have hcap' : β * (P.maxIncome + R * assetCap) < assetCap := by - nlinarith [hcap, mul_pos hβ hmaxpos] - have hcorn : β * R * (P.income z₀ + R * a₀) < P.minIncome := by - rw [hmin] - have hkey : β * R * (R * A) = P.minIncome * (1 - β * R) / 2 := by - rw [hAdef]; field_simp; try ring - have hmono : β * R * (R * a₀) ≤ β * R * (R * A) := - mul_le_mul_of_nonneg_left (mul_le_mul_of_nonneg_left ha₀A hR.le) (by positivity) - have : β * R * (P.minIncome + R * a₀) = β * R * P.minIncome + β * R * (R * a₀) := by ring - rw [this] - nlinarith [hkey, hmono, hroom, hminpos] + obtain ⟨ε, hε, a₀, ha₀, hle, hcapε, hcorn⟩ := + P.exists_log_cap_corner hβ (R := 1 + rhi) (by linarith only [hrlo, hlohi]) hcap hmin + have hcap' : (P.discount : ℝ) * (P.maxIncome + (1 + rhi) * assetCap) < assetCap := by + nlinarith only [hcapε, mul_nonneg hε.le P.assetCap_nonneg] refine ⟨P.log_monotoneOn_capitalSupply hu hunb hβ hrlo hlohi hβR hε hcapε hmono hz₀ hreach ha₀ hle hcorn ν hν, fun r hr hrr μ hμ => ?_⟩ exact (P.log_existsUnique_isStationary hu hunb hβ hβR hcap' hmono hz₀ hreach ha₀ hle hcorn diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean index de753f1be..7cc507618 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationEnvelope.lean @@ -170,14 +170,12 @@ theorem hasDerivAt_bellman_of_optimal {v : (ℝ × Z) →ᵇ ℝ} {z : Z} {a a' theorem hasDerivAt_valueFunction (hpc : P.PositiveConsumption) {z : Z} {a du : ℝ} (ha : assetFloor < a) (hacap : a < assetCap) - (hconc : ConcaveOn ℝ (Icc assetFloor assetCap) - fun x => P.toExtended.valueFunction (x, z)) (hint : P.policy (a, z) < P.maxSaving (a, z)) (hu : HasDerivAt P.u du (P.resources (a, z) - P.policy (a, z))) : HasDerivAt (fun x => P.toExtended.valueFunction (x, z)) ((1 + P.interest) * du) a := by simpa only [P.toExtended.bellman_valueFunction] using P.hasDerivAt_bellman_of_optimal ha hacap - (by simpa only [P.toExtended.bellman_valueFunction] using hconc) + (by simpa only [P.toExtended.bellman_valueFunction] using P.concaveOn_valueFunction z) (P.policy_mem (a, z)) (P.policy_optimal (a, z)) hint (P.consumption_policy_pos hpc ⟨ha.le, hacap.le⟩) hu From 5c1956817687b35108fc4e03907c7d0305998d6e Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 19:44:30 +0000 Subject: [PATCH 15/19] Reuse expectation bounds and rate comparison induction --- .../Analysis/RelativeRiskAversion.lean | 18 ++ .../Models/IncomeFluctuationBounds.lean | 10 +- .../IncomeFluctuationCarrollKimball.lean | 8 + .../Models/IncomeFluctuationIterate.lean | 21 +- .../Models/IncomeFluctuationMonotone.lean | 13 +- .../Models/IncomeFluctuationRateStep.lean | 296 +++++++----------- .../Models/IncomeFluctuationRegion.lean | 9 +- 7 files changed, 144 insertions(+), 231 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean index 8126776c8..23738abb9 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean @@ -239,6 +239,24 @@ theorem mul_marginal_le_of_scale {c du : ℝ → ℝ} {s : Set ℝ} field_simp rwa [e1, e2] at hmul +/-- The pivotal inequality on a bounded asset interval, with the asset rescaled by the +ratio of two positive gross returns. -/ +theorem mul_marginal_le_of_ratio {c du : ℝ → ℝ} {cap R₁ R₂ a : ℝ} + (hc : ConcaveOn ℝ (Icc 0 cap) c) (hc0 : 0 < c 0) + (hmono : MonotoneOn c (Icc 0 cap)) + (hrra : MonotoneOn (fun y => y * du y) (Ioi 0)) (hdu : ∀ y ∈ Ioi 0, 0 ≤ du y) + (hR₁ : 0 < R₁) (hR : R₁ ≤ R₂) (ha : a ∈ Ioo 0 cap) : + R₁ * du (c (R₁ / R₂ * a)) ≤ R₂ * du (c a) := by + have hR₂ : 0 < R₂ := lt_of_lt_of_le hR₁ hR + have hscaled : R₁ / R₂ * a ∈ Icc 0 cap := + ⟨mul_nonneg (div_pos hR₁ hR₂).le ha.1.le, + (mul_le_of_le_one_left ha.1.le ((div_le_one hR₂).mpr hR)).trans ha.2.le⟩ + have e₁ : R₁ * (a / R₂) = R₁ / R₂ * a := by ring + have e₂ : R₂ * (a / R₂) = a := mul_div_cancel₀ a hR₂.ne' + simpa only [e₁, e₂] using + mul_marginal_le_of_scale hc ⟨le_rfl, ha.1.le.trans ha.2.le⟩ hc0 hmono hrra hdu + hR₁ hR (div_pos ha.1 hR₂) (by rwa [e₁]) (by rw [e₂]; exact ⟨ha.1.le, ha.2.le⟩) + /-! ### Beyond relative risk aversion one For `u'(c) = c^{-γ}` with `γ > 1`, `c · u'(c) = c^{1-γ}` FALLS, so the second factor above goes diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean index 025062267..91ca0ff0b 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationBounds.lean @@ -49,15 +49,7 @@ variable (P : IncomeFluctuation Z assetFloor assetCap) theorem abs_expectation_le (a : ℝ) (z : Z) : |∑ z', P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| ≤ ‖P.toExtended.valueFunction‖ := by - calc |∑ z', P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| - ≤ ∑ z', |P.transitionMatrix z z' * P.toExtended.valueFunction (a, z')| := - Finset.abs_sum_le_sum_abs _ _ - _ ≤ ∑ z', P.transitionMatrix z z' * ‖P.toExtended.valueFunction‖ := by - refine Finset.sum_le_sum fun z' _ => ?_ - rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] - exact mul_le_mul_of_nonneg_left - (P.toExtended.valueFunction.norm_coe_le_norm _) (P.transitionMatrix_nonneg z z') - _ = ‖P.toExtended.valueFunction‖ := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] + exact P.toExtended.abs_expect_le P.toExtended.valueFunction ((assetFloor, z), a) /-- **The utility of optimal consumption is bounded below**, by a quantity depending only on the sup norm of the value function — not on the state. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean index f7948f01e..5702a656a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationCarrollKimball.lean @@ -127,6 +127,14 @@ theorem concaveSlices_bellman {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices as ConcaveSlices assetFloor assetCap (P.toExtended.bellman v) := fun z => P.concaveOn_bellman v hv z +/-- Bellman iteration preserves concavity from any concave continuation. -/ +theorem concaveSlices_iterate_bellman {v : (ℝ × Z) →ᵇ ℝ} + (hv : ConcaveSlices assetFloor assetCap v) (n : ℕ) : + ConcaveSlices assetFloor assetCap ((P.toExtended.bellman)^[n] v) := by + induction n with + | zero => exact hv + | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + theorem concaveSlices_valueFunction : ConcaveSlices assetFloor assetCap P.toExtended.valueFunction := P.concaveOn_valueFunction diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index afedd6028..3ec56581c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -244,11 +244,8 @@ theorem concaveOn_consumptionFnOf_iterates_of_crra {γ : ℝ} (hpc : P.PositiveC ∀ n : ℕ, ∀ z : Z, ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z) := by set T := P.toExtended.bellman with hTdef - have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact concaveSlices_zero - | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap (T^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := + P.concaveSlices_iterate_bellman concaveSlices_zero · intro n induction n with | zero => exact P.concaveOn_consumptionFnOf_zero @@ -292,11 +289,8 @@ theorem concaveOn_consumptionFn_of_marginal {γ m : ℝ} (hpc : P.PositiveConsum < assetCap) (z : Z) : ConcaveOn ℝ (Icc assetFloor assetCap) (P.consumptionFn z) := by have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap - ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact concaveSlices_zero - | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := + P.concaveSlices_iterate_bellman concaveSlices_zero refine P.concaveOn_consumptionFn_of_crra hpc hγ0 hβ hu (fun n a ha z' => ?_) z have hb := P.policyOf_le_of_marginal_bound (hslices n) hm hmarg ha z' have hn := P.norm_iterate_le n @@ -395,11 +389,8 @@ theorem concaveOn_consumptionFn_of_oscSpread {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 rw [hb, hu] exact marginalInadaOn_Ici_crraUtility hγ0 hγ1 have hslices : ∀ n : ℕ, ConcaveSlices assetFloor assetCap - ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact concaveSlices_zero - | succ k ih => rw [Function.iterate_succ_apply']; exact P.concaveSlices_bellman ih + ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := + P.concaveSlices_iterate_bellman concaveSlices_zero exact P.concaveOn_consumptionFn_of_iterates (P.concaveOn_consumptionFnOf_iterates_of_crra hpc hγ0 hβ hu (fun n a ha z' => P.crra_policyOf_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc (hslices n) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean index 9de1994e3..5319e7c2d 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationMonotone.lean @@ -110,18 +110,7 @@ theorem concaveOn_contOf {v : (ℝ × Z) →ᵇ ℝ} nlinarith [mul_le_mul_of_nonneg_left hz' (P.transitionMatrix_nonneg z z')] theorem abs_contOf_le (v : (ℝ × Z) →ᵇ ℝ) (z : Z) (x : ℝ) : |P.contOf v z x| ≤ ‖v‖ := by - have h1 : |∑ z' : Z, P.transitionMatrix z z' * v (x, z')| - ≤ ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| := Finset.abs_sum_le_sum_abs _ _ - have h2 : ∑ z' : Z, |P.transitionMatrix z z' * v (x, z')| - ≤ ∑ z' : Z, P.transitionMatrix z z' * ‖v‖ := by - refine Finset.sum_le_sum fun z' _ => ?_ - rw [abs_mul, abs_of_nonneg (P.transitionMatrix_nonneg z z')] - exact mul_le_mul_of_nonneg_left - (by simpa [Real.norm_eq_abs] using v.norm_coe_le_norm (x, z')) - (P.transitionMatrix_nonneg z z') - rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] at h2 - simp only [contOf] - linarith + exact P.toExtended.abs_expect_le v ((assetFloor, z), x) /-- The objective in the reals agrees with the extended objective where consumption is positive. -/ diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean index 152992d8a..e6bc44b9c 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRateStep.lean @@ -310,24 +310,10 @@ theorem monotoneOn_bellman_sub_scaled {assetCap : ℝ} (Q : IncomeFluctuation Z refine Q.monotoneOn_bellman_sub_scaled_of_piv ht0 ht1 hderiv hanti hwc hid hbv hbw hintv hintw hposv hposw ?_ intro a ha - have hR : (0 : ℝ) < 1 + Q.interest := Q.interest_gt_neg_one - have hcap : (0 : ℝ) ≤ assetCap := Q.assetFloor_le_assetCap - have hzero : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, hcap⟩ - have ha0 : (0 : ℝ) < a := ha.1 - have hamem : a ∈ Icc (0 : ℝ) assetCap := ⟨ha.1.le, ha.2.le⟩ - have hta0 : (0 : ℝ) < t * a := mul_pos ht0 ha0 - have htale : t * a ≤ a := by nlinarith - have htamem : t * a ∈ Icc (0 : ℝ) assetCap := ⟨hta0.le, le_trans htale hamem.2⟩ - -- Light's pivotal inequality, at `α₁ = t R`, `α₂ = R`, `x = a / R`, applied to economy - -- two's OWN consumption function - have e1 : t * (1 + Q.interest) * (a / (1 + Q.interest)) = t * a := by field_simp - have e2 : (1 + Q.interest) * (a / (1 + Q.interest)) = a := by field_simp - have hpiv := mul_marginal_le_of_scale (c := Q.consumptionFnOf w z) (du := du) hcw hzero - (hposw 0 hzero) hmw hrra hdunn (α₁ := t * (1 + Q.interest)) (α₂ := 1 + Q.interest) - (x := a / (1 + Q.interest)) (by positivity) (by nlinarith) (by positivity) - (by rw [e1]; exact htamem) (by rw [e2]; exact hamem) - rw [e1, e2] at hpiv - exact hpiv + have hR : 0 < 1 + Q.interest := Q.interest_gt_neg_one + simpa only [mul_div_cancel_right₀ _ hR.ne'] using + mul_marginal_le_of_ratio hcw (hposw 0 ⟨le_rfl, Q.assetFloor_le_assetCap⟩) + hmw hrra hdunn (mul_pos ht0 hR) (mul_le_of_le_one_left hR.le ht1) ha /-! ### The step, across rates @@ -335,12 +321,15 @@ Reading the difference back through the budget coincidence turns the previous th statement Light's induction propagates: the Bellman images of two continuations with increasing differences again have increasing differences, at the two rates. -/ -/-- **Light (2018), step 5.** Increasing differences survive the Bellman operator. -/ -theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) - {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} +omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] in +theorem incDiffSlices_refl (v : (ℝ × Z) →ᵇ ℝ) : IncDiffSlices (0 : ℝ) assetCap v v := + fun _ _ _ _ _ _ => le_rfl + +/-- **Light (2018), step 5, from the pivotal inequality as a hypothesis.** -/ +theorem incDiffSlices_bellman_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) + (hr : r₁ ≤ r₂) {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) - (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) - (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hvc : ConcaveSlices (0 : ℝ) assetCap v) (hwc : ConcaveSlices (0 : ℝ) assetCap w) (hposv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < (P.withRate r₂ h₂).consumptionFnOf v z a) @@ -350,16 +339,18 @@ theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r (P.withRate r₂ h₂).policyOf v (a, z) < assetCap) (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf w (a, z) < assetCap) - (hcw : ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) ((P.withRate r₂ h₂).consumptionFnOf w z)) + (hpiv : ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf w z ((1 + r₁) / (1 + r₂) * a)) + ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf w z a)) (hid : IncDiffSlices (0 : ℝ) assetCap v w) : IncDiffSlices (0 : ℝ) assetCap ((P.withRate r₁ h₁).toExtended.bellman v) ((P.withRate r₂ h₂).toExtended.bellman w) := by have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 intro z x y hx hy hxy - have hmono := (P.withRate r₂ h₂).monotoneOn_bellman_sub_scaled + have hmono := (P.withRate r₂ h₂).monotoneOn_bellman_sub_scaled_of_piv (t := (1 + r₁) / (1 + r₂)) (div_pos hR₁ hR₂) (by rw [div_le_one hR₂]; linarith) - (v := v) (w := w) (z := z) (du := du) hderiv hanti hrra hdunn hwc + (v := v) (w := w) (z := z) (du := du) hderiv hanti hwc ((P.withRate r₂ h₂).contOfIncreasingDifferences_of_slices hid) ((P.withRate r₂ h₂).concaveSlices_bellman hvc z) ((P.withRate r₂ h₂).concaveSlices_bellman hwc z) @@ -367,10 +358,12 @@ theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r (hslackv a ha z)) (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposw a ha z) (hslackw a ha z)) - (fun a ha => hposv a ha z) (fun a ha => hposw a ha z) (hcw z) - (fun a ha a' ha' hle => (P.withRate r₂ h₂).consumptionFnOf_mono hwc ha ha' hle) + (fun a ha => hposv a ha z) (fun a ha => hposw a ha z) + (fun a ha => by + rw [IncomeFluctuation.withRate_interest, + show (1 + r₁) / (1 + r₂) * (1 + r₂) = 1 + r₁ from div_mul_cancel₀ _ hR₂.ne'] + exact hpiv z a ha) have hkey := hmono hx hy hxy - -- read the economy-one terms back through the budget coincidence have hcoin : ∀ a ∈ Icc (0 : ℝ) assetCap, ((P.withRate r₁ h₁).toExtended.bellman v) (a, z) = ((P.withRate r₂ h₂).toExtended.bellman v) ((1 + r₁) / (1 + r₂) * a, z) := by @@ -381,6 +374,84 @@ theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r rw [hcoin x hx, hcoin y hy] linarith +/-- **Light (2018) Theorem 1 from the pivotal inequality, the induction started anywhere.** -/ +theorem contIncreasingDifferences_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) + (hr : r₁ ≤ r₂) {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (v₀ : (ℝ × Z) →ᵇ ℝ) + (hv₀ : ConcaveSlices (0 : ℝ) assetCap v₀) + (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) z a) + (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < + (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a) + (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) (a, z) < assetCap) + (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) (a, z) < assetCap) + (hpiv : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, + (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z ((1 + r₁) / (1 + r₂) * a)) + ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a)) : + ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂) := by + have hvs : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) := + (P.withRate r₁ h₁).concaveSlices_iterate_bellman hv₀ + have hws : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := + (P.withRate r₂ h₂).concaveSlices_iterate_bellman hv₀ + have hstep : ∀ n : ℕ, IncDiffSlices (0 : ℝ) assetCap + (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) + (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := by + intro n + induction n with + | zero => exact incDiffSlices_refl v₀ + | succ k ih => + rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] + exact P.incDiffSlices_bellman_withRate_of_piv h₁ h₂ hr hderiv hanti (hvs k) (hws k) + (hposv k) (hposw k) (hslackv k) (hslackw k) (hpiv k) ih + refine contIncreasingDifferences_of_valueFunction _ _ rfl ?_ + intro z x y hx hy hxy + have hlim₁ : Tendsto (fun n => ((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) atTop + (𝓝 (P.withRate r₁ h₁).toExtended.valueFunction) := + (P.withRate r₁ h₁).toExtended.tendsto_iterate_valueFunction v₀ + have hlim₂ : Tendsto (fun n => ((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) atTop + (𝓝 (P.withRate r₂ h₂).toExtended.valueFunction) := + (P.withRate r₂ h₂).toExtended.tendsto_iterate_valueFunction v₀ + have hpt : ∀ (F : ℕ → (ℝ × Z) →ᵇ ℝ) (G : (ℝ × Z) →ᵇ ℝ), Tendsto F atTop (𝓝 G) → + ∀ p : ℝ × Z, Tendsto (fun n => F n p) atTop (𝓝 (G p)) := fun F G hF p => + (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp hF).tendsto_at p + have hev₁ := hpt _ _ hlim₁ + have hev₂ := hpt _ _ hlim₂ + exact le_of_tendsto_of_tendsto ((hev₁ (y, z)).sub (hev₁ (x, z))) + ((hev₂ (y, z)).sub (hev₂ (x, z))) (Eventually.of_forall fun n => hstep n z x y hx hy hxy) + +/-- **Light (2018), step 5.** Increasing differences survive the Bellman operator. -/ +theorem incDiffSlices_bellman_withRate (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) (hr : r₁ ≤ r₂) + {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} + (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) + (hanti : AntitoneOn du (Ioi (0 : ℝ))) (hrra : MonotoneOn (fun y => y * du y) (Ioi (0 : ℝ))) + (hdunn : ∀ y ∈ Ioi (0 : ℝ), 0 ≤ du y) + (hvc : ConcaveSlices (0 : ℝ) assetCap v) (hwc : ConcaveSlices (0 : ℝ) assetCap w) + (hposv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf v z a) + (hposw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + 0 < (P.withRate r₂ h₂).consumptionFnOf w z a) + (hslackv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf v (a, z) < assetCap) + (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + (P.withRate r₂ h₂).policyOf w (a, z) < assetCap) + (hcw : ∀ z : Z, ConcaveOn ℝ (Icc (0 : ℝ) assetCap) ((P.withRate r₂ h₂).consumptionFnOf w z)) + (hid : IncDiffSlices (0 : ℝ) assetCap v w) : + IncDiffSlices (0 : ℝ) assetCap ((P.withRate r₁ h₁).toExtended.bellman v) + ((P.withRate r₂ h₂).toExtended.bellman w) := by + refine P.incDiffSlices_bellman_withRate_of_piv h₁ h₂ hr hderiv hanti hvc hwc + hposv hposw hslackv hslackw ?_ hid + intro z a ha + exact mul_marginal_le_of_ratio (hcw z) + (hposw 0 ⟨le_rfl, P.assetFloor_le_assetCap⟩ z) + (fun x hx y hy hxy => (P.withRate r₂ h₂).consumptionFnOf_mono hwc hx hy hxy) + hrra hdunn h₁.1 (add_le_add le_rfl hr) ha + /-! ### The induction, and the limit `IncDiffSlices` mentions no economy and is closed under pointwise limits, so the step iterates @@ -407,49 +478,14 @@ theorem contIncreasingDifferences_withRate (h₁ : P.RateOK r₁) (h₂ : P.Rate ((P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z)) : ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂) := by - have hvs : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap - (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact concaveSlices_zero - | succ k ih => - rw [Function.iterate_succ_apply'] - exact (P.withRate r₁ h₁).concaveSlices_bellman ih - have hws : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap - (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact concaveSlices_zero - | succ k ih => - rw [Function.iterate_succ_apply'] - exact (P.withRate r₂ h₂).concaveSlices_bellman ih - -- the induction - have hstep : ∀ n : ℕ, IncDiffSlices (0 : ℝ) assetCap - (((P.withRate r₁ h₁).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) - (((P.withRate r₂ h₂).toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) := by - intro n - induction n with - | zero => exact incDiffSlices_zero _ _ - | succ k ih => - rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] - exact P.incDiffSlices_bellman_withRate h₁ h₂ hr hderiv hanti hrra hdunn (hvs k) (hws k) - (hposv k) (hposw k) (hslackv k) (hslackw k) (hcons k) ih - -- and the limit - refine contIncreasingDifferences_of_valueFunction _ _ rfl ?_ - intro z x y hx hy hxy - have hlim₁ : Tendsto (fun n => ((P.withRate r₁ h₁).toExtended.bellman)^[n] - (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 (P.withRate r₁ h₁).toExtended.valueFunction) := - (P.withRate r₁ h₁).toExtended.tendsto_iterate_valueFunction 0 - have hlim₂ : Tendsto (fun n => ((P.withRate r₂ h₂).toExtended.bellman)^[n] - (0 : (ℝ × Z) →ᵇ ℝ)) atTop (𝓝 (P.withRate r₂ h₂).toExtended.valueFunction) := - (P.withRate r₂ h₂).toExtended.tendsto_iterate_valueFunction 0 - have hpt : ∀ (F : ℕ → (ℝ × Z) →ᵇ ℝ) (G : (ℝ × Z) →ᵇ ℝ), Tendsto F atTop (𝓝 G) → - ∀ p : ℝ × Z, Tendsto (fun n => F n p) atTop (𝓝 (G p)) := fun F G hF p => - (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp hF).tendsto_at p - have hev₁ := hpt _ _ hlim₁ - have hev₂ := hpt _ _ hlim₂ - exact le_of_tendsto_of_tendsto ((hev₁ (y, z)).sub (hev₁ (x, z))) - ((hev₂ (y, z)).sub (hev₂ (x, z))) (Eventually.of_forall fun n => hstep n z x y hx hy hxy) + refine P.contIncreasingDifferences_withRate_of_piv h₁ h₂ hr hderiv hanti 0 + concaveSlices_zero hposv hposw hslackv hslackw ?_ + intro n z a ha + exact mul_marginal_le_of_ratio (hcons n z) + (hposw n 0 ⟨le_rfl, P.assetFloor_le_assetCap⟩ z) + (fun x hx y hy hxy => (P.withRate r₂ h₂).consumptionFnOf_mono + ((P.withRate r₂ h₂).concaveSlices_iterate_bellman concaveSlices_zero n) hx hy hxy) + hrra hdunn h₁.1 (add_le_add le_rfl hr) ha /-- **Light (2018) Theorem 1, unconditional on the increasing-differences hypothesis.** A household facing a higher interest rate saves at least as much, at every asset level and every @@ -520,7 +556,7 @@ theorem policy_mono_withRate_crra {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ ≤ 1) ( /-! ### Beyond relative risk aversion one -Everything above used `γ ≤ 1` in one place: the pivotal inequality +The relative-risk-aversion specializations use `γ ≤ 1` in one place: the pivotal inequality `t R u'(c_w(t a)) ≤ R u'(c_w(a))` applied to economy two's consumption function against the CURRENT continuation `w`. Two changes make that inequality a hypothesis one can hope to meet when `γ > 1`. @@ -536,120 +572,6 @@ consumption function, roughly `c₂(a) ≤ (γ/(γ-1)) c₂(0)` for rates close restriction on wealth, not on preferences: for a small enough asset cap, or on an invariant interval, Light's Theorem 1 holds for every `γ`. -/ -omit [Fintype Z] [Nonempty Z] [DiscreteTopology Z] in -theorem incDiffSlices_refl (v : (ℝ × Z) →ᵇ ℝ) : IncDiffSlices (0 : ℝ) assetCap v v := - fun _ _ _ _ _ _ => le_rfl - -/-- **Light (2018), step 5, from the pivotal inequality as a hypothesis.** -/ -theorem incDiffSlices_bellman_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) - (hr : r₁ ≤ r₂) {v w : (ℝ × Z) →ᵇ ℝ} {du : ℝ → ℝ} - (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) - (hanti : AntitoneOn du (Ioi (0 : ℝ))) - (hvc : ConcaveSlices (0 : ℝ) assetCap v) (hwc : ConcaveSlices (0 : ℝ) assetCap w) - (hposv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - 0 < (P.withRate r₂ h₂).consumptionFnOf v z a) - (hposw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - 0 < (P.withRate r₂ h₂).consumptionFnOf w z a) - (hslackv : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - (P.withRate r₂ h₂).policyOf v (a, z) < assetCap) - (hslackw : ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - (P.withRate r₂ h₂).policyOf w (a, z) < assetCap) - (hpiv : ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, - (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf w z ((1 + r₁) / (1 + r₂) * a)) - ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf w z a)) - (hid : IncDiffSlices (0 : ℝ) assetCap v w) : - IncDiffSlices (0 : ℝ) assetCap ((P.withRate r₁ h₁).toExtended.bellman v) - ((P.withRate r₂ h₂).toExtended.bellman w) := by - have hR₁ : (0 : ℝ) < 1 + r₁ := h₁.1 - have hR₂ : (0 : ℝ) < 1 + r₂ := h₂.1 - intro z x y hx hy hxy - have hmono := (P.withRate r₂ h₂).monotoneOn_bellman_sub_scaled_of_piv - (t := (1 + r₁) / (1 + r₂)) (div_pos hR₁ hR₂) (by rw [div_le_one hR₂]; linarith) - (v := v) (w := w) (z := z) (du := du) hderiv hanti hwc - ((P.withRate r₂ h₂).contOfIncreasingDifferences_of_slices hid) - ((P.withRate r₂ h₂).concaveSlices_bellman hvc z) - ((P.withRate r₂ h₂).concaveSlices_bellman hwc z) - (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposv a ha z) - (hslackv a ha z)) - (fun a ha => (P.withRate r₂ h₂).policyOf_lt_maxSaving_of_pos (hposw a ha z) - (hslackw a ha z)) - (fun a ha => hposv a ha z) (fun a ha => hposw a ha z) - (fun a ha => by - rw [IncomeFluctuation.withRate_interest, - show (1 + r₁) / (1 + r₂) * (1 + r₂) = 1 + r₁ from div_mul_cancel₀ _ hR₂.ne'] - exact hpiv z a ha) - have hkey := hmono hx hy hxy - have hcoin : ∀ a ∈ Icc (0 : ℝ) assetCap, - ((P.withRate r₁ h₁).toExtended.bellman v) (a, z) - = ((P.withRate r₂ h₂).toExtended.bellman v) ((1 + r₁) / (1 + r₂) * a, z) := by - intro a ha - rw [(P.withRate r₁ h₁).toExtended.bellman_apply, - (P.withRate r₂ h₂).toExtended.bellman_apply] - exact P.bellmanFn_withRate h₁ h₂ hr v z ha - rw [hcoin x hx, hcoin y hy] - linarith - -/-- **Light (2018) Theorem 1 from the pivotal inequality, the induction started anywhere.** -/ -theorem contIncreasingDifferences_withRate_of_piv (h₁ : P.RateOK r₁) (h₂ : P.RateOK r₂) - (hr : r₁ ≤ r₂) {du : ℝ → ℝ} (hderiv : ∀ c : ℝ, 0 < c → HasDerivAt P.u (du c) c) - (hanti : AntitoneOn du (Ioi (0 : ℝ))) (v₀ : (ℝ × Z) →ᵇ ℝ) - (hv₀ : ConcaveSlices (0 : ℝ) assetCap v₀) - (hposv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < - (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) z a) - (hposw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, 0 < - (P.withRate r₂ h₂).consumptionFnOf (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a) - (hslackv : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf - (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) (a, z) < assetCap) - (hslackw : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, (P.withRate r₂ h₂).policyOf - (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) (a, z) < assetCap) - (hpiv : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Ioo (0 : ℝ) assetCap, - (1 + r₁) * du ((P.withRate r₂ h₂).consumptionFnOf - (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z ((1 + r₁) / (1 + r₂) * a)) - ≤ (1 + r₂) * du ((P.withRate r₂ h₂).consumptionFnOf - (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) z a)) : - ContIncreasingDifferences (P.withRate r₁ h₁) (P.withRate r₂ h₂) := by - have hvs : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap - (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) := by - intro n - induction n with - | zero => exact hv₀ - | succ k ih => - rw [Function.iterate_succ_apply'] - exact (P.withRate r₁ h₁).concaveSlices_bellman ih - have hws : ∀ n : ℕ, ConcaveSlices (0 : ℝ) assetCap - (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := by - intro n - induction n with - | zero => exact hv₀ - | succ k ih => - rw [Function.iterate_succ_apply'] - exact (P.withRate r₂ h₂).concaveSlices_bellman ih - have hstep : ∀ n : ℕ, IncDiffSlices (0 : ℝ) assetCap - (((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) - (((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) := by - intro n - induction n with - | zero => exact incDiffSlices_refl v₀ - | succ k ih => - rw [Function.iterate_succ_apply', Function.iterate_succ_apply'] - exact P.incDiffSlices_bellman_withRate_of_piv h₁ h₂ hr hderiv hanti (hvs k) (hws k) - (hposv k) (hposw k) (hslackv k) (hslackw k) (hpiv k) ih - refine contIncreasingDifferences_of_valueFunction _ _ rfl ?_ - intro z x y hx hy hxy - have hlim₁ : Tendsto (fun n => ((P.withRate r₁ h₁).toExtended.bellman)^[n] v₀) atTop - (𝓝 (P.withRate r₁ h₁).toExtended.valueFunction) := - (P.withRate r₁ h₁).toExtended.tendsto_iterate_valueFunction v₀ - have hlim₂ : Tendsto (fun n => ((P.withRate r₂ h₂).toExtended.bellman)^[n] v₀) atTop - (𝓝 (P.withRate r₂ h₂).toExtended.valueFunction) := - (P.withRate r₂ h₂).toExtended.tendsto_iterate_valueFunction v₀ - have hpt : ∀ (F : ℕ → (ℝ × Z) →ᵇ ℝ) (G : (ℝ × Z) →ᵇ ℝ), Tendsto F atTop (𝓝 G) → - ∀ p : ℝ × Z, Tendsto (fun n => F n p) atTop (𝓝 (G p)) := fun F G hF p => - (BoundedContinuousFunction.tendsto_iff_tendstoUniformly.mp hF).tendsto_at p - have hev₁ := hpt _ _ hlim₁ - have hev₂ := hpt _ _ hlim₂ - exact le_of_tendsto_of_tendsto ((hev₁ (y, z)).sub (hev₁ (x, z))) - ((hev₂ (y, z)).sub (hev₂ (x, z))) (Eventually.of_forall fun n => hstep n z x y hx hy hxy) - /-- **Light (2018) Theorem 1 from the pivotal inequality on economy two's OWN consumption function.** The induction is started at economy two's value function, which its Bellman operator fixes, so the only consumption function the inequality is asked of is the true one at diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean index a5e0d36f6..af5f1aabd 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationRegion.lean @@ -175,14 +175,7 @@ the state and not on the interest rate, which is what the parametric argument ne theorem abs_expect_le (v : (ℝ × Z) →ᵇ ℝ) (p : (ℝ × Z) × ℝ) : |P.toExtended.expect v p| ≤ ‖v‖ := by - calc |P.toExtended.expect v p| - ≤ ∑ z', |P.toExtended.prob z' p * v (P.toExtended.transition z' p)| := - Finset.abs_sum_le_sum_abs _ _ - _ ≤ ∑ z' : Z, P.toExtended.prob z' p * ‖v‖ := by - refine Finset.sum_le_sum fun z' _ => ?_ - rw [abs_mul, abs_of_nonneg (P.toExtended.prob_nonneg z' _)] - exact mul_le_mul_of_nonneg_left (v.norm_coe_le_norm _) (P.toExtended.prob_nonneg z' _) - _ = ‖v‖ := by rw [← Finset.sum_mul, P.toExtended.prob_sum, one_mul] + exact P.toExtended.abs_expect_le v p /-- **Actions worth keeping consume a bounded amount**, for a cutoff `δ` given in advance. From 782071c1b077e51cb10ca5f04d33dc44da44f530 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 20:34:18 +0000 Subject: [PATCH 16/19] Share continuation gain bounds and generalize helper premises --- .../Analysis/DecreasingIncrements.lean | 2 +- .../Analysis/RelativeRiskAversion.lean | 2 +- .../Equilibrium/PositiveCapital.lean | 77 ++++++++++--------- .../Models/CRRAConstants.lean | 54 ++++--------- .../Models/IncomeFluctuationHARA.lean | 50 +++--------- 5 files changed, 69 insertions(+), 116 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean index e25de2159..892a1ecd8 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/DecreasingIncrements.lean @@ -133,7 +133,7 @@ is the consumption function, which is concave on the asset region and nowhere el original statement over `[0, ∞)` would not reach it. Positivity is needed only at the origin and at the far point: concavity carries it to everything in between. -/ theorem ConcaveOn.div_le_div_of_scale {z : ℝ → ℝ} {s : Set ℝ} (hz : ConcaveOn ℝ s z) - (h0 : (0 : ℝ) ∈ s) (hz0 : 0 < z 0) {α₁ α₂ x : ℝ} (h1 : 0 < α₁) (h12 : α₁ ≤ α₂) (hx : 0 < x) + (h0 : (0 : ℝ) ∈ s) (hz0 : 0 < z 0) {α₁ α₂ x : ℝ} (h1 : 0 < α₁) (h12 : α₁ ≤ α₂) (hm2 : α₂ * x ∈ s) (hp2 : 0 < z (α₂ * x)) : α₁ / z (α₁ * x) ≤ α₂ / z (α₂ * x) := by have h2 : 0 < α₂ := lt_of_lt_of_le h1 h12 diff --git a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean index 23738abb9..af185d854 100644 --- a/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean +++ b/LeanPool/AiyagariEquilibrium/Analysis/RelativeRiskAversion.lean @@ -229,7 +229,7 @@ theorem mul_marginal_le_of_scale {c du : ℝ → ℝ} {s : Set ℝ} have hcle : c (α₁ * x) ≤ c (α₂ * x) := hmono hm1 hm2 (mul_le_mul_of_nonneg_right h12 hx.le) -- the two factors, each signed by one of the two hypotheses - have hdiv := hc.div_le_div_of_scale h0 hc0 h1 h12 hx hm2 hp2 + have hdiv := hc.div_le_div_of_scale h0 hc0 h1 h12 hm2 hp2 have hprod := hrra (mem_Ioi.mpr hp1) (mem_Ioi.mpr hp2) hcle have hnn : 0 ≤ c (α₁ * x) * du (c (α₁ * x)) := mul_nonneg hp1.le (hdu _ (mem_Ioi.mpr hp1)) have hmul := mul_le_mul hdiv hprod hnn (div_pos h2 hp2).le diff --git a/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean index 7c1f82de2..ea7e845bc 100644 --- a/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean +++ b/LeanPool/AiyagariEquilibrium/Equilibrium/PositiveCapital.lean @@ -401,6 +401,33 @@ theorem cont_le_of_le (hpc : P.PositiveConsumption) (z : Z) {x y : ℝ} exact mul_le_mul_of_nonneg_left (P.valueFunction_le_of_le hpc hx hy hxy) (P.transitionMatrix_nonneg _ _) +omit [MeasurableSpace Z] [BorelSpace Z] in +/-- A lower bound on the one-period utility gain in one successor state gives a +probability-weighted lower bound on the continuation gain. -/ +theorem cont_sub_ge_of_utility_bound (hpc : P.PositiveConsumption) (z z₀ : Z) + {x y bound : ℝ} (hx : x ∈ Icc assetFloor assetCap) (hy : y ∈ Icc assetFloor assetCap) + (hxy : x ≤ y) + (hbound : ∀ c : ℝ, 0 < c → c ≤ P.income z₀ + (1 + P.interest) * x - assetFloor → + bound ≤ P.u (c + (1 + P.interest) * (y - x)) - P.u c) : + P.transitionMatrix z z₀ * bound ≤ P.cont z y - P.cont z x := by + have hterm : P.transitionMatrix z z₀ * + (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) + ≤ P.cont z y - P.cont z x := by + simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] + refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * + (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) + (fun z' _ => ?_) (Finset.mem_univ z₀) + exact mul_nonneg (P.transitionMatrix_nonneg _ _) + (sub_nonneg.mpr (P.valueFunction_le_of_le hpc hx hy hxy)) + have hc : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx + have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) + ≤ P.income z₀ + (1 + P.interest) * x - assetFloor := by + simp only [consumption, resources, max_eq_right hx.1] + exact sub_le_sub_left (P.policy_mem_region (x, z₀)).1 _ + exact (mul_le_mul_of_nonneg_left + ((hbound _ hc hcle).trans (P.valueFunction_sub_ge hpc hx hy hxy z₀)) + (P.transitionMatrix_nonneg _ _)).trans hterm + omit [MeasurableSpace Z] [BorelSpace Z] in /-- **The continuation's gain between two saving levels, in primitives.** -/ theorem log_cont_sub_ge_gen (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) @@ -412,44 +439,22 @@ theorem log_cont_sub_ge_gen (hfl : assetFloor = 0) (hpc : P.PositiveConsumption) ≤ P.cont z y - P.cont z x := by subst hfl have hRh : 0 ≤ (1 + P.interest) * (y - x) := - mul_nonneg P.interest_gt_neg_one.le (by linarith) - have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by - simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] - have hterm : P.transitionMatrix z z₀ * - (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) - ≤ P.cont z y - P.cont z x := by - rw [hsum] - refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) - (fun z' _ => ?_) (Finset.mem_univ z₀) - exact mul_nonneg (P.transitionMatrix_nonneg _ _) - (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) - have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx - have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by - simp only [resources, max_eq_right hx.1] - have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) - ≤ P.income z₀ + (1 + P.interest) * x := by - simp only [consumption, hresx] - linarith [(P.policy_mem_region (x, z₀)).1] - have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ - rw [hu] at hgain - have hden : 0 < P.income z₀ + (1 + P.interest) * x := lt_of_lt_of_le hc0 hcle + mul_nonneg P.interest_gt_neg_one.le (sub_nonneg.mpr hxy) + refine P.cont_sub_ge_of_utility_bound hpc z z₀ hx hy hxy ?_ + intro c hc hcle + simp only [sub_zero] at hcle + rw [hu] + have hden : 0 < P.income z₀ + (1 + P.interest) * x := lt_of_lt_of_le hc hcle have hfrac : 0 ≤ (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x) := div_nonneg hRh hden.le - have hlog : Real.log (1 + (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x)) - ≤ Real.log (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) - - Real.log (P.consumption (x, z₀) (P.policy (x, z₀))) := by - rw [← Real.log_div (by linarith) hc0.ne'] - refine Real.log_le_log (by linarith) ?_ - rw [le_div_iff₀ hc0] - have hkey : (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x) - * P.consumption (x, z₀) (P.policy (x, z₀)) ≤ (1 + P.interest) * (y - x) := by - rw [div_mul_eq_mul_div, div_le_iff₀ hden] - nlinarith [hRh, hcle] - nlinarith [hkey] - exact le_trans (mul_le_mul_of_nonneg_left (le_trans hlog hgain) - (P.transitionMatrix_nonneg z z₀)) hterm + rw [← Real.log_div (by linarith) hc.ne'] + refine Real.log_le_log (by linarith) ?_ + rw [le_div_iff₀ hc] + have hkey : (1 + P.interest) * (y - x) / (P.income z₀ + (1 + P.interest) * x) * c + ≤ (1 + P.interest) * (y - x) := by + rw [div_mul_eq_mul_div, div_le_iff₀ hden] + exact mul_le_mul_of_nonneg_left hcle hRh + nlinarith only [hkey] omit [MeasurableSpace Z] [BorelSpace Z] in /-- **The continuation's gain from zero, in primitives.** Only the term for income state `z₀` is diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 6a09eff4f..3f79ebae3 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -198,7 +198,7 @@ theorem slopeBound_crraUtility {γ m : ℝ} (hγ : γ ≠ 1) (hm : 0 < m) : simp only [slopeBound, crraSlopeBound, crraUtility_of_ne hγ, hhalf, hexp] field_simp -theorem crraSlopeBound_nonneg {γ m : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hm : 0 < m) : +theorem crraSlopeBound_nonneg {γ m : ℝ} (hγ1 : γ < 1) (hm : 0 < m) : 0 ≤ crraSlopeBound γ m := by have h2 : (1 : ℝ) ≤ (2 : ℝ) ^ (1 - γ) := Real.one_le_rpow (by norm_num : (1 : ℝ) ≤ 2) (by linarith) @@ -276,19 +276,19 @@ theorem cont_sub_le_oscGap (z : Z) (x y : ℝ) : P.cont z x - P.cont z y ≤ P.o noncomputable def crraLipschitz (γ : ℝ) : ℝ := crraSlopeBound γ P.minConsumption * (1 + P.interest) / (1 - P.discount * (1 + P.interest)) -theorem crraLipschitz_nonneg {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) +theorem crraLipschitz_nonneg {γ : ℝ} (hγ1 : γ < 1) (hβR : P.discount * (1 + P.interest) < 1) : 0 ≤ P.crraLipschitz γ := by - have hs := crraSlopeBound_nonneg hγ0 hγ1 P.minConsumption_pos + have hs := crraSlopeBound_nonneg hγ1 P.minConsumption_pos have hr := P.interest_gt_neg_one exact div_nonneg (by positivity) (by linarith) /-- **The value function is Lipschitz with the explicit CES constant.** -/ -theorem crra_valueFunction_lipschitz {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) +theorem crra_valueFunction_lipschitz {γ : ℝ} (hγ1 : γ < 1) (hu : P.u = crraUtility γ) (hβR : P.discount * (1 + P.interest) < 1) : ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ y ∈ Icc (0 : ℝ) assetCap, |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| ≤ P.crraLipschitz γ * |x - y| := by - refine P.valueFunction_lipschitz (P.crraLipschitz_nonneg hγ0 hγ1 hβR) (le_of_eq ?_) + refine P.valueFunction_lipschitz (P.crraLipschitz_nonneg hγ1 hβR) (le_of_eq ?_) have hne : (1 : ℝ) - P.discount * (1 + P.interest) ≠ 0 := by linarith simp only [crraLipschitz, slopeBoundU, hu, slopeBound_crraUtility (show γ ≠ 1 by linarith) P.minConsumption_pos] @@ -302,7 +302,7 @@ theorem crra_policy_eq_zero_of_resources {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < (hβR : P.discount * (1 + P.interest) < 1) {s : ℝ × Z} (hs : s.1 ∈ Icc (0 : ℝ) assetCap) (hlt : P.discount * P.crraLipschitz γ < P.resources s ^ (-γ)) : P.policy s = 0 := - P.policy_eq_zero_of_corner_at (P.crra_valueFunction_lipschitz hγ0 hγ1 hu hβR) hs + P.policy_eq_zero_of_corner_at (P.crra_valueFunction_lipschitz hγ1 hu hβR) hs (fun c d hd hdc hc => by rw [hu] rw [sub_zero] at hc @@ -343,39 +343,15 @@ theorem crra_cont_sub_ge_gen {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) * ((1 + P.interest) * (y - x))) ≤ P.cont z y - P.cont z x := by have hRh : 0 ≤ (1 + P.interest) * (y - x) := - mul_nonneg P.interest_gt_neg_one.le (by linarith) - have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by - simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] - have hterm : P.transitionMatrix z z₀ * - (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) - ≤ P.cont z y - P.cont z x := by - rw [hsum] - refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) - (fun z' _ => ?_) (Finset.mem_univ z₀) - exact mul_nonneg (P.transitionMatrix_nonneg _ _) - (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) - have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx - have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by - simp only [resources, max_eq_right hx.1] - have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) - ≤ P.income z₀ + (1 + P.interest) * x := by - simp only [consumption, hresx] - linarith [(P.policy_mem_region (x, z₀)).1] - have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ - rw [hu] at hgain - -- the marginal bound, at the largest consumption the richer state could reach - have hbound : (P.income z₀ + (1 + P.interest) * y) ^ (-γ) * ((1 + P.interest) * (y - x)) - ≤ crraUtility γ (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) - - crraUtility γ (P.consumption (x, z₀) (P.policy (x, z₀))) := by - have hR : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) - ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith [hcle] - have := crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hc0.le) - (le_add_of_nonneg_right hRh) hR - simpa using this - exact le_trans (mul_le_mul_of_nonneg_left (le_trans hbound hgain) - (P.transitionMatrix_nonneg z z₀)) hterm + mul_nonneg P.interest_gt_neg_one.le (sub_nonneg.mpr hxy) + refine P.cont_sub_ge_of_utility_bound hpc z z₀ hx hy hxy ?_ + intro c hc hcle + simp only [sub_zero] at hcle + rw [hu] + have hR : c + (1 + P.interest) * (y - x) + ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith only [hcle] + simpa using crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hc.le) + (le_add_of_nonneg_right hRh) hR /-- **The continuation's gain from zero, CES version.** -/ theorem crra_cont_sub_ge {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean index 0a8cb72d3..ac0e36b8a 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationHARA.lean @@ -555,45 +555,17 @@ theorem hara_cont_sub_ge_gen {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) ≤ P.cont z y - P.cont z x := by subst hfl have hRh : 0 ≤ (1 + P.interest) * (y - x) := - mul_nonneg P.interest_gt_neg_one.le (by linarith) - have hsum : P.cont z y - P.cont z x = ∑ z' : Z, P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z')) := by - simp only [cont, ← Finset.sum_sub_distrib, ← mul_sub] - have hterm : P.transitionMatrix z z₀ * - (P.toExtended.valueFunction (y, z₀) - P.toExtended.valueFunction (x, z₀)) - ≤ P.cont z y - P.cont z x := by - rw [hsum] - refine Finset.single_le_sum (f := fun z' : Z => P.transitionMatrix z z' * - (P.toExtended.valueFunction (y, z') - P.toExtended.valueFunction (x, z'))) - (fun z' _ => ?_) (Finset.mem_univ z₀) - exact mul_nonneg (P.transitionMatrix_nonneg _ _) - (by linarith [P.valueFunction_le_of_le hpc (z := z') hx hy hxy]) - have hc0 : 0 < P.consumption (x, z₀) (P.policy (x, z₀)) := P.consumption_policy_pos hpc hx - have hresx : P.resources (x, z₀) = P.income z₀ + (1 + P.interest) * x := by - simp only [resources, max_eq_right hx.1] - have hcle : P.consumption (x, z₀) (P.policy (x, z₀)) - ≤ P.income z₀ + (1 + P.interest) * x := by - simp only [consumption, hresx] - linarith [(P.policy_mem_region (x, z₀)).1] - have hgain := P.valueFunction_sub_ge hpc hx hy hxy z₀ - rw [hu] at hgain - have hbound : (η + P.income z₀ + (1 + P.interest) * y) ^ (-γ) * ((1 + P.interest) * (y - x)) - ≤ haraUtility γ η (P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x)) - - haraUtility γ η (P.consumption (x, z₀) (P.policy (x, z₀))) := by - have hR : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) - ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith [hcle] - have hstep := haraUtility_marginal_bound_pos (γ := γ) (η := η) - (R := P.income z₀ + (1 + P.interest) * y) hγ0 (by linarith) - (le_add_of_nonneg_right hRh) hR - have he : η + (P.income z₀ + (1 + P.interest) * y) - = η + P.income z₀ + (1 + P.interest) * y := by ring - rw [he] at hstep - have he2 : P.consumption (x, z₀) (P.policy (x, z₀)) + (1 + P.interest) * (y - x) - - P.consumption (x, z₀) (P.policy (x, z₀)) = (1 + P.interest) * (y - x) := by ring - rw [he2] at hstep - exact hstep - exact le_trans (mul_le_mul_of_nonneg_left (le_trans hbound hgain) - (P.transitionMatrix_nonneg z z₀)) hterm + mul_nonneg P.interest_gt_neg_one.le (sub_nonneg.mpr hxy) + refine P.cont_sub_ge_of_utility_bound hpc z z₀ hx hy hxy ?_ + intro c hc hcle + simp only [sub_zero] at hcle + rw [hu] + have hR : c + (1 + P.interest) * (y - x) + ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith only [hcle] + have hstep := haraUtility_marginal_bound_pos (γ := γ) (η := η) + (R := P.income z₀ + (1 + P.interest) * y) hγ0 (by linarith) + (le_add_of_nonneg_right hRh) hR + simpa only [add_assoc, add_sub_cancel_left] using hstep /-- **Saving is bounded below**, for shifted CRRA, when the gain beats the cost. -/ theorem hara_policy_ge_of_gain {γ η : ℝ} (hγ : 0 < γ) (hη : 0 ≤ η) From cf808112aceaaa5dc0de3a334a23be9caade22f4 Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 21:28:15 +0000 Subject: [PATCH 17/19] Generalize CRRA saving bounds and reuse shifted MPC proofs --- .../Models/CRRAConstants.lean | 44 ++--- .../Models/IncomeFluctuationIterate.lean | 12 +- .../Models/MinimalMPC.lean | 186 +++++++----------- 3 files changed, 98 insertions(+), 144 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 3f79ebae3..e1b47bb97 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -9,7 +9,7 @@ public import LeanPool.AiyagariEquilibrium.Models.BoundedIncomeFluctuation public import LeanPool.AiyagariEquilibrium.Models.NaturalAssetBound /-! -# The quantitative constants for CES +# Quantitative constants for CRRA households The equilibrium programme runs on four explicit constants: a lower bound on the secant slope of `u` (which drives the corner condition and the precautionary gain), an upper bound on it (which @@ -336,7 +336,7 @@ theorem crra_exists_decline {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hb : P.B /-- **The continuation's gain between two saving levels, CES version.** Where the log form carries `log (1 + R·(y-x) / (income + R·x))`, this carries `(income + R·y) ^ (-γ) · R·(y-x)`: the extra resources, valued at the marginal utility of the largest consumption they could buy. -/ -theorem crra_cont_sub_ge_gen {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) +theorem crra_cont_sub_ge_gen {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z z₀ : Z) {x y : ℝ} (hx : x ∈ Icc (0 : ℝ) assetCap) (hy : y ∈ Icc (0 : ℝ) assetCap) (hxy : x ≤ y) : P.transitionMatrix z z₀ * ((P.income z₀ + (1 + P.interest) * y) ^ (-γ) @@ -350,18 +350,18 @@ theorem crra_cont_sub_ge_gen {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) rw [hu] have hR : c + (1 + P.interest) * (y - x) ≤ P.income z₀ + (1 + P.interest) * y := by nlinarith only [hcle] - simpa using crra_marginal_bound_Ici hγ0 hγ1 (mem_Ici.mpr hc.le) + simpa using crra_marginal_bound hγ0 hc (le_add_of_nonneg_right hRh) hR /-- **The continuation's gain from zero, CES version.** -/ -theorem crra_cont_sub_ge {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) +theorem crra_cont_sub_ge {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (z z₀ : Z) {h : ℝ} (hh : h ∈ Icc (0 : ℝ) assetCap) : P.transitionMatrix z z₀ * ((P.income z₀ + (1 + P.interest) * h) ^ (-γ) * ((1 + P.interest) * h)) ≤ P.cont z h - P.cont z 0 := by have h0 : (0 : ℝ) ∈ Icc (0 : ℝ) assetCap := ⟨le_rfl, P.assetCap_nonneg⟩ - have := P.crra_cont_sub_ge_gen hγ0 hγ1 hu hpc z z₀ h0 hh hh.1 + have := P.crra_cont_sub_ge_gen hγ0 hu hpc z z₀ h0 hh hh.1 simpa using this /-! ### The sharp decline constant @@ -377,7 +377,7 @@ Taking `θ` near 1 makes the right-hand side nearly `(β · oscGap) c ^ γ`, pro consumption, which is the shape the decline condition needs. -/ /-- **The proportional CES bound on saving.** -/ -theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) +theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) @@ -386,9 +386,9 @@ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : have h := P.policyOf_le_mul_of_power_marginal (γ := γ) (η := 0) hθ0 hθ1 P.concaveOn_valueFunction P.oscGap_nonneg (by simpa only [oscSpread, oscGap] using P.oscOn_valueFunction) - (fun c d hc hcd _ => by + (fun c d _ hcd hpositive => by simpa only [zero_add, hu] using - crra_marginal_bound_Ici hγ0 hγ1 (P.nonneg_of_mem_dom hc) hcd (le_refl d)) ha z + crra_marginal_bound hγ0 (by simpa only [zero_add] using hpositive) hcd (le_refl d)) ha z (by simpa only [zero_add, P.consumptionFnOf_valueFunction] using P.consumptionFn_pos hpc ha z) simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using h @@ -397,7 +397,7 @@ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`; with `θ` near one the second factor is close to `income z ^ γ`, so the threshold shrinks with the income of the state -- which is exactly what lets the corner condition reach it. -/ -theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) +theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hθr : θ ≤ 1 + P.interest) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) (hlt : (P.discount * P.oscGap / θ) @@ -409,7 +409,7 @@ theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 have hβ : (0 : ℝ) ≤ (P.discount : ℝ) := P.discount.coe_nonneg set b : ℝ := P.policy (a, z) with hbdef set c : ℝ := P.consumption (a, z) b with hcdef - have hkey := P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc ha z + have hkey := P.crra_policy_le_mul hγ0 hθ0 hθ1 hu hpc ha z rw [← hbdef, ← hcdef] at hkey have hres : P.resources (a, z) = P.income z + (1 + P.interest) * a := by simp only [resources, max_eq_right ha.1] @@ -440,14 +440,12 @@ theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 saving reaches the cap, so the cap is the obstruction to Light's uniqueness argument, not the utility class. It can be removed by calibration rather than by weakening the theory: run `crra_policy_le_mul` by contradiction. If saving reached the cap then consumption would be at -most `maxIncome + (1 + r - θ) · assetCap`, and the saving bound evaluated THERE has to exceed -the cap. +most `maxIncome + (1 + r - θ) · assetCap`. The displayed inequality makes the saving upper +bound at that quantity strictly smaller than the cap. -The point of doing it this way is that the bound does not degrade as the cap grows -- the -right-hand side rises with `assetCap` while the left rises only through `oscGap` -- so raising -the cap always eventually works. A cruder estimate that keeps `maxConsumption ^ γ` on the left -grows on both sides and closes the window for `γ` near one, which is an artefact of the -estimate. +The criterion evaluates the proportional bound at the consumption quantity implied by cap +binding. The theorem retains the displayed numerical inequality as an explicit calibration +hypothesis. Related literature: Marcet, Obiols-Homs and Weil (2007), *Incomplete Markets, Labor Supply and Capital Accumulation*. The sufficient criterion below is specific to this capped household @@ -455,7 +453,7 @@ model: it uses the displayed quantitative inequality and positive optimal consumption. Its fixed-point and iterative versions share the same comparison argument. -/ /-- **Saving never reaches the asset cap.** -/ -theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) +theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (hlt : (P.discount * P.oscGap / θ) @@ -469,7 +467,7 @@ theorem crra_policy_lt_assetCap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h (by simpa only [zero_add, P.consumptionFnOf_valueFunction] using (P.consumptionFn_pos hpc ha z).le) (by simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using - P.crra_policy_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc ha z) + P.crra_policy_le_mul hγ0 hθ0 hθ1 hu hpc ha z) (by simpa only [zero_add, mul_zero, sub_zero] using hlt) simpa only [P.policyOf_valueFunction] using h @@ -478,14 +476,14 @@ hypothesis of `not_concaveOn_consumptionFn_of_cap_binds` -- a flat stretch of th top of the asset region -- cannot occur, so nothing in this development contradicts concavity of the consumption function. That is not a proof of concavity; it is the removal of the obstruction that made one impossible. -/ -theorem no_flat_at_cap {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) +theorem no_flat_at_cap {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) (hlt : (P.discount * P.oscGap / θ) * (P.maxIncome + (1 + P.interest - θ) * assetCap) ^ γ < assetCap) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) : P.policy (a, z) ≠ assetCap := - ne_of_lt (P.crra_policy_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc hlt ha z) + ne_of_lt (P.crra_policy_lt_assetCap hγ0 hθ0 hθ1 hu hpc hlt ha z) /-- **A quantitative CES saving floor**, the form the sign-change argument consumes. -/ theorem crra_policy_ge_of_gain {γ : ℝ} (hγ : 0 < γ) (hu : P.u = crraUtility γ) @@ -506,7 +504,7 @@ variable [MeasurableSpace Z] [BorelSpace Z] `log (m / (m - h)) < β · P z₁ z₀ · log (1 + R h / income z₀)` becomes `(m - h) ^ (-γ) · h < β · P z₁ z₀ · (income z₀ + R h) ^ (-γ) · R h`: the saved unit priced at the margin where consumption is lowest, against its return in the bad state. -/ -theorem crra_aggregateCapital_pos_of_primitives {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) +theorem crra_aggregateCapital_pos_of_primitives {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {μ : ProbabilityMeasure P.State} (hμ : P.IsStationary μ) {z₁ z₀ : Z} {p₀ : ℝ} (hp0 : 0 < p₀) (hp : ∀ z, p₀ ≤ P.transitionMatrix z z₁) @@ -519,7 +517,7 @@ theorem crra_aggregateCapital_pos_of_primitives {γ : ℝ} (hγ0 : 0 < γ) (hγ1 (lt_of_lt_of_le hgain ?_) · rw [hu] simpa using crra_cost_of_saving hγ0 hh0 hhinc hm (le_refl (0 : ℝ)) hh0.le - · exact mul_le_mul_of_nonneg_left (P.crra_cont_sub_ge hγ0 hγ1 hu hpc z₁ z₀ ⟨hh0.le, hhcap⟩) + · exact mul_le_mul_of_nonneg_left (P.crra_cont_sub_ge hγ0 hu hpc z₁ z₀ ⟨hh0.le, hhcap⟩) P.discount.coe_nonneg end Measure diff --git a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean index 3ec56581c..29616dade 100644 --- a/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean +++ b/LeanPool/AiyagariEquilibrium/Models/IncomeFluctuationIterate.lean @@ -341,7 +341,7 @@ where it lands. The bound then involves the continuation's OSCILLATION, not its proportional to consumption rather than constant. -/ /-- **The proportional CES bound on saving, against an arbitrary continuation.** -/ -theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) (hθ1 : θ < 1) +theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumptionAll) {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) (hosc : P.OscOn v G) {a : ℝ} (ha : a ∈ Icc assetFloor assetCap) (z : Z) : @@ -350,15 +350,15 @@ theorem crra_policyOf_le_mul {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (h * (P.consumptionFnOf v z a + (1 - θ) * (P.policyOf v (a, z) - assetFloor)) ^ γ := by simpa only [zero_add] using P.policyOf_le_mul_of_power_marginal (γ := γ) (η := 0) - hθ0 hθ1 hv hG hosc (fun c d hc hcd _ => by + hθ0 hθ1 hv hG hosc (fun c d _ hcd hpositive => by simpa only [zero_add, hu] using - crra_marginal_bound_Ici hγ0 hγ1 (P.nonneg_of_mem_dom hc) hcd (le_refl d)) ha z + crra_marginal_bound hγ0 (by simpa only [zero_add] using hpositive) hcd (le_refl d)) ha z (by simpa only [zero_add] using hpc v hv z a ha) /-- **Saving never reaches the asset cap, against an arbitrary continuation.** The hypothesis is the one `crra_policy_lt_assetCap` carries at the fixed point, so the calibrations already proved for the CES witnesses discharge it. -/ -theorem crra_policyOf_lt_assetCap {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1) (hθ0 : 0 < θ) +theorem crra_policyOf_lt_assetCap {γ θ G : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumptionAll) {v : (ℝ × Z) →ᵇ ℝ} (hv : ConcaveSlices assetFloor assetCap v) (hG : 0 ≤ G) (hosc : P.OscOn v G) @@ -371,7 +371,7 @@ theorem crra_policyOf_lt_assetCap {γ θ G : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < 1 (show 0 ≤ (P.discount : ℝ) * G / θ from div_nonneg (mul_nonneg P.discount.coe_nonneg hG) hθ0.le) ha z (by simpa only [zero_add] using (hpc v hv z a ha).le) ?_ ?_ - · simpa only [zero_add] using P.crra_policyOf_le_mul hγ0 hγ1 hθ0 hθ1 hu hpc hv hG hosc ha z + · simpa only [zero_add] using P.crra_policyOf_le_mul hγ0 hθ0 hθ1 hu hpc hv hG hosc ha z · simpa only [zero_add] using hlt /-- **Carroll and Kimball for the bounded CES family, at the witnesses' own calibration.** The @@ -393,7 +393,7 @@ theorem concaveOn_consumptionFn_of_oscSpread {γ θ : ℝ} (hγ0 : 0 < γ) (hγ1 P.concaveSlices_iterate_bellman concaveSlices_zero exact P.concaveOn_consumptionFn_of_iterates (P.concaveOn_consumptionFnOf_iterates_of_crra hpc hγ0 hβ hu - (fun n a ha z' => P.crra_policyOf_lt_assetCap hγ0 hγ1 hθ0 hθ1 hu hpc (hslices n) + (fun n a ha z' => P.crra_policyOf_lt_assetCap hγ0 hθ0 hθ1 hu hpc (hslices n) P.oscSpread_nonneg (P.oscOn_iterate n) hlt ha z')) z /-! ### A slope bound from the OSCILLATION rather than the level diff --git a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean index 67ebe8c7a..6f03bfec3 100644 --- a/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean +++ b/LeanPool/AiyagariEquilibrium/Models/MinimalMPC.lean @@ -234,21 +234,30 @@ theorem crra_minMPC_step {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) (hu.trans (haraUtility_zero_shift γ).symm) hfl hκ hβ hposv hcapv hposW (fun z' a' ha' => by simpa only [zero_add] using ih z' a' ha') z ha -/-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, -uniformly in the horizon — so it passes to the value function. +/-! ### The shifted-CRRA version -The hypotheses beyond CRRA are the two the Euler inequality needs and nothing else: consumption -positive at each iterate, and the saving cap slack there. Both are fields (or immediate -consequences of fields) of `Calibrated`. -/ -theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) +With `u'(c) = (η + c)^(-γ)` the same argument runs on `η + c` against `η + m`: + + `κ (η + m) ≤ η + c(m)`, + +with the SAME `κ`. The step produces `c ≥ κ m + η(κ - Þ)/R`, and what has to be checked is that +this is at least `κ m - η(1-κ)`, which holds because `κ ≥ 0`. So the subsistence level costs +nothing in the constant; it only shifts what the constant multiplies. + +Read as a bound on saving this is `policy ≤ (1-κ)(η + m)`, so the household behaves like a CRRA +household with income raised by `η` — which is what a subsistence level is. -/ + +/-- **The minimal-MPC bound for shifted CRRA, at every iterate.** -/ +theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, - P.minMPC γ * P.resources (a, z) - ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + P.minMPC γ * (η + P.resources (a, z)) + ≤ η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by subst hfl have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) intro n @@ -262,31 +271,26 @@ theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) rw [hz]; simp [consumption] rw [heq] - nlinarith only [hres, hκ1] + nlinarith only [hres, hκ1, hη] | succ k ih => intro z a ha - have hiter : (P.toExtended.bellman)^[k + 1] (0 : (ℝ × Z) →ᵇ ℝ) - = P.toExtended.bellman ((P.toExtended.bellman)^[k] (0 : (ℝ × Z) →ᵇ ℝ)) := - Function.iterate_succ_apply' _ _ _ - rw [hiter] - exact P.crra_minMPC_step hγ0 hu rfl hκ hβ (hpos k) (hcap k) - (fun z' b hb => by have := hpos (k + 1) z' b hb; rwa [hiter] at this) ih z ha - -/-! ### At the fixed point, and what it buys - -The bound is uniform in the horizon, so it survives the limit: `tendsto_policyOf` says argmaxes -follow uniform limits when the limit has concave slices, which the value function does. -/ + rw [Function.iterate_succ_apply'] + exact P.hara_minMPC_step hγ0 hη hu rfl hκ hβ (hpos k) (hcap k) + (fun z' b hb => by + have h := hpos (k + 1) z' b hb + simpa only [Function.iterate_succ_apply'] using h) ih z ha -/-- **The minimal-MPC bound at the value function.** -/ -theorem crra_minMPC_mul_le_consumptionFn {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) +/-- **The shifted-CRRA bound at the value function.** -/ +theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) + (hu : P.u = haraUtility γ η) (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : - P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := by - have hiter := P.crra_minMPC_mul_le_consumptionFnOf hγ0 hu hfl hκ hβ hpos hcap + P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFn z a := by + have hiter := P.hara_minMPC_mul_le_consumptionFnOf hγ0 hη hu hfl hκ hβ hpos hcap have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha have htend : Tendsto (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop @@ -294,14 +298,49 @@ theorem crra_minMPC_mul_le_consumptionFn {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = P.tendsto_policyOf (P.toExtended.tendsto_iterate_valueFunction 0) P.concaveSlices_valueFunction hmem have htendc : Tendsto - (fun n => P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop - (𝓝 (P.consumptionFn z a)) := by + (fun n => η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop + (𝓝 (η + P.consumptionFn z a)) := by have hcf : P.consumptionFn z a = P.resources (a, z) - P.policy (a, z) := rfl - rw [hcf] - simpa only [consumptionFnOf, consumption] using (tendsto_const_nhds (x := P.resources (a, z)) - (f := atTop (α := ℕ))).sub htend + rw [hcf, ← add_sub_assoc] + simpa only [consumptionFnOf, consumption, ← add_sub_assoc] using + (tendsto_const_nhds (x := η + P.resources (a, z)) (f := atTop (α := ℕ))).sub htend exact ge_of_tendsto htendc (Filter.Eventually.of_forall fun n => hiter n z a ha) +/-- **The minimal-MPC bound, at every iterate.** Consumption is at least `κ` times cash on hand, +uniformly in the horizon — so it passes to the value function. + +The hypotheses beyond CRRA are the two the Euler inequality needs and nothing else: consumption +positive at each iterate, and the saving cap slack there. Both are fields (or immediate +consequences of fields) of `Calibrated`. -/ +theorem crra_minMPC_mul_le_consumptionFnOf {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : + ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + P.minMPC γ * P.resources (a, z) + ≤ P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by + simpa only [zero_add] using P.hara_minMPC_mul_le_consumptionFnOf hγ0 (le_refl (0 : ℝ)) + (hu.trans (haraUtility_zero_shift γ).symm) hfl hκ hβ hpos hcap + +/-! ### At the fixed point, and what it buys + +The bound is uniform in the horizon, so it survives the limit: `tendsto_policyOf` says argmaxes +follow uniform limits when the limit has concave slices, which the value function does. -/ + +/-- **The minimal-MPC bound at the value function.** -/ +theorem crra_minMPC_mul_le_consumptionFn {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crraUtility γ) + (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) + (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, + 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) + (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, + P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) + (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : + P.minMPC γ * P.resources (a, z) ≤ P.consumptionFn z a := by + simpa only [zero_add] using P.hara_minMPC_mul_le_consumptionFn hγ0 (le_refl (0 : ℝ)) + (hu.trans (haraUtility_zero_shift γ).symm) hfl hκ hβ hpos hcap z ha + /-- **The decline condition, from primitives.** Above an explicit asset level the household runs its assets down, because it consumes at least the share `κ` of cash on hand and `(1-κ)R = Þ < 1`. This is the hypothesis `hdecl` of `exists_exhaust_of_decline`, which the @@ -422,78 +461,6 @@ theorem crra_policy_lt_cap_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crra P.crra_minMPC_mul_le_consumptionFn_of_cap hγ0 hu hfl hκ hβ hpos hthr z' hb) ha z -/-! ### The shifted-CRRA version - -With `u'(c) = (η + c)^(-γ)` the same argument runs on `η + c` against `η + m`: - - `κ (η + m) ≤ η + c(m)`, - -with the SAME `κ`. The step produces `c ≥ κ m + η(κ - Þ)/R`, and what has to be checked is that -this is at least `κ m - η(1-κ)`, which holds because `κ ≥ 0`. So the subsistence level costs -nothing in the constant; it only shifts what the constant multiplies. - -Read as a bound on saving this is `policy ≤ (1-κ)(η + m)`, so the household behaves like a CRRA -household with income raised by `η` — which is what a subsistence level is. -/ - -/-- **The minimal-MPC bound for shifted CRRA, at every iterate.** -/ -theorem hara_minMPC_mul_le_consumptionFnOf {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) - (hu : P.u = haraUtility γ η) - (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, - 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) - (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) : - ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, - P.minMPC γ * (η + P.resources (a, z)) - ≤ η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a := by - subst hfl - have hκ1 : P.minMPC γ ≤ 1 := minMPC_le_one (P := P) - intro n - induction n with - | zero => - intro z a ha - have hz : P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z) = 0 := P.policyOf_zero (s := (a, z)) ha - have hres : (0 : ℝ) ≤ P.resources (a, z) := (P.assetFloor_lt_resources (a, z)).le - have heq : P.consumptionFnOf ((P.toExtended.bellman)^[0] (0 : (ℝ × Z) →ᵇ ℝ)) z a - = P.resources (a, z) := by - change P.consumption (a, z) (P.policyOf (0 : (ℝ × Z) →ᵇ ℝ) (a, z)) = P.resources (a, z) - rw [hz]; simp [consumption] - rw [heq] - nlinarith only [hres, hκ1, hη] - | succ k ih => - intro z a ha - rw [Function.iterate_succ_apply'] - exact P.hara_minMPC_step hγ0 hη hu rfl hκ hβ (hpos k) (hcap k) - (fun z' b hb => by - have h := hpos (k + 1) z' b hb - simpa only [Function.iterate_succ_apply'] using h) ih z ha - -/-- **The shifted-CRRA bound at the value function.** -/ -theorem hara_minMPC_mul_le_consumptionFn {γ η : ℝ} (hγ0 : 0 < γ) (hη : 0 ≤ η) - (hu : P.u = haraUtility γ η) - (hfl : assetFloor = 0) (hκ : 0 < P.minMPC γ) (hβ : 0 < (P.discount : ℝ)) - (hpos : ∀ n : ℕ, ∀ z : Z, ∀ a ∈ Icc (0 : ℝ) assetCap, - 0 < P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) - (hcap : ∀ n : ℕ, ∀ a ∈ Icc (0 : ℝ) assetCap, ∀ z : Z, - P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z) < assetCap) - (z : Z) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) : - P.minMPC γ * (η + P.resources (a, z)) ≤ η + P.consumptionFn z a := by - have hiter := P.hara_minMPC_mul_le_consumptionFnOf hγ0 hη hu hfl hκ hβ hpos hcap - have hmem : ((a, z) : ℝ × Z).1 ∈ Icc assetFloor assetCap := by rw [hfl]; exact ha - have htend : Tendsto - (fun n => P.policyOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) (a, z)) atTop - (𝓝 (P.policy (a, z))) := - P.tendsto_policyOf (P.toExtended.tendsto_iterate_valueFunction 0) - P.concaveSlices_valueFunction hmem - have htendc : Tendsto - (fun n => η + P.consumptionFnOf ((P.toExtended.bellman)^[n] (0 : (ℝ × Z) →ᵇ ℝ)) z a) atTop - (𝓝 (η + P.consumptionFn z a)) := by - have hcf : P.consumptionFn z a = P.resources (a, z) - P.policy (a, z) := rfl - rw [hcf, ← add_sub_assoc] - simpa only [consumptionFnOf, consumption, ← add_sub_assoc] using - (tendsto_const_nhds (x := η + P.resources (a, z)) (f := atTop (α := ℕ))).sub htend - exact ge_of_tendsto htendc (Filter.Eventually.of_forall fun n => hiter n z a ha) - /-- **The share form.** `κ(η + m) ≤ η + c` is not a bound on the SHARE `c/m`, because of the constant. Charging the constant against the smallest cash on hand there can be gives one: `1 - (1-κ)(1 + η/minIncome)` is a share the household always consumes. It is what the capital @@ -609,20 +576,9 @@ theorem crra_exists_exhaust_of_minMPC {γ : ℝ} (hγ0 : 0 < γ) (hu : P.u = crr (hthr : (1 - P.minMPC γ) * P.income z₀ < (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀) : ∃ N : ℕ, (P.gBad z₀)^[N] P.topState = P.botState := by - subst hfl - have hR : (0 : ℝ) < 1 + P.interest := P.interest_gt_neg_one - -- the decline threshold only has to be cleared once: the gap grows with assets - have hslope : (0 : ℝ) < 1 - (1 - P.minMPC γ) * (1 + P.interest) := by - rw [one_sub_minMPC_mul (P := P)] - have := patience_lt_one (P := P) hγ0 hβR - linarith - refine P.exists_exhaust_of_decline ha₀ hle hzero fun a ha => ?_ - refine P.crra_policy_lt_self_of_minMPC hγ0 hu rfl hκ hβ hpos hcap z₀ - ⟨le_trans ha₀.le ha.1, ha.2⟩ ?_ - have hmono : (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a₀ - ≤ (1 - (1 - P.minMPC γ) * (1 + P.interest)) * a := - mul_le_mul_of_nonneg_left ha.1 hslope.le - linarith + exact P.hara_exists_exhaust_of_minMPC hγ0 (le_refl (0 : ℝ)) + (hu.trans (haraUtility_zero_shift γ).symm) hfl hκ hβ hβR hpos hcap ha₀ hle hzero + (by simpa only [zero_add] using hthr) end IncomeFluctuation From 93e033690296b8371fc5c6e722219febf2cdc39c Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 21:42:52 +0000 Subject: [PATCH 18/19] Extend CRRA constants across nonlogarithmic parameters --- .../Models/CRRAConstants.lean | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index e1b47bb97..878ddff34 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -198,16 +198,23 @@ theorem slopeBound_crraUtility {γ m : ℝ} (hγ : γ ≠ 1) (hm : 0 < m) : simp only [slopeBound, crraSlopeBound, crraUtility_of_ne hγ, hhalf, hexp] field_simp -theorem crraSlopeBound_nonneg {γ m : ℝ} (hγ1 : γ < 1) (hm : 0 < m) : +theorem crraSlopeBound_nonneg {γ m : ℝ} (hm : 0 ≤ m) : 0 ≤ crraSlopeBound γ m := by - have h2 : (1 : ℝ) ≤ (2 : ℝ) ^ (1 - γ) := - Real.one_le_rpow (by norm_num : (1 : ℝ) ≤ 2) (by linarith) - have hinv : ((2 : ℝ) ^ (1 - γ))⁻¹ ≤ 1 := by - rw [inv_le_one₀ (by linarith)]; exact h2 - have hmp : (0 : ℝ) < m ^ (-γ) := Real.rpow_pos_of_pos hm _ - refine div_nonneg ?_ (by linarith) - have : (0 : ℝ) ≤ 1 - ((2 : ℝ) ^ (1 - γ))⁻¹ := by linarith - positivity + unfold crraSlopeBound + have hmp : 0 ≤ 2 * m ^ (-γ) := mul_nonneg (by norm_num) (Real.rpow_nonneg hm _) + have hpositive : 0 < (2 : ℝ) ^ (1 - γ) := Real.rpow_pos_of_pos (by norm_num) _ + by_cases hγ : γ ≤ 1 + · have hpower : 1 ≤ (2 : ℝ) ^ (1 - γ) := + Real.one_le_rpow (by norm_num) (sub_nonneg.mpr hγ) + exact div_nonneg + (mul_nonneg hmp (sub_nonneg.mpr ((inv_le_one₀ hpositive).mpr hpower))) + (sub_nonneg.mpr hγ) + · have hγ' : 1 ≤ γ := (lt_of_not_ge hγ).le + have hpower : (2 : ℝ) ^ (1 - γ) ≤ 1 := + Real.rpow_le_one_of_one_le_of_nonpos (by norm_num) (sub_nonpos.mpr hγ') + exact div_nonneg_of_nonpos + (mul_nonpos_of_nonneg_of_nonpos hmp (sub_nonpos.mpr ((one_le_inv₀ hpositive).mpr hpower))) + (sub_nonpos.mpr hγ') namespace IncomeFluctuation @@ -272,26 +279,26 @@ theorem cont_sub_le_oscGap (z : Z) (x y : ℝ) : P.cont z x - P.cont z y ≤ P.o linarith [P.valueFunction_le_oscBound (x, z'), P.oscBound_le_valueFunction (y, z')] _ = P.oscGap := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] -/-- The Lipschitz constant of the value function under CES, mirroring `logLipschitz`. -/ +/-- The nonlogarithmic CRRA Lipschitz constant, mirroring `logLipschitz`. -/ noncomputable def crraLipschitz (γ : ℝ) : ℝ := crraSlopeBound γ P.minConsumption * (1 + P.interest) / (1 - P.discount * (1 + P.interest)) -theorem crraLipschitz_nonneg {γ : ℝ} (hγ1 : γ < 1) +theorem crraLipschitz_nonneg {γ : ℝ} (hβR : P.discount * (1 + P.interest) < 1) : 0 ≤ P.crraLipschitz γ := by - have hs := crraSlopeBound_nonneg hγ1 P.minConsumption_pos + have hs := crraSlopeBound_nonneg (γ := γ) P.minConsumption_pos.le have hr := P.interest_gt_neg_one exact div_nonneg (by positivity) (by linarith) -/-- **The value function is Lipschitz with the explicit CES constant.** -/ -theorem crra_valueFunction_lipschitz {γ : ℝ} (hγ1 : γ < 1) +/-- **The value function is Lipschitz with the explicit CRRA constant for `γ ≠ 1`.** -/ +theorem crra_valueFunction_lipschitz {γ : ℝ} (hγ : γ ≠ 1) (hu : P.u = crraUtility γ) (hβR : P.discount * (1 + P.interest) < 1) : ∀ z : Z, ∀ x ∈ Icc (0 : ℝ) assetCap, ∀ y ∈ Icc (0 : ℝ) assetCap, |P.toExtended.valueFunction (x, z) - P.toExtended.valueFunction (y, z)| ≤ P.crraLipschitz γ * |x - y| := by - refine P.valueFunction_lipschitz (P.crraLipschitz_nonneg hγ1 hβR) (le_of_eq ?_) + refine P.valueFunction_lipschitz (P.crraLipschitz_nonneg (γ := γ) hβR) (le_of_eq ?_) have hne : (1 : ℝ) - P.discount * (1 + P.interest) ≠ 0 := by linarith simp only [crraLipschitz, slopeBoundU, hu, - slopeBound_crraUtility (show γ ≠ 1 by linarith) P.minConsumption_pos] + slopeBound_crraUtility hγ P.minConsumption_pos] field_simp ring @@ -302,7 +309,7 @@ theorem crra_policy_eq_zero_of_resources {γ : ℝ} (hγ0 : 0 < γ) (hγ1 : γ < (hβR : P.discount * (1 + P.interest) < 1) {s : ℝ × Z} (hs : s.1 ∈ Icc (0 : ℝ) assetCap) (hlt : P.discount * P.crraLipschitz γ < P.resources s ^ (-γ)) : P.policy s = 0 := - P.policy_eq_zero_of_corner_at (P.crra_valueFunction_lipschitz hγ1 hu hβR) hs + P.policy_eq_zero_of_corner_at (P.crra_valueFunction_lipschitz (ne_of_lt hγ1) hu hβR) hs (fun c d hd hdc hc => by rw [hu] rw [sub_zero] at hc @@ -394,9 +401,9 @@ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : simpa only [P.policyOf_valueFunction, consumptionFnOf, zero_add, sub_zero] using h /-- **Assets fall above an explicit threshold**, in the income state that matters. The threshold -is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`; with `θ` near one the second -factor is close to `income z ^ γ`, so the threshold shrinks with the income of the state -- which -is exactly what lets the corner condition reach it. -/ +is `(β · oscGap / θ) · (income z + (1 + r - θ) · assetCap) ^ γ`. It depends on the +income of the selected state and the residual gross return `1 + r - θ`; its strict comparison +with current assets is an explicit hypothesis. -/ theorem crra_policy_lt_self {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : θ < 1) (hθr : θ ≤ 1 + P.interest) (hu : P.u = crraUtility γ) (hpc : P.PositiveConsumption) {a : ℝ} (ha : a ∈ Icc (0 : ℝ) assetCap) (z : Z) From 0dd57047ee7240a2dc40d8f20724f30bee90f89d Mon Sep 17 00:00:00 2001 From: Vasily Ilin Date: Sun, 18 Oct 2026 22:29:08 +0000 Subject: [PATCH 19/19] Share oscillation constant and remove redundant probability wrapper --- .../Distribution/Uniqueness.lean | 2 -- .../AiyagariEquilibrium/Models/CRRAConstants.lean | 13 ++++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean index b62867452..f84ab0a7f 100644 --- a/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean +++ b/LeanPool/AiyagariEquilibrium/Distribution/Uniqueness.lean @@ -204,8 +204,6 @@ theorem osc_markovOp_le {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 rw [hbad t] at ht linarith -theorem prob_le_one' (s : P.State) (z' : Z) : P.prob s z' ≤ 1 := P.prob_le_one s z' - theorem osc_cycle_le {z₀ : Z} {s₀ : P.State} {p₀ : ℝ} {N : ℕ} (hp0 : 0 ≤ p₀) (hp : ∀ s : P.State, p₀ ≤ P.prob s z₀) (hbad : ∀ s, (P.badStep z₀)^[N] s = s₀) (k : ℕ) (h : P.State →ᵇ ℝ) : diff --git a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean index 878ddff34..26164b670 100644 --- a/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean +++ b/LeanPool/AiyagariEquilibrium/Models/CRRAConstants.lean @@ -253,14 +253,9 @@ DIFFERENCES of continuation values, so an oscillation bound avoids this normaliz `(u maxConsumption - u minConsumption) / (1 - β)`. -/ /-- The spread of the value function, from the two constant plans. -/ -noncomputable def oscGap : ℝ := - (P.u P.maxConsumption - P.u P.minConsumption) / (1 - P.discount) +noncomputable def oscGap : ℝ := P.oscSpread -theorem oscGap_nonneg : 0 ≤ P.oscGap := by - have hβ : (P.discount : ℝ) < 1 := by exact_mod_cast P.discount_lt_one - refine div_nonneg (sub_nonneg.mpr ?_) (by linarith) - exact P.monotoneOn_u_dom (P.mem_dom_of_pos P.minConsumption_pos) - (P.mem_dom_of_pos P.maxConsumption_pos) P.minConsumption_le_maxConsumption +theorem oscGap_nonneg : 0 ≤ P.oscGap := P.oscSpread_nonneg /-- **The continuation value moves by at most the oscillation gap**, whatever the two saving levels. This is what replaces `2 ‖V‖` in every comparison. -/ @@ -275,7 +270,7 @@ theorem cont_sub_le_oscGap (z : Z) (x y : ℝ) : P.cont z x - P.cont z y ≤ P.o ≤ ∑ z' : Z, P.transitionMatrix z z' * P.oscGap := by refine Finset.sum_le_sum fun z' _ => ?_ refine mul_le_mul_of_nonneg_left ?_ (P.transitionMatrix_nonneg _ _) - simp only [oscGap, sub_div] + simp only [oscGap, oscSpread, sub_div] linarith [P.valueFunction_le_oscBound (x, z'), P.oscBound_le_valueFunction (y, z')] _ = P.oscGap := by rw [← Finset.sum_mul, P.transitionMatrix_sum, one_mul] @@ -392,7 +387,7 @@ theorem crra_policy_le_mul {γ θ : ℝ} (hγ0 : 0 < γ) (hθ0 : 0 < θ) (hθ1 : * (P.consumption (a, z) (P.policy (a, z)) + (1 - θ) * P.policy (a, z)) ^ γ := by have h := P.policyOf_le_mul_of_power_marginal (γ := γ) (η := 0) hθ0 hθ1 P.concaveOn_valueFunction P.oscGap_nonneg - (by simpa only [oscSpread, oscGap] using P.oscOn_valueFunction) + P.oscOn_valueFunction (fun c d _ hcd hpositive => by simpa only [zero_add, hu] using crra_marginal_bound hγ0 (by simpa only [zero_add] using hpositive) hcd (le_refl d)) ha z