
General Equilibrium Model Builder
- 86 installs
- 590 repo stars
- Updated June 23, 2026
- meleantonio/awesome-econ-ai-stuff
Helps with ai & agent building tasks.
About
general-equilibrium-model-builder is a Claude Code skill in the AI & Agent Building category.
- general-equilibrium-model-builder
- AI & Agent Building
- AI-coding skill
General Equilibrium Model Builder by the numbers
- 86 all-time installs (skills.sh)
- +5 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,032 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/meleantonio/awesome-econ-ai-stuff --skill general-equilibrium-model-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 86 |
|---|---|
| repo stars | ★ 590 |
| Last updated | June 23, 2026 |
| Repository | meleantonio/awesome-econ-ai-stuff ↗ |
What it does
Helps with ai & agent building tasks.
Files
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:
# ============================================
# 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
endStep 6: Provide Complete Example
# ============================================
# 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
# ============================================
# 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
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/
- 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
# ============================================
# Comparative Statics in General Equilibrium
# How equilibrium changes with endowment shifts
# ============================================
# Author: Abhimanyu Nag
# Skill: general-equilibrium-model-builder v1.0.0
# ============================================
using LinearAlgebra
using NLsolve
using Plots
using Printf
# Include the core functions from pure_exchange_2x2.jl
# (In practice, you would use: include("pure_exchange_2x2.jl"))
# ============================================
# Replicate core structures (for standalone use)
# ============================================
struct PureExchangeEconomy
n_goods::Int
n_consumers::Int
endowments::Matrix{Float64}
utility_params::Vector{Vector{Float64}}
utility_type::Symbol
end
function demand_cobb_douglas(p::Vector{Float64}, wealth::Float64, α::Vector{Float64})
α_normalized = α / sum(α)
return α_normalized .* wealth ./ p
end
function excess_demand(p::Vector{Float64}, 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
function solve_equilibrium(economy::PureExchangeEconomy; p0=nothing)
if p0 === nothing
p0 = ones(economy.n_goods - 1)
end
function excess_demand_reduced!(F, p_rest)
p = vcat(1.0, p_rest)
z = excess_demand(p, economy)
F .= z[2:end]
end
result = nlsolve(excess_demand_reduced!, p0, autodiff=:forward)
return converged(result) ? vcat(1.0, result.zero) : nothing
end
function equilibrium_allocations(p_star::Vector{Float64}, 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
error("Unsupported utility_type: $(economy.utility_type). Only :cobb_douglas is currently implemented.")
end
end
return allocations
end
function utility_cobb_douglas(x::Vector{Float64}, α::Vector{Float64})
return prod(x .^ α)
end
# ============================================
# Comparative Statics Functions
# ============================================
"""
endowment_shock_analysis(base_economy, good, consumer, shock_range)
Analyze how equilibrium changes when one consumer's endowment of one good changes.
# Arguments
- `base_economy`: Baseline economy
- `good`: Which good's endowment to change (1 or 2)
- `consumer`: Which consumer receives the shock (1 or 2)
- `shock_range`: Vector of shock magnitudes (e.g., -2:0.5:2)
# Returns
- Dictionary with results for each shock value
"""
function endowment_shock_analysis(
base_economy::PureExchangeEconomy,
good::Int,
consumer::Int,
shock_range::AbstractVector
)
results = Dict(
:shock => Float64[],
:price_ratio => Float64[],
:allocation_1_good1 => Float64[],
:allocation_1_good2 => Float64[],
:allocation_2_good1 => Float64[],
:allocation_2_good2 => Float64[],
:utility_1 => Float64[],
:utility_2 => Float64[]
)
for Δ in shock_range
# Create shocked economy
new_endowments = copy(base_economy.endowments)
new_endowments[consumer, good] += Δ
# Skip if endowment becomes non-positive
if any(new_endowments .<= 0)
continue
end
shocked_economy = PureExchangeEconomy(
base_economy.n_goods,
base_economy.n_consumers,
new_endowments,
base_economy.utility_params,
base_economy.utility_type
)
# Solve equilibrium
p_star = solve_equilibrium(shocked_economy)
if p_star === nothing
continue
end
x_star = equilibrium_allocations(p_star, shocked_economy)
# Compute utilities
u1 = utility_cobb_douglas(x_star[1, :], base_economy.utility_params[1])
u2 = utility_cobb_douglas(x_star[2, :], base_economy.utility_params[2])
# Store results
push!(results[:shock], Δ)
push!(results[:price_ratio], p_star[2] / p_star[1])
push!(results[:allocation_1_good1], x_star[1, 1])
push!(results[:allocation_1_good2], x_star[1, 2])
push!(results[:allocation_2_good1], x_star[2, 1])
push!(results[:allocation_2_good2], x_star[2, 2])
push!(results[:utility_1], u1)
push!(results[:utility_2], u2)
end
return results
end
"""
plot_comparative_statics(results, title_suffix="")
Create plots showing how equilibrium variables respond to endowment shocks.
"""
function plot_comparative_statics(results::Dict, title_suffix::String="")
shock = results[:shock]
# Price response
p1 = plot(
shock, results[:price_ratio],
xlabel="Endowment Shock (Δω)",
ylabel="Price Ratio (p₂/p₁)",
title="Price Response" * title_suffix,
linewidth=2,
color=:blue,
legend=false,
marker=:circle,
markersize=4
)
idx = findfirst(shock .≈ 0)
if idx !== nothing
hline!([results[:price_ratio][idx]],
linestyle=:dash, color=:gray, alpha=0.5)
end
vline!([0], linestyle=:dash, color=:gray, alpha=0.5)
# Allocation response - Consumer 1
p2 = plot(
shock, results[:allocation_1_good1],
xlabel="Endowment Shock (Δω)",
ylabel="Consumption",
title="Consumer 1 Allocation" * title_suffix,
label="Good 1",
linewidth=2,
color=:red,
marker=:circle,
markersize=4
)
plot!(shock, results[:allocation_1_good2],
label="Good 2", linewidth=2, color=:orange, marker=:square, markersize=4)
vline!([0], linestyle=:dash, color=:gray, alpha=0.5, label="")
# Allocation response - Consumer 2
p3 = plot(
shock, results[:allocation_2_good1],
xlabel="Endowment Shock (Δω)",
ylabel="Consumption",
title="Consumer 2 Allocation" * title_suffix,
label="Good 1",
linewidth=2,
color=:red,
marker=:circle,
markersize=4
)
plot!(shock, results[:allocation_2_good2],
label="Good 2", linewidth=2, color=:orange, marker=:square, markersize=4)
vline!([0], linestyle=:dash, color=:gray, alpha=0.5, label="")
# Utility response
p4 = plot(
shock, results[:utility_1],
xlabel="Endowment Shock (Δω)",
ylabel="Utility",
title="Welfare Effects" * title_suffix,
label="Consumer 1",
linewidth=2,
color=:green,
marker=:circle,
markersize=4
)
plot!(shock, results[:utility_2],
label="Consumer 2", linewidth=2, color=:purple, marker=:square, markersize=4)
vline!([0], linestyle=:dash, color=:gray, alpha=0.5, label="")
# Combine into single figure
combined = plot(p1, p2, p3, p4, layout=(2, 2), size=(900, 700))
return combined
end
# ============================================
# MAIN: Run Comparative Statics Analysis
# ============================================
function main()
println("=" ^ 60)
println("COMPARATIVE STATICS IN GENERAL EQUILIBRIUM")
println("=" ^ 60)
println()
# Define baseline economy (same as pure_exchange_2x2.jl)
base_economy = PureExchangeEconomy(
2, 2,
[4.0 1.0; 1.0 4.0],
[[0.6, 0.4], [0.3, 0.7]],
:cobb_douglas
)
println("BASELINE ECONOMY")
println("-" ^ 40)
println("Consumer 1: u(x,y) = x^0.6 × y^0.4, ω = (4, 1)")
println("Consumer 2: u(x,y) = x^0.3 × y^0.7, ω = (1, 4)")
# Solve baseline
p_base = solve_equilibrium(base_economy)
if p_base === nothing
error("Failed to solve baseline equilibrium. Check economy specification.")
end
x_base = equilibrium_allocations(p_base, base_economy)
@printf("\nBaseline equilibrium: p* = (%.4f, %.4f)\n", p_base[1], p_base[2])
@printf("Consumer 1: x* = (%.4f, %.4f)\n", x_base[1, 1], x_base[1, 2])
@printf("Consumer 2: x* = (%.4f, %.4f)\n", x_base[2, 1], x_base[2, 2])
# ============================================
# Experiment 1: Increase Consumer 1's endowment of Good 1
# ============================================
println()
println("EXPERIMENT 1: Shock to Consumer 1's Good 1 Endowment")
println("-" ^ 40)
shock_range = range(-3, 3, length=25)
results1 = endowment_shock_analysis(base_economy, 1, 1, shock_range)
println("Effect of increasing Consumer 1's Good 1 endowment:")
println(" - More Good 1 in economy → Good 1 becomes relatively cheaper")
println(" - Price ratio p₂/p₁ increases (Good 2 becomes relatively more expensive)")
println(" - Both consumers substitute toward Good 1")
# ============================================
# Experiment 2: Increase Consumer 2's endowment of Good 2
# ============================================
println()
println("EXPERIMENT 2: Shock to Consumer 2's Good 2 Endowment")
println("-" ^ 40)
results2 = endowment_shock_analysis(base_economy, 2, 2, shock_range)
println("Effect of increasing Consumer 2's Good 2 endowment:")
println(" - More Good 2 in economy → Good 2 becomes relatively cheaper")
println(" - Price ratio p₂/p₁ decreases")
println(" - Consumer 2's wealth increases")
# ============================================
# Generate Plots
# ============================================
println()
println("GENERATING PLOTS...")
println("-" ^ 40)
plt1 = plot_comparative_statics(results1, "\n(Shock: ω₁¹)")
savefig(plt1, "comparative_statics_consumer1_good1.png")
println("Saved: comparative_statics_consumer1_good1.png")
plt2 = plot_comparative_statics(results2, "\n(Shock: ω₂²)")
savefig(plt2, "comparative_statics_consumer2_good2.png")
println("Saved: comparative_statics_consumer2_good2.png")
# ============================================
# Transfer Analysis
# ============================================
println()
println("EXPERIMENT 3: Lump-Sum Transfers")
println("-" ^ 40)
println("Redistributing endowments while keeping total constant")
# Transfer Good 1 from Consumer 1 to Consumer 2
transfer_range = range(-2, 2, length=21)
transfer_results = Dict(
:transfer => Float64[],
:price_ratio => Float64[],
:utility_1 => Float64[],
:utility_2 => Float64[]
)
for τ in transfer_range
new_endowments = [4.0 - τ 1.0; 1.0 + τ 4.0]
if any(new_endowments .<= 0)
continue
end
transfer_economy = PureExchangeEconomy(
2, 2, new_endowments,
base_economy.utility_params,
:cobb_douglas
)
p_star = solve_equilibrium(transfer_economy)
if p_star === nothing
continue
end
x_star = equilibrium_allocations(p_star, transfer_economy)
u1 = utility_cobb_douglas(x_star[1, :], base_economy.utility_params[1])
u2 = utility_cobb_douglas(x_star[2, :], base_economy.utility_params[2])
push!(transfer_results[:transfer], τ)
push!(transfer_results[:price_ratio], p_star[2] / p_star[1])
push!(transfer_results[:utility_1], u1)
push!(transfer_results[:utility_2], u2)
end
# Plot transfer analysis
plt3 = plot(
layout=(1, 2),
size=(800, 350)
)
plot!(plt3[1],
transfer_results[:transfer], transfer_results[:price_ratio],
xlabel="Transfer τ (Good 1: 1→2)",
ylabel="Price Ratio (p₂/p₁)",
title="Price Effect of Redistribution",
linewidth=2, color=:blue, legend=false,
marker=:circle, markersize=4)
vline!(plt3[1], [0], linestyle=:dash, color=:gray, alpha=0.5)
plot!(plt3[2],
transfer_results[:transfer], transfer_results[:utility_1],
xlabel="Transfer τ (Good 1: 1→2)",
ylabel="Utility",
title="Welfare Effects of Redistribution",
label="Consumer 1", linewidth=2, color=:green, marker=:circle, markersize=4)
plot!(plt3[2],
transfer_results[:transfer], transfer_results[:utility_2],
label="Consumer 2", linewidth=2, color=:purple, marker=:square, markersize=4)
vline!(plt3[2], [0], linestyle=:dash, color=:gray, alpha=0.5, label="")
savefig(plt3, "transfer_analysis.png")
println("Saved: transfer_analysis.png")
println()
println("KEY INSIGHTS:")
println("-" ^ 40)
println("1. Increasing supply of a good lowers its relative price")
println("2. Endowment shocks affect both prices AND allocations")
println("3. Transfers affect welfare distribution but all equilibria are Pareto efficient")
println("4. GE effects capture spillovers missed in partial equilibrium")
println()
println("=" ^ 60)
println("COMPARATIVE STATICS ANALYSIS COMPLETE")
println("=" ^ 60)
return results1, results2, transfer_results
end
# Run if executed directly
if abspath(PROGRAM_FILE) == @__FILE__
main()
end
# ============================================
# General Equilibrium Solver in Julia
# Pure Exchange Economy - 2 Goods, 2 Consumers
# ============================================
# Author: Abhimanyu Nag
# Skill: general-equilibrium-model-builder v1.0.0
#
# This example demonstrates solving a Walrasian equilibrium
# for a 2×2 pure exchange economy with Cobb-Douglas preferences.
# ============================================
using LinearAlgebra
using NLsolve
using Plots
using Printf
# ============================================
# Economy Structure
# ============================================
"""
PureExchangeEconomy
A pure exchange economy with I consumers and L goods.
# Fields
- `n_goods::Int`: Number of goods (L)
- `n_consumers::Int`: Number of consumers (I)
- `endowments::Matrix{Float64}`: I × L matrix of initial endowments
- `utility_params::Vector{Vector{Float64}}`: Utility function parameters for each consumer
- `utility_type::Symbol`: Type of utility function (:cobb_douglas, :ces, :leontief)
"""
struct PureExchangeEconomy
n_goods::Int
n_consumers::Int
endowments::Matrix{Float64}
utility_params::Vector{Vector{Float64}}
utility_type::Symbol
end
# ============================================
# Utility Functions
# ============================================
"""
utility_cobb_douglas(x, α)
Compute Cobb-Douglas utility: u(x) = ∏ᵢ xᵢ^αᵢ
# Arguments
- `x`: Consumption bundle (vector of length L)
- `α`: Preference parameters (vector of length L, αᵢ > 0)
# Returns
- Utility value (scalar)
"""
function utility_cobb_douglas(x::Vector{Float64}, α::Vector{Float64})
return prod(x .^ α)
end
"""
marginal_utility_cobb_douglas(x, α)
Compute marginal utilities for Cobb-Douglas preferences.
# Returns
- Vector of marginal utilities ∂u/∂xₗ
"""
function marginal_utility_cobb_douglas(x::Vector{Float64}, α::Vector{Float64})
u = utility_cobb_douglas(x, α)
return α .* u ./ x
end
# ============================================
# Demand Functions
# ============================================
"""
demand_cobb_douglas(p, wealth, α)
Compute Marshallian demand for Cobb-Douglas preferences.
For u(x) = ∏ xₗ^αₗ, the optimal demand is:
xₗ = (αₗ / Σα) × (wealth / pₗ)
# Arguments
- `p`: Price vector
- `wealth`: Consumer's wealth (p · ω)
- `α`: Preference parameters
# Returns
- Optimal consumption bundle
"""
function demand_cobb_douglas(p::Vector{Float64}, wealth::Float64, α::Vector{Float64})
α_normalized = α / sum(α)
return α_normalized .* wealth ./ p
end
# ============================================
# Excess Demand
# ============================================
"""
excess_demand(p, economy)
Compute aggregate excess demand z(p) = Σᵢ [xᵢ(p) - ωᵢ]
# Arguments
- `p`: Price vector
- `economy`: PureExchangeEconomy struct
# Returns
- Excess demand vector (length L)
"""
function excess_demand(p::Vector{Float64}, 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("Utility type $(economy.utility_type) not implemented")
end
z += x_i - ω_i
end
return z
end
"""
verify_walras_law(p, economy)
Verify that Walras' Law holds: p · z(p) = 0
"""
function verify_walras_law(p::Vector{Float64}, economy::PureExchangeEconomy)
z = excess_demand(p, economy)
return dot(p, z)
end
# ============================================
# Equilibrium Solver
# ============================================
"""
solve_equilibrium(economy; p0=nothing, tol=1e-10)
Solve for Walrasian equilibrium prices.
We normalize p₁ = 1 (numeraire) and solve z₂(p) = ⋯ = zₗ(p) = 0.
By Walras' Law, z₁(p) = 0 automatically.
# Arguments
- `economy`: PureExchangeEconomy struct
- `p0`: Initial price guess (optional)
- `tol`: Convergence tolerance
# Returns
- `p_star`: Equilibrium price vector
"""
function solve_equilibrium(economy::PureExchangeEconomy; p0=nothing, tol=1e-10)
if p0 === nothing
p0 = ones(economy.n_goods - 1)
end
function excess_demand_reduced!(F, p_rest)
p = vcat(1.0, p_rest) # Numeraire p₁ = 1
z = excess_demand(p, economy)
F .= z[2:end]
end
result = nlsolve(excess_demand_reduced!, p0, autodiff=:forward, ftol=tol)
if converged(result)
p_star = vcat(1.0, result.zero)
return p_star
else
error("Equilibrium solver did not converge. Try different initial guess.")
end
end
# ============================================
# Equilibrium Analysis
# ============================================
"""
equilibrium_allocations(p_star, economy)
Compute equilibrium consumption allocations for all consumers.
# Returns
- I × L matrix of allocations
"""
function equilibrium_allocations(p_star::Vector{Float64}, 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
error("Unsupported utility_type: $(economy.utility_type). Only :cobb_douglas is currently implemented.")
end
end
return allocations
end
"""
compute_utilities(allocations, economy)
Compute utility levels at given allocations.
"""
function compute_utilities(allocations::Matrix{Float64}, economy::PureExchangeEconomy)
utilities = zeros(economy.n_consumers)
for i in 1:economy.n_consumers
x_i = allocations[i, :]
if economy.utility_type == :cobb_douglas
α_i = economy.utility_params[i]
utilities[i] = utility_cobb_douglas(x_i, α_i)
else
throw(ArgumentError("Unsupported utility_type: $(economy.utility_type). Only :cobb_douglas is currently implemented."))
end
end
return utilities
end
"""
check_pareto_efficiency(allocations, economy)
Verify Pareto efficiency by checking MRS equality across consumers.
For Cobb-Douglas: MRS₁₂ = (α₁/α₂) × (x₂/x₁)
At Pareto efficient allocations, MRS is equal for all consumers.
# Returns
- Vector of MRS values for each consumer (should all be equal)
"""
function check_pareto_efficiency(allocations::Matrix{Float64}, economy::PureExchangeEconomy)
if economy.utility_type == :cobb_douglas && economy.n_goods >= 2
mrs_values = Float64[]
for i in 1:economy.n_consumers
α_i = economy.utility_params[i]
x_i = allocations[i, :]
# MRS between goods 1 and 2
mrs_i = (α_i[1] / α_i[2]) * (x_i[2] / x_i[1])
push!(mrs_values, mrs_i)
end
return mrs_values
else
return nothing
end
end
# ============================================
# Visualization: Edgeworth Box
# ============================================
"""
plot_edgeworth_box(economy, p_star, x_star; show_indifference=true)
Create an Edgeworth box diagram showing:
- Initial endowment point
- Equilibrium allocation
- Budget line
- Indifference curves (optional)
- Contract curve
Only works for 2-good, 2-consumer economies.
"""
function plot_edgeworth_box(
economy::PureExchangeEconomy,
p_star::Vector{Float64},
x_star::Matrix{Float64};
show_indifference::Bool = true
)
if economy.n_goods != 2 || economy.n_consumers != 2
error("Edgeworth box only works for 2×2 economies")
end
# Total endowment defines box dimensions
ω_total = vec(sum(economy.endowments, dims=1))
# Consumer 1's endowment and equilibrium allocation
ω1 = economy.endowments[1, :]
x1_star = x_star[1, :]
# Create plot
plt = plot(
xlim=(0, ω_total[1] * 1.05),
ylim=(0, ω_total[2] * 1.05),
xlabel="Good 1 (Consumer 1 →)",
ylabel="Good 2 (Consumer 1 →)",
title="Edgeworth Box: Walrasian Equilibrium",
legend=:topright,
size=(700, 600),
grid=true,
gridalpha=0.3
)
# Draw box outline
plot!([0, ω_total[1], ω_total[1], 0, 0],
[0, 0, ω_total[2], ω_total[2], 0],
color=:black, linewidth=2, label="")
# Budget line through endowment
wealth1 = dot(p_star, ω1)
x1_range = range(0, ω_total[1], length=200)
x2_budget = (wealth1 .- p_star[1] .* x1_range) ./ p_star[2]
# Filter to box bounds
valid = (x2_budget .>= 0) .& (x2_budget .<= ω_total[2])
plot!(x1_range[valid], x2_budget[valid],
label="Budget Line", color=:blue, linewidth=2.5, linestyle=:dash)
if show_indifference
# Consumer 1's indifference curve through equilibrium
α1 = economy.utility_params[1]
u1_star = utility_cobb_douglas(x1_star, α1)
x1_ic = range(0.01, ω_total[1], length=200)
# u = x₁^α₁ × x₂^α₂ → x₂ = (u / x₁^α₁)^(1/α₂)
x2_ic1 = (u1_star ./ (x1_ic .^ α1[1])) .^ (1/α1[2])
valid1 = (x2_ic1 .>= 0) .& (x2_ic1 .<= ω_total[2])
plot!(x1_ic[valid1], x2_ic1[valid1],
label="IC Consumer 1", color=:red, linewidth=1.5, alpha=0.7)
# Consumer 2's indifference curve (from their origin at top-right)
α2 = economy.utility_params[2]
x2_star = x_star[2, :]
u2_star = utility_cobb_douglas(x2_star, α2)
# Consumer 2's coordinates: (ω_total - x1)
x2_2_ic = range(0.01, ω_total[1], length=200)
y2_ic = (u2_star ./ (x2_2_ic .^ α2[1])) .^ (1/α2[2])
# Transform to Consumer 1's coordinates
x1_from_2 = ω_total[1] .- x2_2_ic
y1_from_2 = ω_total[2] .- y2_ic
valid2 = (y1_from_2 .>= 0) .& (y1_from_2 .<= ω_total[2]) .& (x1_from_2 .>= 0)
plot!(x1_from_2[valid2], y1_from_2[valid2],
label="IC Consumer 2", color=:orange, linewidth=1.5, alpha=0.7)
end
# Contract curve (locus of Pareto efficient allocations)
# For Cobb-Douglas, derived from MRS equality
α1, α2 = economy.utility_params[1], economy.utility_params[2]
a = α1[1] / α1[2] # Consumer 1's MRS coefficient
b = α2[1] / α2[2] # Consumer 2's MRS coefficient
x1_cc = range(0.01, ω_total[1] - 0.01, length=200)
# Contract curve: x₂¹/x₁¹ × a = (ω₂ - x₂¹)/(ω₁ - x₁¹) × b
# Solving for x₂¹:
x2_cc = (a .* ω_total[2] .* x1_cc) ./ (b .* (ω_total[1] .- x1_cc) .+ a .* x1_cc)
valid_cc = (x2_cc .>= 0) .& (x2_cc .<= ω_total[2])
plot!(x1_cc[valid_cc], x2_cc[valid_cc],
label="Contract Curve", color=:green, linewidth=2)
# Plot points
scatter!([ω1[1]], [ω1[2]],
label="Endowment (ω)", markersize=10, color=:red, markershape=:diamond)
scatter!([x1_star[1]], [x1_star[2]],
label="Equilibrium (x*)", markersize=10, color=:green, markershape=:circle)
# Add second axis labels at top-right
annotate!(ω_total[1], ω_total[2] + 0.15,
text("← Consumer 2", 8, :right))
return plt
end
# ============================================
# MAIN: Run Example Economy
# ============================================
function main()
println("=" ^ 60)
println("WALRASIAN GENERAL EQUILIBRIUM: 2×2 PURE EXCHANGE ECONOMY")
println("=" ^ 60)
println()
# Define the economy
# 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 (I × L)
[[0.6, 0.4], [0.3, 0.7]], # Cobb-Douglas parameters
:cobb_douglas
)
println("ECONOMY SPECIFICATION")
println("-" ^ 40)
println("Number of goods: ", economy.n_goods)
println("Number of consumers: ", economy.n_consumers)
println("Utility type: Cobb-Douglas")
println()
for i in 1:economy.n_consumers
α = economy.utility_params[i]
ω = economy.endowments[i, :]
println("Consumer $i:")
println(" Utility: u(x₁,x₂) = x₁^$(α[1]) × x₂^$(α[2])")
println(" Endowment: ω = ($(ω[1]), $(ω[2]))")
end
println()
println("TOTAL RESOURCES")
println("-" ^ 40)
total = vec(sum(economy.endowments, dims=1))
println("Total endowment: ($(total[1]), $(total[2]))")
# Solve for equilibrium
println()
println("SOLVING FOR WALRASIAN EQUILIBRIUM...")
println("-" ^ 40)
p_star = solve_equilibrium(economy)
x_star = equilibrium_allocations(p_star, economy)
@printf("Equilibrium prices: p* = (%.4f, %.4f)\n", p_star[1], p_star[2])
@printf("Relative price p₂/p₁ = %.4f\n", p_star[2] / p_star[1])
println()
println("EQUILIBRIUM ALLOCATIONS")
println("-" ^ 40)
for i in 1:economy.n_consumers
x_i = x_star[i, :]
@printf("Consumer %d: x* = (%.4f, %.4f)\n", i, x_i[1], x_i[2])
end
# Verify market clearing
println()
println("VERIFICATION")
println("-" ^ 40)
total_alloc = vec(sum(x_star, dims=1))
@printf("Total allocation: (%.4f, %.4f)\n", total_alloc[1], total_alloc[2])
@printf("Total endowment: (%.4f, %.4f)\n", total[1], total[2])
market_clears = isapprox(total_alloc, total, atol=1e-8)
println("Market clearing: ", market_clears ? "✓ YES" : "✗ NO")
# Check Walras' Law
walras_value = verify_walras_law(p_star, economy)
@printf("Walras' Law (p·z should be 0): %.2e\n", walras_value)
# Check Pareto efficiency
mrs_values = check_pareto_efficiency(x_star, economy)
println()
println("PARETO EFFICIENCY CHECK")
println("-" ^ 40)
for i in 1:economy.n_consumers
@printf("Consumer %d MRS₁₂ = %.4f\n", i, mrs_values[i])
end
mrs_equal = isapprox(mrs_values[1], mrs_values[2], atol=1e-8)
println("MRS equality (Pareto efficient): ", mrs_equal ? "✓ YES" : "✗ NO")
# Compute utility levels
println()
println("WELFARE ANALYSIS")
println("-" ^ 40)
# Utility at endowment
u_endow = compute_utilities(economy.endowments, economy)
# Utility at equilibrium
u_equil = compute_utilities(x_star, economy)
for i in 1:economy.n_consumers
@printf("Consumer %d: u(ω)=%.4f → u(x*)=%.4f (gain: %.1f%%)\n",
i, u_endow[i], u_equil[i], 100*(u_equil[i]/u_endow[i] - 1))
end
# Create Edgeworth box
println()
println("GENERATING EDGEWORTH BOX DIAGRAM...")
println("-" ^ 40)
plt = plot_edgeworth_box(economy, p_star, x_star, show_indifference=true)
savefig(plt, "edgeworth_box_equilibrium.png")
println("Saved: edgeworth_box_equilibrium.png")
display(plt)
println()
println("=" ^ 60)
println("EQUILIBRIUM ANALYSIS COMPLETE")
println("=" ^ 60)
return economy, p_star, x_star
end
# Run if executed directly
if abspath(PROGRAM_FILE) == @__FILE__
main()
end
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:
# ============================================
# 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: y = log(p_rest), so p_rest = exp(y)
function excess_demand_reduced!(F, y)
p_rest = exp.(y) # 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
endStep 6: Provide Complete Example
# ============================================
# 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
# ============================================
# 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
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/
- 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