Skip to contents

Solves for an origin–destination flow matrix that satisfies directional net migration constraints while minimizing squared deviation from a prior matrix.

Usage

net_matrix_optim(net_tot, m, zero_mask = NULL, maxit = 500, tol = 1e-06)

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, where TRUE indicates forbidden (structurally zero) flows. Defaults to disallowing diagonal flows.

maxit

Maximum number of iterations to perform. Default is 500.

tol

Numeric tolerance for checking whether sum(net_tot) == 0. Default is 1e-6.

Value

A named list with components:

n

Estimated matrix of flows satisfying the net constraints.

it

Number of optimization iterations (if available).

tol

Tolerance used for the net flow balance check.

value

Objective function value (sum of squared deviations).

convergence

Logical indicating successful convergence.

message

Solver message or status.

Details

The function minimizes: $$\sum_{i,j} (y_{ij} - m_{ij})^2$$ subject to directional net flow constraints: $$\sum_j y_{ji} - \sum_j y_{ij} = \text{net}_i$$ and non-negativity constraints on all flows. Structural zeros are enforced using zero_mask. Internally uses optim() or a constrained quadratic programming solver.

See also

net_matrix_entropy() for KL divergence minimization, net_matrix_ipf() for iterative proportional fitting, and net_matrix_lp() for linear programming with L1 loss.

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_optim(net_tot = net, m = m)
result$n |>
  addmargins() |>
  round(2)
#>      dest
#> orig       A      B      C     D    Sum
#>   A     0.00  90.97  27.22 62.55 180.74
#>   B    73.37   0.00  53.30  0.56 127.23
#>   C    75.07  32.34   0.00 35.32 142.74
#>   D    62.29  43.92  47.22  0.00 153.43
#>   Sum 210.74 167.23 127.74 98.43 604.13
sum_region(result$n)
#> # A tibble: 4 × 5
#>   region out_mig in_mig  turn   net
#>   <chr>    <dbl>  <dbl> <dbl> <dbl>
#> 1 A         181.  211.   391.  30.0
#> 2 B         127.  167.   294.  40.0
#> 3 C         143.  128.   270. -15.0
#> 4 D         153.   98.4  252. -55.0