Skip to content

Commit f65b8b6

Browse files
authored
Merge pull request #8 from SebKrantz/main
Update.
2 parents 5880515 + 6a0d9a7 commit f65b8b6

17 files changed

+291
-226
lines changed

Project.toml

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
name = "OptimalTransportNetworks"
22
uuid = "e2b46e68-897f-4e4e-ba36-a93c9789fd96"
33
authors = ["Sebastian Krantz <sebastian.krantz@graduateinstitute.ch>"]
4-
version = "0.1.4"
4+
version = "0.1.7"
55

66
[deps]
77
Dierckx = "39dd38d3-220a-591b-8e3c-4c3a8c710a94"
88
Ipopt = "b6b21f68-93f8-5de0-b562-5493be1d77c9"
99
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
1010
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
11-
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
11+
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
1212
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1313
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1414
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
15+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
1516
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1617

1718
[compat]
18-
julia = "1.8.5"
19-
Dierckx = "0.5.0"
20-
Ipopt = "1.4.0"
21-
JuMP = "1.20.0"
22-
MathOptInterface = "1.0.0"
23-
Plots = "1.19.0"
24-
Random = "1.0.0"
25-
SparseArrays = "1.0.0"
26-
Statistics = "1.0.0"
27-
LinearAlgebra = "1.0.0"
19+
julia = "1.8.5, 1.9"
20+
Ipopt = "1.4.0, 2"
21+
JuMP = "1.20.0, 2"
22+
LinearAlgebra = "1.0.0, 2"
23+
NearestNeighbors = "0.4.10, 1"
24+
Plots = "1.19.0, 2"
25+
Dierckx = "0.5.0, 1"
26+
Random = "1.0.0, 2"
27+
SparseArrays = "1.0.0, 2"
28+
StaticArrays = "1.1.0, 2"
29+
Statistics = "1.0.0, 2"
30+
31+
32+
2833

2934

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
# OptimalTransportNetworks.jl
2+
3+
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://sebkrantz.github.io/OptimalTransportNetworks.jl/stable)
4+
[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://sebkrantz.github.io/OptimalTransportNetworks.jl/dev)
5+
[![Julia version](https://juliahub.com/docs/General/OptimalTransportNetworks/stable/version.svg)](https://juliahub.com/ui/Packages/General/OptimalTransportNetworks)
6+
27
**Optimal Transport Networks in Spatial Equilibrium - in Julia**
38

9+
410
Modern Julia ([JuMP](https://github.com/jump-dev/JuMP.jl)) translation of the [MATLAB OptimalTransportNetworkToolbox (v1.0.4b)](https://github.com/SebKrantz/OptimalTransportNetworkToolbox) implementing the quantitative spatial economic model of:
511

612
Fajgelbaum, P. D., & Schaal, E. (2020). Optimal transport networks in spatial equilibrium. *Econometrica, 88*(4), 1411-1452.
713

814
The model/software uses duality principles to optimize over the space of networks, nesting an optimal flows problem and a neoclasical general-equilibrium trade model into a global network design problem to derive the optimal (welfare maximizing) transport network (extension) from any primitive set of economic fundamantals [population per location, productivity per location for each of *N* traded goods, endowment of a non-traded good, and (optionally) a pre-existing transport network].
915

10-
For more information about the model see [this folder](https://github.com/SebKrantz/OptimalTransportNetworks.jl/tree/main/misc/paper_materials) and the [MATLAB User Guide](https://raw.githubusercontent.com/SebKrantz/OptimalTransportNetworkToolbox/main/docs/User%20Guide.pdf).
16+
For more information about the model see [this folder](https://github.com/SebKrantz/OptimalTransportNetworkToolbox/tree/main/docs/paper_materials) and the [MATLAB User Guide](https://raw.githubusercontent.com/SebKrantz/OptimalTransportNetworkToolbox/main/docs/User%20Guide.pdf).
1117

1218
The model is the first of its kind and a pathbreaking contribution towards the welfare maximizing planning of transport infrastructure. Its creation has been funded by the European Union through an [ERC Research Grant](https://cordis.europa.eu/project/id/804095). The author of this Julia library has no personal connections to the authors, but has used their Matlab library for research purposes and belives that it deserves an accessible open-source implementation. Community efforts to further improve the code are welcome. In particular, there is a probabilistic extenstion to solving the model using MCMC methods which may be more suitable for large networks, implemented in:
1319

__init__.jl

Lines changed: 0 additions & 2 deletions
This file was deleted.

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Fajgelbaum, P. D., & Schaal, E. (2020). Optimal transport networks in spatial eq
88

99
The model/software uses duality principles to optimize over the space of networks, nesting an optimal flows problem and a neoclasical general-equilibrium trade model into a global network design problem to derive the optimal (welfare maximizing) transport network (extension) from any primitive set of economic fundamantals [population per location, productivity per location for each of *N* traded goods, endowment of a non-traded good, and (optionally) a pre-existing transport network].
1010

11-
For more information about the model see [this folder](https://github.com/SebKrantz/OptimalTransportNetworks.jl/tree/main/misc/paper_materials) and the [MATLAB User Guide](https://raw.githubusercontent.com/SebKrantz/OptimalTransportNetworkToolbox/main/docs/User%20Guide.pdf).
11+
For more information about the model see [this folder](https://github.com/SebKrantz/OptimalTransportNetworkToolbox/tree/main/docs/paper_materials) and the [MATLAB User Guide](https://raw.githubusercontent.com/SebKrantz/OptimalTransportNetworkToolbox/main/docs/User%20Guide.pdf).
1212

1313
The model is the first of its kind and a pathbreaking contribution towards the welfare maximizing planning of transport infrastructure. Its creation has been funded by the European Union through an [ERC Research Grant](https://cordis.europa.eu/project/id/804095). The author of this Julia library has no personal connections to the authors, but has used their Matlab library for research purposes and belives that it deserves an accessible open-source implementation. Community efforts to further improve the code are welcome. In particular, there is a probabilistic extenstion to solving the model using MCMC methods which may be more suitable for large networks, implemented in:
1414

examples/example04.jl

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ param, graph = create_graph(param, w, h, type = "map")
1616
# ----------------
1717
# Draw populations
1818

19-
param[:Zjn] = ones(param[:J], 1) .* 1e-3 # matrix of productivity (not 0 to avoid numerical glitches)
20-
param[:Lj] = ones(param[:J]) .* 1e-3 # matrix of population
19+
param[:Zjn] = ones(param[:J], 1) .* 1e-6 # matrix of productivity (not 0 to avoid numerical glitches)
20+
param[:Lj] = ones(param[:J]) .* 1e-6 # matrix of population
2121

2222
Ni = find_node(graph, ceil(w/2), ceil(h/2)) # center
2323
param[:Zjn][Ni] = 1 # more productive node
@@ -29,15 +29,15 @@ for i in 1:ncities-1
2929
newdraw = false
3030
while newdraw == false
3131
j = round(Int, 1 + rand() * (param[:J] - 1))
32-
if param[:Lj][j] <= 1e-3
32+
if param[:Lj][j] <= 1e-6
3333
newdraw = true
3434
param[:Lj][j] = 1
3535
end
3636
end
3737
end
3838

3939
param[:hj] = param[:Hj] ./ param[:Lj]
40-
param[:hj][param[:Lj] .== 1e-3] .= 1 # catch errors in places with infinite housing per capita
40+
param[:hj][param[:Lj] .== 1e-6] .= 1 # catch errors in places with infinite housing per capita
4141

4242

4343
# --------------
@@ -68,6 +68,14 @@ geography = (z = z, obstacles = obstacles)
6868
# to changes in elevation in building costs (symmetric up/down)
6969
graph = apply_geography(graph, geography, alpha_up_i = 10, alpha_down_i = 10)
7070

71+
# Plot endowments
72+
plot_graph(graph, aspect_ratio = 3/4,
73+
geography = geography, obstacles = true,
74+
mesh = true, mesh_transparency = 0.2,
75+
node_sizes = param[:Lj], node_shades = param[:Zjn],
76+
node_color = :seismic,
77+
edge_min_thickness = 1, edge_max_thickness = 4)
78+
7179
# =======================
7280
# COMPUTE OPTIMAL NETWORK
7381
# =======================
@@ -78,24 +86,12 @@ graph = apply_geography(graph, geography, alpha_up_i = 10, alpha_down_i = 10)
7886
# PLOT RESULTS
7987
# ============
8088

81-
sizes = 2 .* results[:cj] .* (param[:Lj] .> 1e-3) / maximum(results[:cj])
82-
shades = results[:cj] .* (param[:Lj] .> 1e-3) / maximum(results[:cj])
89+
sizes = 2 .* results[:cj] .* (param[:Lj] .> 1e-6) / maximum(results[:cj])
90+
shades = results[:cj] .* (param[:Lj] .> 1e-6) / maximum(results[:cj])
8391

84-
plot_graph(graph, results[:Ijk],
92+
plot_graph(graph, results[:Ijk], aspect_ratio = 3/4,
8593
geography = geography, obstacles = true,
8694
mesh = true, mesh_transparency = 0.2,
8795
node_sizes = sizes, node_shades = shades,
8896
edge_min_thickness = 1, edge_max_thickness = 4)
8997

90-
91-
# sizes = param[:Lj] .+ 1
92-
# shades = param[:Zjn]
93-
94-
# plot_graph(graph, # results[:Ijk], edges = false,
95-
# geography = geography, obstacles = true,
96-
# mesh = true, mesh_transparency = 0.2,
97-
# node_sizes = sizes, node_shades = shades,
98-
# node_color = :seismic,
99-
# edge_min_thickness = 1, edge_max_thickness = 4)
100-
101-

examples/paper_example04.jl

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,44 @@
11
using OptimalTransportNetworks
22
using Random
3-
using LinearAlgebra
43
using Plots
54

65
# Initialize parameters
76
param = init_parameters(K = 100, labor_mobility = false)
87
width, height = 13, 13
98

109
# Create graph
11-
param, g = create_graph(param, width, height, type = "map")
10+
param, g0 = create_graph(param, width, height, type = "map")
1211

1312
# Set fundamentals
14-
Random.seed!(5)
15-
minpop = minimum(param[:Lj])
16-
param[:Zjn] = fill(minpop, g[:J], 1) # matrix of productivity
17-
param[:Lj] = fill(1e-6, g[:J]) # matrix of population
13+
param[:Zjn] = ones(param[:J], 1) .* 1e-7 # matrix of productivity (not 0 to avoid numerical glitches)
14+
param[:Lj] = ones(param[:J]) .* 1e-7 # matrix of population
1815

19-
Ni = find_node(g, 5, 5) # center
16+
Ni = find_node(g0, ceil(width/2), ceil(height/2)) # center
2017
param[:Zjn][Ni] = 1 # more productive node
2118
param[:Lj][Ni] = 1 # more productive node
2219

20+
Random.seed!(10) # use 5, 8, 10 or 11
2321
nb_cities = 20 # draw a number of random cities in space
2422
for i in 1:nb_cities-1
2523
newdraw = false
2624
while newdraw == false
27-
j = round(Int, 1 + rand() * (g[:J] - 1))
28-
if param[:Lj][j] <= minpop
25+
j = round(Int, 1 + rand() * (param[:J] - 1))
26+
if param[:Lj][j] <= 1e-7
2927
newdraw = true
3028
param[:Lj][j] = 1
3129
end
3230
end
3331
end
3432

3533
param[:hj] = param[:Hj] ./ param[:Lj]
36-
param[:hj][param[:Lj] .== 1e-6] .= 1
34+
param[:hj][param[:Lj] .== 1e-7] .= 1 # catch errors in places with infinite housing per capita
3735

3836
# Draw geography
39-
z = zeros(g[:J]) # altitude of each node
40-
37+
z = zeros(param[:J]) # altitude of each node
4138
geographies = Dict()
4239
geographies[:blank] = (z = z, obstacles = nothing)
43-
g = apply_geography(g, geographies[:blank])
4440

45-
param0 = param # store initial params
46-
g0 = g # store initial graph
41+
g = apply_geography(g0, geographies[:blank])
4742

4843
# Blank geography
4944
results = Dict(:blank => optimal_network(param, g))
@@ -53,13 +48,12 @@ mountain_size = 0.75
5348
mountain_height = 1
5449
mount_x = 10
5550
mount_y = 10
56-
geographies[:mountain] = (z = mountain_height * exp.(-((g[:x] .- mount_x).^2 .+ (g[:y] .- mount_y).^2) / (2 * mountain_size^2)),
51+
geographies[:mountain] = (z = mountain_height * exp.(-((g0[:x] .- mount_x).^2 .+ (g0[:y] .- mount_y).^2) / (2 * mountain_size^2)),
5752
obstacles = nothing)
58-
g = apply_geography(g, geographies[:mountain], alpha_up_i = 10, alpha_down_i = 10)
53+
g = apply_geography(g0, geographies[:mountain], alpha_up_i = 10, alpha_down_i = 10)
5954
results[:mountain] = optimal_network(param, g)
6055

6156
# Adding river and access by land
62-
g = g0
6357
geographies[:river] = (z = geographies[:mountain].z,
6458
obstacles = [6 + (1-1)*width 6 + (2-1)*width;
6559
6 + (2-1)*width 6 + (3-1)*width;
@@ -69,13 +63,11 @@ geographies[:river] = (z = geographies[:mountain].z,
6963
11 + (5-1)*width 12 + (5-1)*width;
7064
12 + (5-1)*width 13 + (5-1)*width])
7165

72-
g = apply_geography(g, geographies[:river], across_abstacle_delta_i = Inf,
73-
alpha_up_i = 10, alpha_down_i = 10)
66+
g = apply_geography(g0, geographies[:river], alpha_up_i = 10, alpha_down_i = 10)
7467
results[:river] = optimal_network(param, g)
7568

7669
# Reinit and put another river and bridges
77-
g = g0
78-
geographies[:bridges] = (z = mountain_height * exp.(-((g[:x] .- mount_x).^2 .+ (g[:y] .- mount_y).^2) / (2 * mountain_size^2)),
70+
geographies[:bridges] = (z = mountain_height * exp.(-((g0[:x] .- mount_x).^2 .+ (g0[:y] .- mount_y).^2) / (2 * mountain_size^2)),
7971
obstacles = [6 + (1-1)*width 6 + (2-1)*width;
8072
6 + (2-1)*width 6 + (3-1)*width;
8173
6 + (3-1)*width 7 + (4-1)*width;
@@ -86,13 +78,12 @@ geographies[:bridges] = (z = mountain_height * exp.(-((g[:x] .- mount_x).^2 .+ (
8678
11 + (5-1)*width 12 + (5-1)*width;
8779
12 + (5-1)*width 13 + (5-1)*width])
8880

89-
g = apply_geography(g, geographies[:bridges], alpha_up_i = 10, alpha_down_i = 10,
90-
across_abstacle_delta_i = 2, along_obstacle_delta_i = Inf)
81+
g = apply_geography(g0, geographies[:bridges], alpha_up_i = 10, alpha_down_i = 10,
82+
across_obstacle_delta_i = 2)
9183
results[:bridges] = optimal_network(param, g)
9284

9385
# Allowing for water transport
94-
g = g0
95-
geographies[:water_transport] = (z = mountain_height * exp.(-((g[:x] .- mount_x).^2 .+ (g[:y] .- mount_y).^2) / (2 * mountain_size^2)),
86+
geographies[:water_transport] = (z = mountain_height * exp.(-((g0[:x] .- mount_x).^2 .+ (g0[:y] .- mount_y).^2) / (2 * mountain_size^2)),
9687
obstacles = [6 + (1-1)*width 6 + (2-1)*width;
9788
6 + (2-1)*width 6 + (3-1)*width;
9889
6 + (3-1)*width 7 + (4-1)*width;
@@ -103,8 +94,10 @@ geographies[:water_transport] = (z = mountain_height * exp.(-((g[:x] .- mount_x)
10394
11 + (5-1)*width 12 + (5-1)*width;
10495
12 + (5-1)*width 13 + (5-1)*width])
10596

106-
g = apply_geography(g, geographies[:water_transport], alpha_up_i = 10, alpha_down_i = 10,
107-
across_abstacle_delta_i = 2, along_obstacle_delta_i = 0.5)
97+
g = apply_geography(g0, geographies[:water_transport], alpha_up_i = 10, alpha_down_i = 10,
98+
across_abstacle_delta_i = 2,
99+
along_obstacle_delta_i = 0.5,
100+
along_abstacle_delta_tau = 1)
108101
results[:water_transport] = optimal_network(param, g)
109102

110103
# Increasing returns to transport
@@ -120,12 +113,12 @@ plots = Vector{Any}(undef, length(simulation))
120113
i = 0
121114
for s in simulation
122115
i += 1
123-
plots[i] = plot_graph(g, results[Symbol(s)][:Ijk],
116+
plots[i] = plot_graph(g0, results[Symbol(s)][:Ijk],
124117
geography = geographies[Symbol(s)], obstacles = obstacles[i] == "on",
125118
mesh = true, mesh_transparency = 0.2,
126-
node_sizes = results[Symbol(s)][:cj] .* (param[:Lj] .> minpop),
127-
node_shades = param[:Zjn],
128-
edge_min_thickness = 1.5)
119+
node_sizes = results[Symbol(s)][:cj] .* (param[:Lj] .> 1e-7),
120+
node_shades = param[:Zjn], node_color = :seismic,
121+
edge_min_thickness = 1, edge_max_thickness = 4)
129122
title!(plots[i], "Geography $(s)")
130123
end
131124

-2.11 MB
Binary file not shown.
-899 KB
Binary file not shown.
-2.71 MB
Binary file not shown.
-7.51 MB
Binary file not shown.

0 commit comments

Comments
 (0)