optedr is an optimal experimental design suite for non-linear and multi-factor models. Its main capabilities are:
You can install the released version of optedr from CRAN with:
install.packages("optedr")
You can install the latest version from GitHub with:
devtools::install_github("kezrael/optedr")
opt_des() — calculates optimal designs.design_efficiency() — evaluates the efficiency of a design against
the optimum.get_augment_region() — computes the candidate region for
augmentation with controlled efficiency loss.augment_design() — adds points to a design and rescales weights.efficient_round() — rounds approximate designs to exact designs
(multiplier method).combinatorial_round() — rounds approximate designs to exact designs
(exhaustive floor/ceiling search).make_kl_fun() — constructs a KL divergence function from two
model-family pairs.shiny_optimal() / shiny_augment() — interactive Shiny demos.The optdes object returned by opt_des() has its own print(),
summary() and plot() methods.
library(optedr)
#> ℹ Loading optedr
opt_des() requires a criterion, a model, parameters and their
nominal values, and a design_space. For multi-factor models pass
design_space as a named list — variable names must match those in
model.
library(optedr)
result_D <- opt_des(
criterion = "D-Optimality",
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10))
)
#>
#> ℹ Stop condition reached: difference between sensitivity and criterion < 1e-05
#> ⠙ Calculating optimal design 22 done (11/s) | 2sℹ The lower bound for efficiency is 99.9990099490678%
#>
result_D
#> Optimal design for D-Optimality (2 factors):
#> x1 x2 Weight
#> 1 0.8332604 10.0000000 0.3333301
#> 2 10.0000000 0.8332352 0.3333300
#> 3 10.0000000 10.0000000 0.3333399
plot() returns a heatmap of the sensitivity function, with support
points overlaid in red. By the Equivalence Theorem the design is optimal
if and only if the sensitivity equals the number of parameters at every
support point and is below it everywhere else.
plot(result_D)

summary() gives additional detail: criterion value, Atwood convergence
criterion, and the design space.
The same interface supports five optimality criteria:
| Criterion | Key argument | Objective |
|-------------------|--------------|-----------------------------------------|
| "D-Optimality" | — | Minimise $-\log\det M$ |
| "Ds-Optimality" | par_int | Focus on a subset of parameters |
| "A-Optimality" | — | Minimise $\operatorname{tr} M^{-1}$ |
| "I-Optimality" | reg_int | Minimise integrated prediction variance |
| "L-Optimality" | matB | Minimise $\operatorname{tr}(B\,M^{-1})$ |
For example, A-Optimality minimises the average variance of all estimators:
result_A <- opt_des(
criterion = "A-Optimality",
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10))
)
#>
#> ℹ Stop condition not reached, max iterations performed
#> ⠙ Calculating optimal design 22 done (14/s) | 1.6sℹ The lower bound for efficiency is 99.9999976902864%
#>
result_A
#> Optimal design for A-Optimality (2 factors):
#> x1 x2 Weight
#> 1 0.6569581 10.0000000 0.3725354
#> 2 10.0000000 0.6569844 0.3725333
#> 3 10.0000000 10.0000000 0.2549313
Combine any set of criteria as a weighted sum. Weights are normalised automatically.
result_DI <- opt_des(
criterion = "Compound",
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
compound = list(
list(criterion = "D-Optimality", weight = 0.6),
list(criterion = "I-Optimality", weight = 0.4,
reg_int = list(x1 = c(1, 5), x2 = c(1, 5)))
)
)
#> ⠙ Calculating optimal design 7 done (3.3/s) | 2.1s⠹ Calculating optimal design 8 done (3.2/s) | 2.5s⠸ Calculating optimal design 9 done (3.1/s) | 2.9s⠼ Calculating optimal design 10 done (3/s) | 3.3s ⠴ Calculating optimal design 11 done (3.1/s) | 3.5s⠦ Calculating optimal design 13 done (3.4/s) | 3.8s⠧ Calculating optimal design 14 done (3.4/s) | 4.1s⠇ Calculating optimal design 16 done (3.6/s) | 4.4s⠏ Calculating optimal design 17 done (3.7/s) | 4.6s⠋ Calculating optimal design 18 done (3.8/s) | 4.8s⠙ Calculating optimal design 20 done (3.9/s) | 5.1s⠹ Calculating optimal design 21 done (4/s) | 5.3s
#> ℹ Stop condition not reached, max iterations performed
#> ⠸ Calculating optimal design 22 done (4/s) | 5.5sℹ The lower bound for efficiency is 98.0491819762597%
#>
result_DI
#> Optimal design for Compound (0.60*D + 0.40*I):
#> x1 x2 Weight
#> 1 0.9122428 8.703202 0.3548138
#> 2 1.6738998 1.439094 0.1073170
#> 3 10.0000000 1.012563 0.3389087
#> 4 10.0000000 10.000000 0.1989605
design_efficiency() measures the fraction of the optimal information
that a given design achieves. An efficiency of 0.80 means the design
needs $1/0.80 = 1.25\times$ more observations to match the optimal.
corners <- data.frame(
x1 = c(0.1, 10, 0.1, 10),
x2 = c(0.1, 0.1, 10, 10),
Weight = rep(0.25, 4)
)
eff <- design_efficiency(corners, result_D)
#> ℹ The efficiency of the design is 19.3350596170462%
get_augment_region() finds which points can be added to an existing
design while keeping its D-efficiency above a threshold delta_val.
augment_design() then adds the chosen point and rescales weights. Both
functions work in 1D and multi-dimensional spaces.
init_des <- data.frame(
x1 = c(0.8, 10, 5),
x2 = c(10, 0.8, 5),
Weight = c(1/3, 1/3, 1/3)
)
region <- get_augment_region(
criterion = "D-Optimality",
init_design = init_des,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
delta_val = 0.85
)

region$region contains the candidate points ranked by efficiency gain.
Pick the best and augment:
best <- region$region[which.max(region$region$efficiency), ]
augmented <- augment_design(
criterion = "D-Optimality",
init_design = init_des,
alpha = 0.25,
model = y ~ Vmax * x1 * x2 / ((K1 + x1) * (K2 + x2)),
parameters = c("Vmax", "K1", "K2"),
par_values = c(1, 1, 1),
design_space = list(x1 = c(0.1, 10), x2 = c(0.1, 10)),
calc_optimal_design = FALSE,
delta_val = 0.85,
new_points = data.frame(x1 = best$x1, x2 = best$x2, Weight = 1)
)

#> ℹ 1903 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
#> Sample of candidate points:
#> x1 x2 efficiency
#> 1 6.7385595 0.2924680 0.8909334
#> 2 1.9821626 1.1154958 0.9394470
#> 3 4.0971554 8.4766962 1.0007669
#> 4 6.0239305 8.7571051 1.1028042
#> 5 4.6024281 0.4528793 0.9276096
#> 6 9.4770793 4.5600493 1.0465406
#> 7 0.9444112 2.8258679 0.9368173
#> 8 8.0232267 1.8904368 0.8922271
#> 9 3.1618572 4.4661287 0.8625847
#> 10 5.0458372 9.1524421 1.0662419
#> 11 7.0391033 7.7941107 1.1160897
#> 12 0.4494250 7.4074376 0.9311998
#> 13 1.5010313 3.8698458 0.8872015
#> 14 8.1851605 4.7172844 1.0284918
#> 15 3.2994805 9.4786392 0.9698640
augmented
#> x1 x2 Weight
#> 1 0.800000 10.000000 0.25
#> 2 10.000000 0.800000 0.25
#> 3 5.000000 5.000000 0.25
#> 4 9.912547 9.954952 0.25
Approximate designs assign continuous weights. Two functions convert them to integer replication counts for a given total $n$:
# Multiplier method
efficient_round(result_D$optdes, n = 12)
#> x1 x2 Weight
#> 1 0.8332604 10.0000000 4
#> 2 10.0000000 0.8332352 4
#> 3 10.0000000 10.0000000 4
# Exhaustive floor/ceiling search (optimal for small number of support points)
combinatorial_round(result_D, n = 12)
#> x1 x2 Weight
#> 1 0.8332604 10.0000000 4
#> 2 10.0000000 0.8332352 4
#> 3 10.0000000 10.0000000 4
KL-Optimality finds the design that maximises the Kullback-Leibler divergence between a reference model and the most adversarial rival, for Normal, Poisson, Binomial and Gamma families.
result_kl <- opt_des(
"KL-Optimality",
model = y ~ Vmax * x / (Km + x),
parameters = c("Vmax", "Km"),
par_values = c(2, 1),
design_space = c(0.1, 5),
rival_model = y ~ a * x,
rival_params = c("a"),
rival_pars = c(1),
rival_lower = c(0.2),
rival_upper = c(2.5),
family = "Normal",
phi = 1
)
#>
#> ℹ Stop condition reached: difference between sensitivity and criterion < 1e-05
#> ⠙ Calculating optimal design 7 done (8.1/s) | 863msℹ The lower bound for efficiency is 99.9957589718079%
#>
result_kl
#> Optimal design for KL-Optimality:
#> Point Weight
#> 1 1.127578 0.8159831
#> 2 5.000000 0.1840169
plot(result_kl)

make_kl_fun() automates the KL function construction for custom
model-family pairs, including different error structures or families.
vignette("optedr-intro") — optimality criteria, multi-dimensional
design spaces, compound designs, efficiency and rounding.vignette("optedr-augment") — augmenting designs in 1D, 2D and 3D
with controlled efficiency loss.vignette("optedr-kl") — KL-Optimality for model discrimination,
make_kl_fun(), and the Hill model case study.Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.