|
| 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