2. Loc 5: UFL Heuristics¶

ISE 754, Fall 2024

Package Used: No new packages used.

1. UFL Construction and Improvement Procedures¶

UFL ADD Construction Procedure¶

Data: Asheville, Statesville, Greensboro, Raleigh, and Wilmington

In [1]:
d1(x1, x2) = length(x1) == length(x2) ? sum(abs.(x1 .- x2)) : 
    error("Inputs not same length.")
D1(X₁, X₂) = [d1(i, j) for i in eachrow(X₁), j in eachrow(X₂)]

P = [50 150 220 295 420]'
r, f = 1, 1
w = r * f
k = [150, 200, 150, 150, 200]
C = w * D1(P, P)
Out[1]:
5×5 Matrix{Int64}:
   0  100  170  245  370
 100    0   70  145  270
 170   70    0   75  200
 245  145   75    0  125
 370  270  200  125    0
In [2]:
# Find first NF to add: 3
N = 1:size(C, 1)
hcat(N, k, sum(C, dims=2), k + sum(C, dims=2))
Out[2]:
5×4 Matrix{Int64}:
 1  150  885  1035
 2  200  585   785
 3  150  515   665
 4  150  590   740
 5  200  965  1165
In [3]:
y = [3]
setdiff(N, y)
Out[3]:
4-element Vector{Int64}:
 1
 2
 4
 5
In [4]:
# Find next NF to add: 1
for i in setdiff(N, y)
    y′ = vcat(y, i)
        [println("    ",r, ":",k[r],C[r, :]) for r in y′]
        println("    ",minimum(C[y′, :], dims=1))
    TC′ = sum(k[y′]) + sum(minimum(C[y′, :], dims=1))
        println(i, " : ", sum(k[y′]), " + ", sum(minimum(C[y′, :], dims=1))," = ", TC′)
end
    3:150[170, 70, 0, 75, 200]
    1:150[0, 100, 170, 245, 370]
    [0 70 0 75 200]
1 : 300 + 345 = 645
    3:150[170, 70, 0, 75, 200]
    2:200[100, 0, 70, 145, 270]
    [100 0 0 75 200]
2 : 350 + 375 = 725
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    [170 70 0 0 125]
4 : 300 + 365 = 665
    3:150[170, 70, 0, 75, 200]
    5:200[370, 270, 200, 125, 0]
    [170 70 0 75 0]
5 : 350 + 315 = 665
In [5]:
y = vcat(y, 1)
Out[5]:
2-element Vector{Int64}:
 3
 1
In [6]:
# Find next NF to add: none reduce TC, so STOP
for i in setdiff(N, y)
    y′ = vcat(y, i)
        [println("    ",r, ":",k[r],C[r, :]) for r in y′]
        println("    ",minimum(C[y′, :], dims=1))
    TC′ = sum(k[y′]) + sum(minimum(C[y′, :], dims=1))
        println(i, " : ", sum(k[y′]), " + ", sum(minimum(C[y′, :], dims=1))," = ", TC′)
end
    3:150[170, 70, 0, 75, 200]
    1:150[0, 100, 170, 245, 370]
    2:200[100, 0, 70, 145, 270]
    [0 0 0 75 200]
2 : 500 + 275 = 775
    3:150[170, 70, 0, 75, 200]
    1:150[0, 100, 170, 245, 370]
    4:150[245, 145, 75, 0, 125]
    [0 70 0 0 125]
4 : 450 + 195 = 645
    3:150[170, 70, 0, 75, 200]
    1:150[0, 100, 170, 245, 370]
    5:200[370, 270, 200, 125, 0]
    [0 70 0 75 0]
5 : 500 + 145 = 645
In [7]:
# Write as a general procedure, given k and C as inputs
function ufladd(k, C)
    fTC(y) = sum(k[y]) + sum(minimum(C[y, :], dims=1))
    y = Int[]
    TCᵒ, done = Inf, false
    while !done
        TC, i = Inf, nothing               # Stops if y = all NF
        for i′ = setdiff(1:size(C, 1), y)  # since i′ = []
            TC′ = fTC(vcat(y, i′))
            if TC′ < TC
                TC, i = TC′, i′
            end
        end
        if TC < TCᵒ                     # TC = Inf if y = all NF
            TCᵒ, y = TC, push!(y, i)
        else
            done = true
        end
    end
    return y, TCᵒ
end

ufladd(k, C)
Out[7]:
([3, 1], 645)

UFL DROP Construction Procedure¶

Using same data as UFL ADD

In [8]:
# Calc TC with no dropped NFs
y = 1:size(C, 1)
TC = sum(k[y]) + sum(minimum(C[y, :], dims=1))
Out[8]:
850
In [9]:
# Find first NF to drop: 2
for i in y
    y′ = setdiff(y, i)
        [println("    ",r, ":",k[r],C[r, :]) for r in y′]
        println("    ",minimum(C[y′, :], dims=1))
    TC′ = sum(k[y′]) + sum(minimum(C[y′, :], dims=1))
        println(i, " : ", sum(k[y′]), " + ", sum(minimum(C[y′, :], dims=1))," = ", TC′)
end
    2:200[100, 0, 70, 145, 270]
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    5:200[370, 270, 200, 125, 0]
    [100 0 0 0 0]
1 : 700 + 100 = 800
    1:150[0, 100, 170, 245, 370]
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    5:200[370, 270, 200, 125, 0]
    [0 70 0 0 0]
2 : 650 + 70 = 720
    1:150[0, 100, 170, 245, 370]
    2:200[100, 0, 70, 145, 270]
    4:150[245, 145, 75, 0, 125]
    5:200[370, 270, 200, 125, 0]
    [0 0 70 0 0]
3 : 700 + 70 = 770
    1:150[0, 100, 170, 245, 370]
    2:200[100, 0, 70, 145, 270]
    3:150[170, 70, 0, 75, 200]
    5:200[370, 270, 200, 125, 0]
    [0 0 0 75 0]
4 : 700 + 75 = 775
    1:150[0, 100, 170, 245, 370]
    2:200[100, 0, 70, 145, 270]
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    [0 0 0 0 125]
5 : 650 + 125 = 775
In [10]:
y = setdiff(y, 2)
Out[10]:
4-element Vector{Int64}:
 1
 3
 4
 5
In [11]:
# Find next NF to drop: 4 or 5, pick 4
for i in y
    y′ = setdiff(y, i)
        [println("    ",r, ":",k[r],C[r, :]) for r in y′]
        println("    ",minimum(C[y′, :], dims=1))
    TC′ = sum(k[y′]) + sum(minimum(C[y′, :], dims=1))
        println(i, " : ", sum(k[y′]), " + ", sum(minimum(C[y′, :], dims=1))," = ", TC′)
end
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    5:200[370, 270, 200, 125, 0]
    [170 70 0 0 0]
1 : 500 + 240 = 740
    1:150[0, 100, 170, 245, 370]
    4:150[245, 145, 75, 0, 125]
    5:200[370, 270, 200, 125, 0]
    [0 100 75 0 0]
3 : 500 + 175 = 675
    1:150[0, 100, 170, 245, 370]
    3:150[170, 70, 0, 75, 200]
    5:200[370, 270, 200, 125, 0]
    [0 70 0 75 0]
4 : 500 + 145 = 645
    1:150[0, 100, 170, 245, 370]
    3:150[170, 70, 0, 75, 200]
    4:150[245, 145, 75, 0, 125]
    [0 70 0 0 125]
5 : 450 + 195 = 645
In [12]:
y = setdiff(y, 4)
Out[12]:
3-element Vector{Int64}:
 1
 3
 5
In [13]:
# Find next NF to add: none reduce TC, so STOP
for i in y
    y′ = setdiff(y, i)
        [println("    ",r, ":",k[r],C[r, :]) for r in y′]
        println("    ",minimum(C[y′, :], dims=1))
    TC′ = sum(k[y′]) + sum(minimum(C[y′, :], dims=1))
        println(i, " : ", sum(k[y′]), " + ", sum(minimum(C[y′, :], dims=1))," = ", TC′)
end
    3:150[170, 70, 0, 75, 200]
    5:200[370, 270, 200, 125, 0]
    [170 70 0 75 0]
1 : 350 + 315 = 665
    1:150[0, 100, 170, 245, 370]
    5:200[370, 270, 200, 125, 0]
    [0 100 170 125 0]
3 : 350 + 395 = 745
    1:150[0, 100, 170, 245, 370]
    3:150[170, 70, 0, 75, 200]
    [0 70 0 75 200]
5 : 300 + 345 = 645

Download file ufl_heuristics.jl posted along with this notebook, and then use include it to input all of its functions for use:

In [14]:
include("ufl_heuristics.jl")

ufldrop(k, C)
Out[14]:
([1, 3, 5], 645)

UFL Exchange Improvement Procedure¶

In [15]:
# First use UFLADD to construct a solution `y` that can then be improved
y, TC = ufladd(k, C)
uflxchg(k, C, y)
Out[15]:
([4, 1], 600)

UFL Hybrid Procedure¶

In [16]:
ufl(k, C)
  Add: 645
 Xchg: 600
  Add: 600
 Drop: 600
Out[16]:
([4, 1], 600)

p-Median Construction Procedure¶

In [17]:
p = 2
pmedian(p, C)
Out[17]:
([2, 5], 295)

2. Ex: NC & SC 10K+ Cities¶

In [18]:
function dgc(xy₁, xy₂; unit=:mi)
    length(xy₁) == length(xy₂) == 2 || error("Inputs must have length 2.")
    unit in [:mi, :km] || error("Unit must be :mi or :km")

    Δx, Δy = xy₂[1] - xy₁[1], xy₂[2] - xy₁[2]
    a = sind(Δy / 2)^2 + cosd(xy₁[2]) * cosd(xy₂[2]) * sind(Δx / 2)^2
    2 * asin(min(sqrt(a), 1.0)) * (unit == :mi ? 3958.75 : 6371.00)
end

Dgc(X₁, X₂) = [dgc(i, j) for i in eachrow(X₁), j in eachrow(X₂)]

function lonlat2name(X, D, name, st)
    for i = 1:size(D, 1)
        d = D[i, :]
        println("NF", i, ": ", round(minimum(d)), " miles from ", 
            name[argmin(d)], ", ", st[argmin(d)])
    end
end
Out[18]:
lonlat2name (generic function with 1 method)
In [19]:
using Logjam.DataTools, DataFrames

include("ufl_heuristics.jl")

df = filter!(r -> any(i -> r.STFIP == i, st2fips.([:NC,:SC])) && 
    r.POP > 10_000, usplace())
select!(df, :NAME, :ST, :LAT, :LON, :POP)

P = hcat(df.LON, df.LAT)
w = df.POP

D = Dgc(P, P)
C = w[:]' .* D
Out[19]:
155×155 Matrix{Float64}:
 0.0        9.52936e5  1.26403e7  1.93727e6  …      2.69232e6      1.87705e6
 1.14833e6  0.0        1.92483e7  3.15753e6         4.13654e6      2.61596e6
 2.19587e6  2.77484e6  0.0        1.27228e6         1.05608e6      2.38361e6
 1.66736e6  2.25519e6  6.30335e6  0.0               2.13336e6      2.73321e6
 1.05173e6  8.59114e5  1.6674e7   2.34719e6         3.88762e6      2.9804e6
 1.3553e6   4.99219e5  1.98684e7  3.0905e6   …      4.45741e6      3.11635e6
 1.24345e6  6.26805e5  1.87838e7  2.8265e6          4.27439e6      3.09141e6
 6.16389e5  1.44203e6  9.47312e6  1.59848e6         1.88681e6      1.49064e6
 4.12052e5  1.29487e6  1.02697e7  1.54549e6         2.19322e6      1.74832e6
 1.38335e6  6.76204e5  1.95452e7  2.96233e6         4.45698e6      3.22952e6
 3.7931e6   2.29626e6  3.3756e7   5.77613e6  …      7.64915e6      5.52991e6
 1.17327e6  1.71006e5  1.93483e7  3.24807e6         4.09446e6      2.4837e6
 9.29875e5  1.71426e6  7.57604e6  1.37237e6         1.49366e6      1.51704e6
 ⋮                                           ⋱                 
 2.24114e6  2.76844e6  5.1776e6   2.09199e6         2.53149e5      1.68267e6
 1.89325e6  2.16672e6  1.28539e7  3.04903e6         1.95162e6      1.52803e5
 2.10641e6  1.40063e6  2.23322e7  4.30376e6  …      4.38312e6      2.09763e6
 1.71327e6  2.01857e6  1.26076e7  2.91609e6         1.94286e6  73629.6
 1.73462e6  2.04375e6  1.25174e7  2.91474e6         1.91444e6  79207.6
 2.04826e6  2.59485e6  5.7289e6   2.05966e6         2.46599e5      1.45274e6
 2.19798e6  1.48578e6  2.26852e7  4.39909e6         4.44418e6      2.12091e6
 2.67704e6  2.33399e6  2.11185e7  4.53311e6  …      3.84864e6      1.50507e6
 2.03388e6  2.60771e6  4.47688e6  1.85018e6     51639.7            1.63983e6
 8.49583e5  1.60037e6  8.91089e6  1.70392e6         1.61541e6      1.25948e6
 2.05941e6  2.62572e6  4.65012e6  1.89601e6         0.0            1.62772e6
 1.771e6    2.04819e6  1.29458e7  2.99623e6         2.00773e6      0.0
In [20]:
# Check broadcasting:
w[1] * D[2, 1], w[2] * D[1, 2]
Out[20]:
(1.148331477137756e6, 952936.2233599344)
In [21]:
y, TC = ufladd(0, C, p = 2)
y, TC = uflxchg(0, C, y)
Out[21]:
([6, 8], 3.935223149761722e8)
In [22]:
y, TC = pmedian(2, C)
Out[22]:
([6, 8], 3.935223149761722e8)
In [23]:
lonlat2name(P[y, :], Dgc(P[y, :], hcat(df.LON, df.LAT)), df.NAME, df.ST)
NF1: 0.0 miles from Cary, NC
NF2: 0.0 miles from Charlotte, NC

3. Ex: Best Retail Warehouse Locations in U.S.¶

In [24]:
using Logjam.DataTools, DataFrames

include("ufl_heuristics.jl")

df = filter!(r -> r.ISCUS == true && r.POP > 0, uszcta3())
select!(df, :ZCTA3, :LAT, :LON, :POP)

P = hcat(df.LON, df.LAT)
w = df.POP

D = Dgc(P, P)
C = w[:]' .* D
Out[24]:
882×882 Matrix{Float64}:
 0.0        1.27581e6  4.80354e6  2.18512e6  …  9.2409e8   4.91286e7
 3.51217e6  0.0        5.41746e6  2.77588e6     9.257e8    4.92168e7
 1.77188e7  7.25901e6  0.0        3.21568e6     9.08958e8  4.8294e7
 1.24675e7  5.75327e6  4.974e6    0.0           9.21586e8  4.89931e7
 2.25424e7  8.77433e6  9.50444e6  2.97611e6     9.3507e8   4.97385e7
 1.87714e7  6.75069e6  9.60909e6  3.61903e6  …  9.38799e8  4.99417e7
 1.85466e7  6.78733e6  9.43232e6  3.40518e6     9.3798e8   4.98968e7
 2.73191e7  9.9899e6   1.16637e7  4.61751e6     9.44071e8  5.0234e7
 3.57961e7  1.33727e7  1.33056e7  5.41801e6     9.45415e8  5.0311e7
 4.18636e7  1.54333e7  1.51848e7  6.66314e6     9.51715e8  5.06587e7
 3.45029e7  1.23299e7  1.3883e7   6.19699e6  …  9.51841e8  5.06621e7
 3.6616e7   1.33181e7  1.4167e7   6.16469e6     9.5125e8   5.0631e7
 3.60784e7  1.31345e7  1.40081e7  6.05778e6     9.50721e8  5.06018e7
 ⋮                                           ⋱  ⋮          
 1.13975e9  4.14776e8  3.04181e8  1.99091e8  …  8.26229e7  5.98634e6
 1.15063e9  4.18704e8  3.07132e8  2.01052e8     7.39824e7  5.77462e6
 1.15008e9  4.18497e8  3.06983e8  2.00969e8     7.04601e7  5.62251e6
 1.16344e9  4.23344e8  3.10605e8  2.03319e8     7.87306e7  6.13573e6
 1.16173e9  4.22683e8  3.10148e8  2.03096e8     7.00666e7  5.72333e6
 1.0916e9   3.97255e8  2.91128e8  1.90713e8  …  3.955e7    3.32021e6
 1.10906e9  4.0357e8   2.95868e8  1.93833e8     2.8577e7   3.37339e6
 1.03629e9  3.7716e8   2.76135e8  1.8103e8      5.01937e7  2.03863e6
 1.04366e9  3.79836e8  2.78131e8  1.82319e8     4.632e7    2.07917e6
 1.0363e9   3.77165e8  2.76138e8  1.81032e8     5.02776e7  2.04599e6
 1.07985e9  3.92941e8  2.87952e8  1.88747e8  …  0.0        1.86568e6
 1.04058e9  3.78672e8  2.77307e8  1.81874e8     3.38165e7  0.0
In [25]:
y, TC = pmedian(9, C)
Out[25]:
([834, 281, 873, 686, 312, 729, 559, 397, 60], 5.710476191147255e10)
In [26]:
df2 = filter!(r -> r.ISCUS == true && r.POP > 100_000, usplace())
lonlat2name(P[y, :], Dgc(P[y, :], hcat(df2.LON, df2.LAT)), df2.NAME, df2.ST)
NF1: 14.0 miles from Lancaster, CA
NF2: 40.0 miles from Athens-Clarke County unified government (balance), GA
NF3: 4.0 miles from Tacoma, WA
NF4: 58.0 miles from Tyler, TX
NF5: 14.0 miles from Lakeland, FL
NF6: 5.0 miles from Denver, CO
NF7: 17.0 miles from Rockford, IL
NF8: 43.0 miles from Columbus, OH
NF9: 3.0 miles from Newark, NJ

Using Integrated Formulation from previous notebook:

image.png