Applies a fitness_aggregator function to the values that were alive in the archive at a given generation. The function is supplied with the fitness values, and optionally other data, of all individuals that are alive at that point.

mies_aggregate_single_generation(
  archive,
  fitness_aggregator,
  generation = NA,
  include_previous_generations = FALSE
)

Arguments

archive

(Archive)
The archive over which to aggregate.

fitness_aggregator

(function)
Aggregation function, called with information about alive individuals of each generation. See details.

generation

(numeric(1))
Generation for which to aggregate the value. If include_previous_generations is FALSE, then an individual is considered to be alive at generation i if its dob is smaller or equal to i, and if its eol is either NA or greater than i. If include_previous_generations is TRUE, then all individuals with dob smaller or equal to i are considered. If this is NA, the currently alive (include_previous_generations FALSE) or all (include_previous_generations TRUE) individuals are aggregated. If multiple individuals considered "alive" with the same x_id are found, then only the last individual is used. This excludes previous individuals that were re-evaluated with a different fidelity.

include_previous_generations

(logical(1))
Aggregate all individuals that were alive at generation or at any point before that. Duplicates with the same x_id are removed, meaning that if an individual was re-evaluated with different fidelity, only the last re-evaluation is counted. However, note that individuals from different generations may still have been evaluated with different fidelity, so if Default FALSE.

Value

The value returned by fitness_aggregator when applied to individuals alive at generation generation. If no individuals of the requested generation are present, fitness_aggregator is not called and mies_aggregate_single_generation() returns NULL instead.

Details

The fitness_aggregator function may have any of the following arguments, which will be given the following information when fitness_aggregator is called:

  • fitnesses :: matrix
    Will contain fitnesses for each alive individual. This value has one column when doing single-crit optimization and one column for each "criterion" when doing multi-crit optimization. Fitnesses are always being maximized, so if an objective is being minimized, the fitness_aggregator function is given the objective values * -1.

  • objectives_unscaled :: matrix
    The objective values as given to fitnesses, but not multiplied by -1 if they are being minimized. It is recommended that the codomain argument is queried for "maximize" or "minimize" tags when objectives_unscaled is used.

  • budget :: scalar
    If multi-fidelity evaluation is being performed, then this is the "budget" value of each individual. Otherwise it is a vector containing the value 1 for each individual.

  • xdt :: data.table
    The configurations that were evaluated for the alive individuals. Rows are in the same order as the values given to fitnesses or objectives_unscaled.

  • search_space :: ParamSet
    The search space of the Archive under evaluation.

  • codomain :: ParamSet
    The codomain of the Archive under evaluation. This is particularly useful when using objectives_unscaled to determine minimization or maximization.

Not all of these arguments need to present, but at least one of fitnesses, objectives_unscaled, or xdt must be.

fitness_aggregator will never be called for an empty generation.

See also

Examples

library("bbotk")
lgr::threshold("warn")

objective <- ObjectiveRFun$new(
  fun = function(xs) {
    list(y1 = xs$x1, y2 = xs$x2)
  },
  domain = ps(x1 = p_dbl(0, 1), x2 = p_dbl(-1, 0)),
  codomain = ps(y1 = p_dbl(0, 1, tags = "maximize"),
    y2 = p_dbl(-1, 0, tags = "minimize"))
)

oi <- OptimInstanceMultiCrit$new(objective, terminator = trm("none"))

try(mies_aggregate_single_generation(oi$archive, identity), silent = TRUE)

mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses)
#> NULL


mies_init_population(oi, 2, budget_id = "x1", fidelity = .5)

oi$archive$data
#>            x2  x1 dob eol x_id  y1         y2  x_domain           timestamp
#> 1: -0.3379949 0.5   1  NA    1 0.5 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 2: -0.5931698 0.5   1  NA    2 0.5 -0.5931698 <list[2]> 2023-09-20 04:41:18
#>    batch_nr
#> 1:        1
#> 2:        1

mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses)
#>       y1        y2
#> [1,] 0.5 0.3379949
#> [2,] 0.5 0.5931698

# Notice how fitnesses are positive, since x2 is scaled with -1.
# To get the original objective-values, use objectives_unscaled:
mies_aggregate_single_generation(oi$archive,
  function(objectives_unscaled) objectives_unscaled)
#>       y1         y2
#> [1,] 0.5 -0.3379949
#> [2,] 0.5 -0.5931698

# When `...` is used, all information is passed:
mies_aggregate_single_generation(oi$archive, function(...) names(list(...)))
#> [1] "fitnesses"           "objectives_unscaled" "xdt"                
#> [4] "search_space"        "codomain"            "budget"             

# Generation 10 is not present, but individuals with eol `NA` are still
# considered alive:
mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses,
  generation = 10)
#>       y1        y2
#> [1,] 0.5 0.3379949
#> [2,] 0.5 0.5931698

# Re-evaluating points with higher "fidelity" (x1)
mies_step_fidelity(oi, budget_id = "x1", fidelity = 0.7)

oi$archive$data
#>            x2  x1 dob eol x_id  y1         y2  x_domain           timestamp
#> 1: -0.3379949 0.5   1   1    1 0.5 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 2: -0.5931698 0.5   1   1    2 0.5 -0.5931698 <list[2]> 2023-09-20 04:41:18
#> 3: -0.3379949 0.7   1  NA    1 0.7 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 4: -0.5931698 0.7   1  NA    2 0.7 -0.5931698 <list[2]> 2023-09-20 04:41:18
#>    batch_nr
#> 1:        1
#> 2:        1
#> 3:        2
#> 4:        2
# Lower-fidelity values are considered dead now, even for generation 1:
mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses,
  generation = 1)
#>       y1        y2
#> [1,] 0.7 0.3379949
#> [2,] 0.7 0.5931698

# This adds two new alive individuals at generation 2.
# Also the individuals from gen 1 are reevaluated with fidelity 0.8
mies_evaluate_offspring(oi, offspring = data.frame(x2 = c(-0.1, -0.2)),
  budget_id = "x1", fidelity = 0.9, reevaluate_fidelity = 0.8)

oi$archive$data
#>            x2  x1 dob eol x_id  y1         y2  x_domain           timestamp
#> 1: -0.3379949 0.5   1   1    1 0.5 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 2: -0.5931698 0.5   1   1    2 0.5 -0.5931698 <list[2]> 2023-09-20 04:41:18
#> 3: -0.3379949 0.7   1   2    1 0.7 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 4: -0.5931698 0.7   1   2    2 0.7 -0.5931698 <list[2]> 2023-09-20 04:41:18
#> 5: -0.1000000 0.9   2  NA    3 0.9 -0.1000000 <list[2]> 2023-09-20 04:41:18
#> 6: -0.2000000 0.9   2  NA    4 0.9 -0.2000000 <list[2]> 2023-09-20 04:41:18
#> 7: -0.3379949 0.8   2  NA    1 0.8 -0.3379949 <list[2]> 2023-09-20 04:41:18
#> 8: -0.5931698 0.8   2  NA    2 0.8 -0.5931698 <list[2]> 2023-09-20 04:41:18
#>    batch_nr
#> 1:        1
#> 2:        1
#> 3:        2
#> 4:        2
#> 5:        3
#> 6:        3
#> 7:        3
#> 8:        3

mies_aggregate_single_generation(oi$archive, function(budget, ...) budget)
#> [1] 1 1 1 1

mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses,
  generation = 1)
#>       y1        y2
#> [1,] 0.7 0.3379949
#> [2,] 0.7 0.5931698

mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses,
  generation = 2)
#>       y1        y2
#> [1,] 0.9 0.1000000
#> [2,] 0.9 0.2000000
#> [3,] 0.8 0.3379949
#> [4,] 0.8 0.5931698

# No individuals were killed, but some were fidelity-reevaluated.
# These are not present with include_previous_generations:
mies_aggregate_single_generation(oi$archive, function(fitnesses) fitnesses,
  generation = 2, include_previous_generations = TRUE)
#>       y1        y2
#> [1,] 0.9 0.1000000
#> [2,] 0.9 0.2000000
#> [3,] 0.8 0.3379949
#> [4,] 0.8 0.5931698

# Typical use-case: get dominated hypervolume
mies_aggregate_single_generation(oi$archive, function(fitnesses) domhv(fitnesses))
#> [1] 0.4945359

# Get generation-wise mean fitness values
mies_aggregate_single_generation(oi$archive, function(fitnesses) {
  apply(fitnesses, 2, mean)
})
#>        y1        y2 
#> 0.8500000 0.3077912