Loading...
Loading...
Build and solve Walrasian general equilibrium models with theory derivations and Julia computation
npx skill4agent add meleantonio/awesome-econ-ai-stuff general-equilibrium-model-builder# ============================================
# 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# ============================================
# 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)# ============================================
# 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")using Pkg
Pkg.add(["NLsolve", "LinearAlgebra", "Plots", "ForwardDiff"])| Package | Purpose |
|---|---|
| Nonlinear equation solver for excess demand = 0 |
| Vector/matrix operations |
| Visualization (Edgeworth box, etc.) |
| Automatic differentiation for Jacobians |
NLsolve.jl