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\).
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 unlessabs(n_steps * step - 1) < 1e-8). Default0.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