Skip to contents

Enumerates all points of the probability simplex \(\{\lambda \in [0,1]^M : \sum_m \lambda_m = 1\}\) on a regular grid with increment step, using integer arithmetic: with \(S = 1/\code{step}\) steps, every integer composition \((c_1, \dots, c_M)\) with \(\sum_m c_m = S\) and \(c_m \ge 0\) is listed and returned as \(c / S\). This is the grid over which psave() searches for the propensity score mixing weights \(\lambda\) and the prognostic mixing weights \(\gamma\).

Usage

simplex_grid(M, step = 0.05)

Arguments

M

Integer; the number of mixture components (grid columns).

step

Numeric; the grid increment. Must evenly divide 1 (checked in integer arithmetic: with n_steps = round(1/step), the call errors unless abs(n_steps * step - 1) < 1e-8). Default 0.05, the value used in Kabata, Stuart and Shintani (2024).

Value

A numeric matrix with \(\binom{S + M - 1}{M - 1}\) rows and M columns; each row sums to 1 exactly (in integer arithmetic before the single final division by \(S\)).

Details

The number of grid points is exactly \(\binom{S + M - 1}{M - 1}\); e.g., M = 4, step = 0.05 gives choose(23, 3) = 1771 points. Because the grid is built from integer compositions, every valid point is present by construction; the reference implementation of the paper instead filtered expand.grid() rows with a floating-point rowSums(gr) == 1 test, which silently dropped about 10.6% of the valid points for M = 4, step = 0.05.

Enumeration order (the tie-breaking rule). Rows are generated by recursive descent: \(c_1\) runs from \(S\) down to 0; within each value of \(c_1\), \(c_2\) runs from the remainder down to 0; and so on. The first row is therefore \((1, 0, \dots, 0)\) and the last row is \((0, \dots, 0, 1)\). All grid searches in psave() resolve ties by taking the first row attaining the minimum, within a 1e-9 relative tolerance of the minimum, so ties favor learners listed earlier in ps.methods / prog.methods. The tolerance is deliberate: criterion values are computed with floating-point matrix algebra whose lowest-order bits can differ across BLAS implementations, so an exact bitwise which.min() would not be reproducible across machines, whereas the tolerant first-minimum rule is.

References

Kabata D, Stuart EA, Shintani A (2024). Prognostic score-based model averaging approach for propensity score estimation. BMC Medical Research Methodology, 24, 228. doi:10.1186/s12874-024-02350-y

Examples

simplex_grid(2, step = 0.25)
#>      [,1] [,2]
#> [1,] 1.00 0.00
#> [2,] 0.75 0.25
#> [3,] 0.50 0.50
#> [4,] 0.25 0.75
#> [5,] 0.00 1.00
nrow(simplex_grid(4, step = 0.05))  # choose(23, 3) = 1771
#> [1] 1771

# first row = all weight on the first component; last = on the last:
head(simplex_grid(3, step = 0.25), 3)
#>      [,1] [,2] [,3]
#> [1,] 1.00 0.00 0.00
#> [2,] 0.75 0.25 0.00
#> [3,] 0.75 0.00 0.25
tail(simplex_grid(3, step = 0.25), 3)
#>       [,1] [,2] [,3]
#> [13,]    0 0.50 0.50
#> [14,]    0 0.25 0.75
#> [15,]    0 0.00 1.00