Skip to content

Commit 4137066

Browse files
committed
Yet more Armington.
1 parent 78b9a4d commit 4137066

2 files changed

Lines changed: 102 additions & 2 deletions

File tree

src/main/optimal_network.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,13 @@ function optimal_network(param, graph; I0=nothing, Il=nothing, Iu=nothing, verbo
100100
recover_allocation = recover_allocation_mobility
101101
end
102102
elseif param.mobility == 0.5 && !param.cong
103-
model = model_partial_mobility(optimizer, auxdata)
104-
recover_allocation = recover_allocation_partial_mobility
103+
if all(sum(param.Zjn .> 0, dims = 2) .<= 1) # Armington case
104+
model = model_partial_mobility_armington(optimizer, auxdata)
105+
recover_allocation = recover_allocation_partial_mobility_armington
106+
else
107+
model = model_partial_mobility(optimizer, auxdata)
108+
recover_allocation = recover_allocation_partial_mobility
109+
end
105110
elseif param.mobility == 0 && !param.cong
106111
if all(sum(param.Zjn .> 0, dims = 2) .<= 1) # Armington case
107112
model = model_fixed_armington(optimizer, auxdata)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# The primal case, with labor mobility within regions, no cross-good congestion, and an Armington (1969) world where each location produces only one good
2+
3+
function model_partial_mobility_armington(optimizer, auxdata)
4+
5+
# Parameters and data
6+
param = auxdata.param
7+
graph = auxdata.graph
8+
region = graph.region
9+
if length(region) != graph.J
10+
error("length(region) = $(length(region)) does not match number of nodes = $(graph.J)")
11+
end
12+
kappa_ex_init = auxdata.kappa_ex
13+
A = auxdata.edges.A
14+
Apos = auxdata.edges.Apos
15+
Aneg = auxdata.edges.Aneg
16+
psigma = (param.sigma - 1) / param.sigma
17+
Hj = param.Hj
18+
Lr = param.Lr
19+
if length(param.omegar) != param.nregions
20+
error("length(param.omegar) = $(length(param.omegar)) does not match number of regions = $(param.nregions)")
21+
end
22+
if length(Lr) != param.nregions
23+
error("Populations Lr = $(length(Lr)) does not match number of regions = $(param.nregions)")
24+
end
25+
26+
# Model
27+
model = Model(optimizer)
28+
set_string_names_on_creation(model, false)
29+
30+
# Variables + bounds
31+
@variable(model, ur[1:param.nregions], container=Array, start = 0.0) # Utility per capita in each region
32+
@variable(model, Cjn[1:graph.J, 1:param.N] >= 1e-8, container=Array, start = 1e-6) # Good specific consumption
33+
@variable(model, Qin[1:graph.ndeg, 1:param.N], container=Array, start = 0.0) # Good specific flow
34+
# NOTE: Fajgelbaum et al (2019) only optimize Lj and distribute it equally for goods with positive productivity
35+
@variable(model, Lj[1:graph.J] >= 1e-8, container=Array) # Total labour
36+
# Calculate start values for Lj
37+
pop_start = (Lr ./ gsum(ones(graph.J), param.nregions, region))[region]
38+
set_start_value.(Lj, pop_start)
39+
40+
# Parameters: to be updated between solves
41+
@variable(model, kappa_ex[i = 1:graph.ndeg] in Parameter(i), container=Array)
42+
set_parameter_value.(kappa_ex, kappa_ex_init)
43+
44+
# Objective
45+
@expression(model, U, sum(param.omegar .* Lr .* ur)) # Overall utility
46+
@objective(model, Max, U)
47+
48+
# Utility constraint (Lj * ur <= ... )
49+
for j in 1:graph.J
50+
Cj = sum(Cjn[j, n]^psigma for n in 1:param.N)^(1 / psigma)
51+
@constraint(model, Lj[j] * ur[region[j]] - (Cj / param.alpha)^param.alpha * (Hj[j] / (1 - param.alpha))^(1 - param.alpha) <= -1e-8)
52+
end
53+
54+
# Balanced flow constraints: same as with unrestricted mobility (no restrictions on goods)
55+
@expression(model, Yjn[j=1:graph.J, n=1:param.N], param.Zjn[j, n] * Lj[j]^param.a)
56+
@constraint(model, Pjn[j in 1:param.J, n in 1:param.N],
57+
Cjn[j, n] + sum(A[j, i] * Qin[i, n] for i in 1:graph.ndeg) -
58+
Yjn[j, n] + sum(
59+
ifelse(Qin[i, n] > 0, Apos[j, i], Aneg[j, i]) *
60+
abs(Qin[i, n])^(1 + param.beta) / kappa_ex[i]
61+
for i in 1:graph.ndeg
62+
) <= -1e-8
63+
)
64+
65+
# Labor resource constraints (within each region)
66+
@constraint(model, -1e-8 .<= gsum(Lj, param.nregions, region) .- Lr .<= 1e-8)
67+
68+
return model
69+
end
70+
71+
function recover_allocation_partial_mobility_armington(model, auxdata)
72+
param = auxdata.param
73+
graph = auxdata.graph
74+
model_dict = model.obj_dict
75+
results = Dict()
76+
77+
results[:welfare] = value(model_dict[:U])
78+
results[:reg_pc_welfare] = value.(model_dict[:ur])
79+
results[:Yjn] = value.(model_dict[:Yjn])
80+
results[:Yj] = dropdims(sum(results[:Yjn], dims=2), dims = 2)
81+
results[:Cjn] = value.(model_dict[:Cjn])
82+
results[:Cj] = dropdims(sum(results[:Cjn] .^ ((param.sigma-1)/param.sigma), dims=2), dims = 2) .^ (param.sigma/(param.sigma-1))
83+
results[:Lj] = value.(model_dict[:Lj])
84+
results[:Ljn] = (param.Zjn .> 0).* results[:Lj]
85+
results[:cj] = ifelse.(results[:Lj] .== 0, 0.0, results[:Cj] ./ results[:Lj])
86+
results[:hj] = ifelse.(results[:Lj] .== 0, 0.0, param.Hj ./ results[:Lj])
87+
results[:uj] = param.u.(results[:cj], results[:hj])
88+
# Prices
89+
results[:Pjn] = shadow_price.(model_dict[:Pjn])
90+
results[:PCj] = dropdims(sum(results[:Pjn] .^ (1-param.sigma), dims=2), dims=2) .^ (1/(1-param.sigma))
91+
# Network flows
92+
results[:Qin] = value.(model_dict[:Qin])
93+
results[:Qjkn] = gen_network_flows(results[:Qin], graph, param.N)
94+
return results
95+
end

0 commit comments

Comments
 (0)