# Augmenting designs with controlled efficiency loss

    library(optedr)

## Motivation

In practice an experiment is rarely designed from scratch. A researcher may already have data collected at certain conditions and want to add new observations to improve estimation — without discarding what has already been measured. The key question is: where can new points be placed so that the efficiency of the augmented design stays above an acceptable threshold?

optedr answers this question with two functions used in sequence:

1. `get_augment_region()` — computes the candidate region: the set of design points whose addition keeps the D-efficiency of the augmented design above a user-specified threshold `delta_val`.
2. `augment_design()` — adds a chosen point to the initial design and rescales the weights.

Both functions support the same optimality criteria as `opt_des()` and work for any number of factors.

## Key parameters

Parameter | Role
--- | ---
`init_design` | Current design (data frame with `Point`/`Weight` in 1D, or factor columns + `Weight` in multi-D)
`alpha` | Fraction of total weight assigned to the new point after augmentation
`delta_val` | Minimum acceptable D-efficiency of the augmented design
`calc_optimal_design` | If `TRUE`, also computes the optimal design and uses it as the reference for efficiency
`new_points` | Data frame of points to add (non-interactive mode); omit for interactive mode
`par_int` | Indices of parameters of interest (Ds-Optimality only)
`n_lhs` | Number of Latin-Hypercube candidates for the region search (multi-D)

## One-factor augmentation

### Step 1: compute the candidate region

We start with a uniform three-point design for Antoine’s equation and look for points that keep the D-efficiency of the augmented design above 85 %.

    init_des <- data.frame(
      Point  = c(30, 60, 90),
      Weight = c(1/3, 1/3, 1/3)
    )

    region <- get_augment_region(
      criterion           = "D-Optimality",
      init_design         = init_des,
      alpha               = 0.25,
      model               = y ~ 10^(a - b / (c + x)),
      parameters          = c("a", "b", "c"),
      par_values          = c(8.07131, 1730.63, 233.426),
      design_space        = c(1, 100),
      calc_optimal_design = FALSE,
      delta_val           = 0.85
    )

    print(region)
    #> Augment candidate region  (delta = 0.8500)
    #>   Intervals: [5.361, 100]

`region$region` is a data frame of candidate intervals. Each row gives a lower and upper bound on the design space where the new point can be placed.

### Step 2: choose a point and augment

    new_pt <- mean(region$region[1:2])

    augmented <- augment_design(
      criterion           = "D-Optimality",
      init_design         = init_des,
      alpha               = 0.25,
      model               = y ~ 10^(a - b / (c + x)),
      parameters          = c("a", "b", "c"),
      par_values          = c(8.07131, 1730.63, 233.426),
      design_space        = c(1, 100),
      calc_optimal_design = FALSE,
      delta_val           = 0.85,
      new_points          = data.frame(Point = new_pt, Weight = 1)
    )

    print(augmented)
    #>      Point Weight
    #> 1 30.00000   0.25
    #> 2 60.00000   0.25
    #> 3 90.00000   0.25
    #> 4 52.68026   0.25
    cat("Sum of weights:", sum(augmented$Weight), "\n")
    #> Sum of weights: 1

### Comparing efficiency before and after

    result_opt <- opt_des(
      "D-Optimality",
      y ~ 10^(a - b / (c + x)), c("a", "b", "c"),
      c(8.07131, 1730.63, 233.426), c(1, 100)
    )
    #>
    #> ℹ Stop condition not reached, max iterations performed
    #> ⠙ Calculating optimal design 22 done (16/s) | 1.4sℹ The lower bound for efficiency is 99.9986187558838%
    #>

    eff_before <- design_efficiency(init_des, result_opt)
    #> ℹ The efficiency of the design is 38.5312233962882%
    eff_after  <- design_efficiency(augmented, result_opt)
    #> ℹ The efficiency of the design is 34.2933573610529%

    cat("Efficiency before augmenting:", round(eff_before * 100, 2), "%\n")
    #> Efficiency before augmenting: 38.53 %
    cat("Efficiency after augmenting: ", round(eff_after  * 100, 2), "%\n")
    #> Efficiency after augmenting:  34.29 %
    cat("Gain:                        ", round((eff_after - eff_before) * 100, 2),
        "percentage points\n")
    #> Gain:                         -4.24 percentage points

### Using the optimal design as reference (`calc_optimal_design = TRUE`)

When `calc_optimal_design = TRUE`, the function internally computes the optimal design and uses it to define the efficiency threshold. This is the recommended mode when no optimal design has been computed yet:

    region_opt <- get_augment_region(
      criterion           = "D-Optimality",
      init_design         = init_des,
      alpha               = 0.25,
      model               = y ~ 10^(a - b / (c + x)),
      parameters          = c("a", "b", "c"),
      par_values          = c(8.07131, 1730.63, 233.426),
      design_space        = c(1, 100),
      calc_optimal_design = TRUE,
      delta_val           = 0.85
    )

## Two-factor augmentation

In multi-dimensional spaces `get_augment_region()` samples candidate points with a Latin Hypercube (controlled by `n_lhs`) and returns a data frame of candidates together with their estimated efficiency gain. A heatmap of the efficiency function is displayed automatically.

### Initial design and candidate region

    init_2d <- data.frame(
      x1     = c(0.8, 10, 5),
      x2     = c(10, 0.8, 5),
      Weight = c(1/3, 1/3, 1/3)
    )

    result_2D <- 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 20 done (11/s) | 1.8sℹ The lower bound for efficiency is 99.9990066738592%
    #>

    region_2d <- get_augment_region(
      criterion           = "D-Optimality",
      init_design         = init_2d,
      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
    )

    #> ℹ 1920 candidate points with efficiency >= 0.85 (from LHS sample of 2000)

`region_2d$region` is a data frame of sampled candidates, each with an `efficiency` column. Pick the candidate that maximises efficiency:

    best_2d <- region_2d$region[which.max(region_2d$region$efficiency), ]

    eff_antes <- suppressMessages(design_efficiency(init_2d, result_2D))

    aug_2d <- augment_design(
      criterion           = "D-Optimality",
      init_design         = init_2d,
      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_2d$x1, x2 = best_2d$x2, Weight = 1)
    )

    #> ℹ 1910 candidate points with efficiency >= 0.85 (from LHS sample of 2000)
    #> Sample of candidate points:
    #>          x1       x2 efficiency
    #> 1  8.794390 0.212954  0.8582158
    #> 2  6.838483 9.551944  1.1518121
    #> 3  9.459755 1.825559  0.9033251
    #> 4  9.972765 6.345771  1.1414484
    #> 5  5.583301 8.467391  1.0770282
    #> 6  0.523416 6.496930  0.9385391
    #> 7  3.039475 0.515030  0.9331933
    #> 8  4.600099 8.381351  1.0267086
    #> 9  9.709010 5.238821  1.0868141
    #> 10 7.185740 7.984717  1.1262232
    #> 11 1.269713 5.595604  0.9042071
    #> 12 9.849443 2.206128  0.9145444
    #> 13 2.698294 7.970581  0.9129588
    #> 14 1.789031 3.713830  0.8694875
    #> 15 7.478578 8.792885  1.1563916

    eff_despues <- suppressMessages(design_efficiency(aug_2d, result_2D))

    cat("Efficiency before:", round(eff_antes  * 100, 2), "%\n")
    #> Efficiency before: 68.4 %
    cat("Efficiency after: ", round(eff_despues * 100, 2), "%\n")
    #> Efficiency after:  85.12 %
    print(aug_2d)
    #>          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.834484  9.991616   0.25

## Three-factor augmentation

For three or more factors the candidate region is displayed as a scatter-matrix coloured by candidate/non-candidate status, with the current design shown as triangles.

    init_3d <- data.frame(
      x1     = c(0.8, 10,  10,  0.8, 10),
      x2     = c(10,  0.8, 10,  10,  0.8),
      x3     = c(10,  10,  0.8, 0.8, 10),
      Weight = rep(0.2, 5)
    )

    region_3d <- get_augment_region(
      criterion           = "D-Optimality",
      init_design         = init_3d,
      alpha               = 0.45,
      model               = y ~ Vmax * x1 * x2 * x3 / ((K1+x1) * (K2+x2) * (K3+x3)),
      parameters          = c("Vmax", "K1", "K2", "K3"),
      par_values          = c(1, 1, 1, 1),
      design_space        = list(x1 = c(0.1, 10), x2 = c(0.1, 10), x3 = c(0.1, 10)),
      calc_optimal_design = FALSE,
      delta_val           = 0.93
    )

    #> ℹ 981 candidate points with efficiency >= 0.93 (from LHS sample of 2000)
    cat("Number of candidate points:", nrow(region_3d$region), "\n")
    #> Number of candidate points: 981
    plot(region_3d$plot)

## Augmenting with Ds-Optimality

When the goal is to augment while preserving estimation quality for a subset of parameters, use `criterion = "Ds-Optimality"` and pass `par_int`:

    region_ds <- get_augment_region(
      criterion           = "Ds-Optimality",
      init_design         = init_2d,
      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,
      par_int             = c(1),
      delta_val           = 0.85,
      n_lhs               = 5000
    )

    #> ℹ 3453 candidate points with efficiency >= 0.85 (from LHS sample of 5000)

    best_ds <- region_ds$region[which.max(region_ds$region$efficiency), ]

    aug_ds <- augment_design(
      criterion           = "Ds-Optimality",
      init_design         = init_2d,
      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,
      par_int             = c(1),
      delta_val           = 0.85,
      new_points          = data.frame(x1 = best_ds$x1, x2 = best_ds$x2, Weight = 1),
      n_lhs               = 5000
    )

    #> ℹ 3469 candidate points with efficiency >= 0.85 (from LHS sample of 5000)
    #> Sample of candidate points:
    #>           x1        x2 efficiency
    #> 1  7.6032146 9.3698539  2.4871401
    #> 2  6.3288098 2.7861100  0.8715261
    #> 3  0.2309604 3.7634435  0.9144785
    #> 4  9.0195639 9.0019514  2.7257777
    #> 5  5.7033820 7.2771142  1.6913988
    #> 6  6.6428368 4.2905667  1.2472598
    #> 7  3.0184121 6.7119601  0.9384701
    #> 8  5.1552525 8.1395227  1.6583790
    #> 9  6.4106194 4.5610415  1.2870381
    #> 10 5.5796609 0.7116589  0.8822207
    #> 11 4.2507370 6.9246339  1.2670307
    #> 12 4.6568340 5.0945095  1.1257668
    #> 13 5.2756721 4.4240340  1.1090500
    #> 14 6.9540511 6.5696378  1.8360266
    #> 15 5.2299034 3.9739387  1.0192873
    print(aug_ds)
    #>          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.889364  9.929961   0.25

## Interactive mode

Omitting `new_points` (and `delta_val`) from both functions triggers an interactive session where the package plots the candidate region and asks the user to type a point. This mode is documented in `?augment_design`.