Estimate Migration Flows to Match Net Totals via Linear Programming
Source:R/net_matrix_lp.R
net_matrix_lp.RdSolves for an origin-destination flow matrix that satisfies directional net migration constraints while minimizing the total absolute deviation from a prior matrix. This method uses linear programming with split variables to minimize L1 error, optionally respecting a structural zero mask.
Arguments
- net_tot
A numeric vector of net migration totals for each region. Must sum to zero.
- m
A square numeric matrix providing prior flow estimates. Must have dimensions
length(net_tot) × length(net_tot).- zero_mask
A logical matrix of the same dimensions as
m, whereTRUEindicates forbidden (structurally zero) flows. Defaults to disallowing diagonal flows.- tol
A numeric tolerance for checking that
sum(net_tot) == 0. Default is1e-6.
Value
A named list with components:
nEstimated matrix of flows satisfying the net constraints.
itNumber of iterations (always
1for LP method).tolTolerance used for checking net flow balance.
valueTotal L1 deviation from prior matrix
m.convergenceLogical indicator of successful solve.
messageText summary of convergence status.
Details
This function uses lpSolve::lp() to solve a linear program. The estimated matrix minimizes the sum of absolute deviations from the prior matrix m, subject to directional net flow constraints:
$$\sum_j x_{ji} - \sum_j x_{ij} = \text{net}_i$$
Structural zeros are enforced by the zero_mask. All flows are constrained to be non-negative.
See also
net_matrix_entropy() for KL divergence minimization,
net_matrix_ipf() for iterative proportional fitting (IPF),
and net_matrix_optim() for least-squares (L2) flow estimation.
Examples
m <- matrix(c(0, 100, 30, 70,
50, 0, 45, 5,
60, 35, 0, 40,
20, 25, 20, 0),
nrow = 4, byrow = TRUE,
dimnames = list(orig = LETTERS[1:4], dest = LETTERS[1:4]))
addmargins(m)
#> dest
#> orig A B C D Sum
#> A 0 100 30 70 200
#> B 50 0 45 5 100
#> C 60 35 0 40 135
#> D 20 25 20 0 65
#> Sum 130 160 95 115 500
sum_region(m)
#> # A tibble: 4 × 5
#> region out_mig in_mig turn net
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 A 200 130 330 -70
#> 2 B 100 160 260 60
#> 3 C 135 95 230 -40
#> 4 D 65 115 180 50
net <- c(30, 40, -15, -55)
result <- net_matrix_lp(net_tot = net, m = m)
result$n |>
addmargins() |>
round(2)
#> dest
#> orig A B C D Sum
#> A 0 0 0 0 0
#> B 0 0 0 0 0
#> C 0 15 0 0 15
#> D 30 25 0 0 55
#> Sum 30 40 0 0 70
sum_region(result$n)
#> # A tibble: 4 × 5
#> region out_mig in_mig turn net
#> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 A 0 30 30 30
#> 2 B 0 40 40 40
#> 3 C 15 0 15 -15
#> 4 D 55 0 55 -55