library(designit)
library(ggplot2)
#> Want to understand how all the pieces fit together? Read R for Data
#> Science: https://r4ds.hadley.nz/
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(tidyr)

Example 1: An expensive way to construct a 4x4 latin square (one plate)

(latin square should give the best score)

First using a combination of two OSAT scores (for row and column).

This usually produces a latin square when using the squared L2 norm (L2s) for aggregation of the 2 scores.

# Setting up the batch container
example1 <- BatchContainer$new(
  dimensions = c(
    plate = 1,
    row = 4, col = 4
  )
)

# Add samples to container
# Need unique Sample ID. Can we drop this constraint?
example1 <- assign_in_order(example1,
  samples = tibble::tibble(
    Group = rep(c("Grp 1", "Grp 2", "Grp 3", "Grp 4"), each = 4),
    ID = 1:16
  )
)

# The following does not work (an gives a constant score of 144!)
# scoring_f <- osat_score_generator(batch_vars = c("row","col"), feature_vars = c("Group"))
# First analysis of problem indicates that osat_score generates a full row*col vector of 'ideal scores'
# which are in fact the same value, implying an identical overall result as each position can be either
# allocated by 1 sample or 0 samples, the sum of 1's being the sample count.
# --> don't use osat_score if there's a lack of samples as compared to possible positioning

# # Set scoring function
scoring_f <- list(
  Row.Score = osat_score_generator(batch_vars = c("row"), feature_vars = c("Group")),
  Column.Score = osat_score_generator(batch_vars = c("col"), feature_vars = c("Group"))
)
set.seed(41)

bc <- optimize_design(
  example1,
  scoring = scoring_f,
  max_iter = 300, # this is set to shorten vignette run-time based on known random seed, normally we don't know.
  n_shuffle = 2,
  acceptance_func = mk_simanneal_acceptance_func(mk_simanneal_temp_func(T0 = 1000, alpha = 0.5)),
  aggregate_scores_func = L2s_norm
)
#> Checking variances of 2-dim. score vector.
#> ... (11.385, 16.687) - OK
#> Initial score: c(48, 0)
#>    Aggregated: 2304
#> Achieved score: c(36, 4) at iteration 1
#>    Aggregated: 1312
#> Achieved score: c(24, 4) at iteration 2
#>    Aggregated: 592
#> Achieved score: c(24, 12) at iteration 3
#>    Aggregated: 720
#> Achieved score: c(24, 8) at iteration 4
#>    Aggregated: 640
#> Achieved score: c(16, 10) at iteration 5
#>    Aggregated: 356
#> Achieved score: c(12, 10) at iteration 6
#>    Aggregated: 244
#> Achieved score: c(10, 10) at iteration 7
#>    Aggregated: 200
#> Achieved score: c(10, 6) at iteration 9
#>    Aggregated: 136
#> Achieved score: c(10, 4) at iteration 10
#>    Aggregated: 116
#> Achieved score: c(8, 10) at iteration 11
#>    Aggregated: 164
#> Achieved score: c(8, 8) at iteration 12
#>    Aggregated: 128
#> Achieved score: c(8, 8) at iteration 14
#>    Aggregated: 128
#> Achieved score: c(4, 10) at iteration 19
#>    Aggregated: 116
#> Achieved score: c(4, 6) at iteration 20
#>    Aggregated: 52
#> Achieved score: c(4, 4) at iteration 22
#>    Aggregated: 32
#> Achieved score: c(4, 4) at iteration 27
#>    Aggregated: 32
#> Achieved score: c(4, 4) at iteration 36
#>    Aggregated: 32
#> Achieved score: c(4, 4) at iteration 38
#>    Aggregated: 32
#> Achieved score: c(0, 4) at iteration 39
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 50
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 54
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 82
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 87
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 119
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 125
#>    Aggregated: 16
#> Achieved score: c(0, 4) at iteration 200
#>    Aggregated: 16
#> Achieved score: c(4, 0) at iteration 215
#>    Aggregated: 16
#> Achieved score: c(0, 0) at iteration 284
#>    Aggregated: 0
bc$trace$elapsed
#> Time difference of 4.846822 secs

plot_plate(bc,
  plate = plate, row = row, column = col, .color = Group,
  title = "Ex1: Using OSAT scores for plate design\n(not the recommended way!)"
)

Now using a dedicated scoring for the group distances on a plate.

This should reliably lead to a nice symmetry-bearing latin square design with only a one-dimensional score to look at.

scoring_f <- mk_plate_scoring_functions(bc, row = "row", column = "col", group = "Group")
set.seed(42)
bc <- optimize_design(
  example1,
  scoring = scoring_f,
  max_iter = 1000, # this is set to shorten vignette run-time based on random seed, normally we don't know.
  n_shuffle = 2,
  acceptance_func = mk_simanneal_acceptance_func(mk_simanneal_temp_func(T0 = 1000, alpha = 0.5)),
  quiet = TRUE
)
bc$trace$elapsed
#> Time difference of 4.184167 secs

plot_plate(bc,
  plate = plate, row = row, column = col, .color = Group,
  title = "Ex1: Using a dedicated plate scoring function:\nThis should show a latin square!"
)