--- name: general-equilibrium-model-builder description: Build and solve Walrasian general equilibrium models with theory derivations and Julia computation workflow_stage: theory compatibility: - claude-code - cursor - codex - gemini-cli author: Abhimanyu Nag version: 1.0.0 tags: - general-equilibrium - walrasian - microeconomic-theory - julia - computational-economics - welfare-economics - pure-exchange --- # General Equilibrium Model Builder ## Purpose This skill helps economists build, analyze, and numerically solve **Walrasian General Equilibrium (GE)** models. It covers both the theoretical foundations (existence, uniqueness, welfare theorems) and computational implementation in Julia for finding equilibrium prices and allocations. **Current Scope**: Pure exchange economies (no production). Future versions will extend to production economies, Arrow-Debreu with uncertainty, and dynamic models. ## When to Use - **Theory Development**: Formalizing a pure exchange GE model for a paper - **Teaching**: Creating examples for microeconomic theory courses - **Computation**: Numerically solving for equilibrium prices and allocations - **Welfare Analysis**: Evaluating Pareto efficiency and social welfare - **Comparative Statics**: Analyzing how equilibrium changes with parameters ## Instructions ### Step 1: Understand the Economic Environment Before generating any model, ask the user: 1. **Number of goods** ($L$): How many commodities in the economy? 2. **Number of consumers** ($I$): How many agents? 3. **Preferences**: What utility functions? (Cobb-Douglas, CES, Leontief, quasilinear) 4. **Endowments**: What is each agent's initial endowment vector? 5. **Output format**: Theory derivation, Julia code, or both? ### Step 2: Set Up the Theoretical Framework A **pure exchange economy** $\mathcal{E}$ is characterized by: $$\mathcal{E} = \left\{ (u^i, \omega^i)_{i=1}^{I} \right\}$$ where: - $u^i: \mathbb{R}^L_+ \to \mathbb{R}$ is consumer $i$'s utility function - $\omega^i \in \mathbb{R}^L_+$ is consumer $i$'s endowment vector - $L$ is the number of goods - $I$ is the number of consumers **Consumer's Problem**: Given prices $p \in \mathbb{R}^L_{++}$, consumer $i$ solves: $$\max_{x^i \in \mathbb{R}^L_+} u^i(x^i) \quad \text{s.t.} \quad p \cdot x^i \leq p \cdot \omega^i$$ The solution yields the **Marshallian demand** $x^i(p, p \cdot \omega^i)$. ### Step 3: Define Walrasian Equilibrium **Definition (Walrasian Equilibrium)**: An allocation $(x^{*1}, \ldots, x^{*I})$ and price vector $p^* \in \mathbb{R}^L_{++}$ constitute a Walrasian equilibrium if: 1. **Utility Maximization**: For each $i$, $x^{*i}$ solves consumer $i$'s problem at prices $p^*$ 2. **Market Clearing**: $\sum_{i=1}^{I} x^{*i} = \sum_{i=1}^{I} \omega^i$ Equivalently, the **excess demand function** $z(p) = \sum_{i=1}^{I} [x^i(p) - \omega^i]$ satisfies $z(p^*) = 0$. ### Step 4: State Key Theoretical Results Include the following theorems as appropriate: **Theorem (Walras' Law)**: For any price vector $p$: $$p \cdot z(p) = 0$$ *Interpretation*: The value of excess demand is always zero (budget constraints bind). **Theorem (First Welfare Theorem)**: Every Walrasian equilibrium allocation is Pareto efficient. **Theorem (Second Welfare Theorem)**: Under convexity assumptions, any Pareto efficient allocation can be supported as a Walrasian equilibrium with appropriate lump-sum transfers. **Theorem (Existence - Debreu, 1959)**: Under standard assumptions (continuity, strict convexity, strict monotonicity of preferences, strictly positive endowments), a Walrasian equilibrium exists. ### Step 5: Generate Julia Code for Computation Use Julia with the following structure: ```julia # ============================================ # General Equilibrium Solver in Julia # Pure Exchange Economy # ============================================ using LinearAlgebra using NLsolve using Plots # Define the economy structure struct PureExchangeEconomy n_goods::Int # Number of goods (L) n_consumers::Int # Number of consumers (I) endowments::Matrix{Float64} # I × L matrix of endowments utility_params::Vector{Any} # Parameters for utility functions utility_type::Symbol # :cobb_douglas, :ces, :leontief end # Cobb-Douglas utility: u(x) = ∏ x_l^α_l function utility_cobb_douglas(x, α) return prod(x .^ α) end # Marshallian demand for Cobb-Douglas preferences function demand_cobb_douglas(p, wealth, α) # x_l = (α_l / sum(α)) * (wealth / p_l) α_normalized = α / sum(α) return α_normalized .* wealth ./ p end # Excess demand function function excess_demand(p, economy::PureExchangeEconomy) z = zeros(economy.n_goods) for i in 1:economy.n_consumers ω_i = economy.endowments[i, :] wealth_i = dot(p, ω_i) if economy.utility_type == :cobb_douglas α_i = economy.utility_params[i] x_i = demand_cobb_douglas(p, wealth_i, α_i) else error("Unsupported utility_type: $(economy.utility_type). Only :cobb_douglas is currently implemented.") end z += x_i - ω_i end return z end # Solve for equilibrium prices (normalize p_1 = 1) # Uses log-price parameterization to ensure prices remain strictly positive function solve_equilibrium(economy::PureExchangeEconomy) # Initial guess in log-space (log of ones = zeros) p0 = zeros(economy.n_goods - 1) # Excess demand for goods 2 to L (Walras' Law implies good 1 clears) # Reparameterize using log-prices: x = log(p_rest), so p_rest = exp(x) function excess_demand_reduced!(F, x) p_rest = exp.(x) # Exponentiate to get positive prices p = vcat(1.0, p_rest) # Numeraire p_1 = 1 z = excess_demand(p, economy) F .= z[2:end] end # Solve z(p) = 0 in log-space result = nlsolve(excess_demand_reduced!, p0, autodiff=:forward) if converged(result) p_rest_star = exp.(result.zero) # Convert back from log-space p_star = vcat(1.0, p_rest_star) return p_star else error("Equilibrium solver did not converge") end end # Compute equilibrium allocations function equilibrium_allocations(p_star, economy::PureExchangeEconomy) allocations = zeros(economy.n_consumers, economy.n_goods) for i in 1:economy.n_consumers ω_i = economy.endowments[i, :] wealth_i = dot(p_star, ω_i) if economy.utility_type == :cobb_douglas α_i = economy.utility_params[i] allocations[i, :] = demand_cobb_douglas(p_star, wealth_i, α_i) else throw(ArgumentError("Unsupported utility_type: $(economy.utility_type) in equilibrium_allocations. Only :cobb_douglas is currently implemented.")) end end return allocations end # Check Pareto efficiency via MRS equality function check_pareto_efficiency(allocations, economy::PureExchangeEconomy) # Currently only supports 2-good economies if economy.n_goods != 2 throw(ArgumentError("check_pareto_efficiency currently only supports 2-good economies. Got economy.n_goods = $(economy.n_goods).")) end if economy.utility_type == :cobb_douglas # MRS_{12} = (α_1/α_2) * (x_2/x_1) should be equal for all consumers epsilon = 1e-12 # Small threshold for near-zero detection mrs_values = [] for i in 1:economy.n_consumers α_i = economy.utility_params[i] x_i = allocations[i, :] # Guard against division by zero: check both x_i[1] and α_i[2] if abs(x_i[1]) < epsilon || abs(α_i[2]) < epsilon # Handle corner case: set sentinel value for zero/near-zero consumption push!(mrs_values, Inf) else mrs_i = (α_i[1] / α_i[2]) * (x_i[2] / x_i[1]) push!(mrs_values, mrs_i) end end return mrs_values else throw(ArgumentError("Unsupported utility type: $(economy.utility_type) in check_pareto_efficiency. Only :cobb_douglas is currently implemented.")) end end ``` ### Step 6: Provide Complete Example ```julia # ============================================ # Example: 2×2 Pure Exchange Economy # ============================================ # Two consumers, two goods # Consumer 1: u(x,y) = x^0.6 * y^0.4, endowment (4, 1) # Consumer 2: u(x,y) = x^0.3 * y^0.7, endowment (1, 4) economy = PureExchangeEconomy( 2, # 2 goods 2, # 2 consumers [4.0 1.0; 1.0 4.0], # Endowment matrix [[0.6, 0.4], [0.3, 0.7]], # Cobb-Douglas parameters :cobb_douglas ) # Solve for equilibrium p_star = solve_equilibrium(economy) println("Equilibrium prices: p = ", p_star) # Compute allocations x_star = equilibrium_allocations(p_star, economy) println("Consumer 1 allocation: ", x_star[1, :]) println("Consumer 2 allocation: ", x_star[2, :]) # Verify market clearing total_endowment = sum(economy.endowments, dims=1) total_allocation = sum(x_star, dims=1) println("Market clearing check: ", isapprox(total_endowment, total_allocation)) # Check Pareto efficiency (MRS equality) mrs = check_pareto_efficiency(x_star, economy) println("MRS values (should be equal): ", mrs) ``` ### Step 7: Visualize with Edgeworth Box ```julia # ============================================ # Edgeworth Box Visualization # ============================================ function plot_edgeworth_box(economy::PureExchangeEconomy, p_star, x_star) # Total endowment defines box dimensions ω_total = vec(sum(economy.endowments, dims=1)) # Create plot plt = plot( xlim=(0, ω_total[1]), ylim=(0, ω_total[2]), xlabel="Good 1", ylabel="Good 2", title="Edgeworth Box", legend=:topright, aspect_ratio=:equal ) # Plot endowment point ω1 = economy.endowments[1, :] scatter!([ω1[1]], [ω1[2]], label="Endowment", markersize=8, color=:red) # Plot equilibrium allocation scatter!([x_star[1, 1]], [x_star[1, 2]], label="Equilibrium", markersize=8, color=:green) # Plot budget line through endowment # p_1 * x_1 + p_2 * x_2 = p_1 * ω_1 + p_2 * ω_2 wealth1 = dot(p_star, ω1) x1_range = range(0, ω_total[1], length=100) x2_budget = (wealth1 .- p_star[1] .* x1_range) ./ p_star[2] plot!(x1_range, x2_budget, label="Budget line", color=:blue, linewidth=2) # Plot contract curve (locus of Pareto efficient allocations) # For Cobb-Douglas, contract curve: x_2^1 / x_1^1 = (α_2^1/α_1^1) / (α_2^2/α_1^2) * (ω_2 - x_2^1) / (ω_1 - x_1^1) return plt end # Generate the plot plt = plot_edgeworth_box(economy, p_star, x_star) savefig(plt, "edgeworth_box.png") ``` ## Example Prompts Users might invoke this skill with prompts like: - "Set up a 2-good, 3-consumer pure exchange economy with CES preferences" - "Derive the Walrasian equilibrium conditions for a Cobb-Douglas economy" - "Write Julia code to solve for equilibrium prices in my exchange economy" - "Prove the First Welfare Theorem for a pure exchange economy" - "Plot an Edgeworth box showing the contract curve and equilibrium" - "Compute comparative statics: how does equilibrium change if endowments shift?" ## Requirements ### Software - Julia 1.9+ ### Packages ```julia using Pkg Pkg.add(["NLsolve", "LinearAlgebra", "Plots", "ForwardDiff"]) ``` | Package | Purpose | |---------|---------| | `NLsolve` | Nonlinear equation solver for excess demand = 0 | | `LinearAlgebra` | Vector/matrix operations | | `Plots` | Visualization (Edgeworth box, etc.) | | `ForwardDiff` | Automatic differentiation for Jacobians | ## Mathematical Background ### Assumptions for Existence Standard assumptions ensuring equilibrium existence: 1. **Continuity**: Each $u^i$ is continuous 2. **Strict Monotonicity**: $x \gg y \Rightarrow u^i(x) > u^i(y)$ 3. **Strict Convexity**: $u^i$ is strictly quasiconcave 4. **Positive Endowments**: $\omega^i \gg 0$ for all $i$ ### Properties of Excess Demand Under standard assumptions, $z(p)$ satisfies: 1. **Continuity**: $z$ is continuous 2. **Homogeneity of degree 0**: $z(\lambda p) = z(p)$ for all $\lambda > 0$ 3. **Walras' Law**: $p \cdot z(p) = 0$ 4. **Boundary behavior**: If $p_l \to 0$, then $z_l(p) \to +\infty$ ### Numerical Solution Strategy 1. **Normalize prices**: Set $p_1 = 1$ (numeraire) 2. **Reduce dimension**: Solve $z_2(p) = \cdots = z_L(p) = 0$ (Walras' Law gives $z_1 = 0$) 3. **Use Newton's method**: `NLsolve.jl` with autodiff for Jacobian 4. **Handle boundaries**: Ensure $p_l > 0$ during iteration ## Best Practices 1. **Always verify market clearing** after solving 2. **Check Walras' Law** holds numerically ($p \cdot z \approx 0$) 3. **Verify Pareto efficiency** by checking MRS equality across consumers 4. **Use multiple initial guesses** if solver doesn't converge 5. **Normalize prices** to avoid indeterminacy (homogeneity of degree 0) ## Common Pitfalls - ❌ Forgetting that prices are only determined up to a scalar (must normalize) - ❌ Not checking for corner solutions (zero consumption of some good) - ❌ Ignoring numerical precision issues near boundaries - ❌ Assuming uniqueness without verifying (multiple equilibria are possible) - ❌ Confusing Marshallian (uncompensated) and Hicksian (compensated) demands ## Extensions (Future Versions) - **Production economies**: Firms with profit maximization - **Arrow-Debreu securities**: Contingent claims and uncertainty - **Overlapping generations (OLG)**: Dynamic GE with generational overlap - **Computable GE (CGE)**: Calibrated models for policy analysis - **Incomplete markets**: When not all contingencies can be traded ## References ### Textbooks - Mas-Colell, Whinston, and Green (1995). *Microeconomic Theory*. Oxford University Press. Chapters 15-17. - Debreu, G. (1959). *Theory of Value*. Yale University Press. - Varian, H. (1992). *Microeconomic Analysis*. 3rd Edition. Chapters 17-18. ### Computational Resources - QuantEcon Julia lectures: [https://julia.quantecon.org/](https://julia.quantecon.org/) - Judd, K. (1998). *Numerical Methods in Economics*. MIT Press. ### Key Papers - Arrow, K. J., & Debreu, G. (1954). Existence of an equilibrium for a competitive economy. *Econometrica*, 22(3), 265-290. - Scarf, H. (1967). The approximation of fixed points of a continuous mapping. *SIAM Journal on Applied Mathematics*, 15(5), 1328-1343. ## Changelog ### v1.0.0 - Initial release: Pure exchange economies with Cobb-Douglas preferences - Julia implementation with NLsolve - Edgeworth box visualization - Theoretical framework and welfare theorems