
Set the epsilon-constraint multi-objective method
Source:R/set_method_epsilon_constraint.R
set_method_epsilon_constraint.RdConfigure a Problem object to be solved with the
epsilon-constraint multi-objective method.
In this method, one objective is designated as the primary objective and is optimized directly, while the remaining objectives are transformed into \(\varepsilon\)-constraints.
Two operating modes are supported:
manual mode: the user supplies the \(\varepsilon\)-levels explicitly,
automatic mode: the \(\varepsilon\)-levels are generated later during
solvefrom extreme-point or payoff information.
This function does not solve the problem. It stores the method configuration
in x$data$method, to be used later by solve.
Usage
set_method_epsilon_constraint(
x,
primary,
eps = NULL,
aliases = NULL,
mode = c("manual", "auto"),
n_points = 10,
include_extremes = TRUE,
lexicographic = TRUE,
lexicographic_tol = 1e-08
)Arguments
- x
A
Problemobject.- primary
Character string giving the alias of the primary objective to optimize directly.
- eps
Optional epsilon specification used only in
mode = "manual". It may be:a named numeric vector, defining epsilon values for a single run,
or a named list of numeric vectors, defining epsilon values for a grid of runs.
Names must correspond exactly to the constrained objective aliases.
- aliases
Optional character vector of objective aliases to include. By default, all registered objective aliases are used. The value of
primarymust be included inaliases.- mode
Character string. Must be either
"manual"or"auto".- n_points
Integer scalar used only in
mode = "auto". Number of epsilon points to generate automatically for the constrained objective. Must be at least 2.- include_extremes
Logical scalar used only in
mode = "auto". IfTRUE, include extreme epsilon values in the automatically generated grid.- lexicographic
Logical scalar used only in
mode = "auto". IfTRUE, compute extreme points lexicographically.- lexicographic_tol
Numeric scalar \(\ge 0\). Tolerance used in lexicographic extreme-point computation.
Value
An updated Problem object with the epsilon-constraint method
configuration stored in x$data$method.
In manual mode, x$data$method$runs contains the explicit
epsilon-design grid.
In automatic mode, x$data$method$runs is NULL until the grid is
generated later during solve.
Details
Use this method when one objective should be optimized directly while the remaining objectives are controlled through explicit performance thresholds.
General idea
Suppose that \(m \ge 2\) objective functions have already been registered in the problem: $$ f_1(x), f_2(x), \dots, f_m(x). $$
The epsilon-constraint method selects one of them as the primary objective, say \(f_p(x)\), and treats the remaining objectives as constrained objectives.
For a fixed vector of epsilon levels, the method solves subproblems in which the primary objective is optimized directly and the remaining objectives are imposed through epsilon constraints.
A representative formulation is:
$$ \max \; f_p(x) $$
subject to
$$ f_k(x) \ge \varepsilon_k, \qquad k \in \mathcal{C}, $$
together with all original feasibility constraints of the planning problem, where \(\mathcal{C}\) is the set of constrained objectives.
Depending on objective sense, the internal implementation may transform minimization objectives into equivalent constrained forms, but the method always follows the same principle:
one objective is optimized directly,
all remaining objectives are imposed through \(\varepsilon\)-constraints.
By solving the problem repeatedly for different epsilon levels, the method generates a set of efficient trade-off solutions.
Atomic objectives requirement
The epsilon-constraint method can only be used with atomic objectives that
have already been registered under aliases. These aliases are typically
created by calling objective setters with an alias argument, for
example:
x <- x |>
add_objective_max_benefit(alias = "benefit") |>
add_objective_min_cost(alias = "cost") |>
add_objective_min_fragmentation(alias = "frag")The primary argument selects which registered objective is optimized
directly. The remaining aliases are treated as constrained objectives.
Manual mode
In mode = "manual", the user must provide eps.
The eps argument can be supplied as:
a named numeric vector, defining a single run,
or a named list of numeric vectors, defining a grid of runs.
The names of eps must correspond exactly to the constrained objective
aliases, that is, to all aliases in aliases except primary.
If the constrained objectives are \(\mathcal{C} = \{c_1, \dots, c_q\}\), then manual mode creates a design grid containing all combinations of the supplied epsilon levels for the constrained objectives.
Each row of this grid defines one subproblem to be solved later.
Important: manual mode supports two or more objectives. In particular, it can be used with:
2 objectives: 1 primary + 1 constrained objective,
3 or more objectives: 1 primary + multiple constrained objectives.
Thus, manual mode is the general way to use the epsilon-constraint method when more than two objectives are involved.
In manual mode, the generated design grid is stored immediately in
x$data$method$runs. Its epsilon columns are named
eps_<alias>, for example eps_frag.
Automatic mode
In mode = "auto", the user omits eps and instead supplies
n_points.
In this case, the epsilon grid is not built immediately. Instead, it is
constructed later during solve using extreme-point or
payoff-table information.
In the current implementation, automatic mode supports exactly two objectives only:
one primary objective,
one constrained objective.
Therefore, if mode = "auto", then aliases must contain exactly
two objective aliases. Problems with three or more objectives must use
mode = "manual".
If include_extremes = TRUE, the automatically generated grid includes
the extreme values of the constrained objective. Otherwise, only interior
values are used.
If lexicographic = TRUE, the extreme points used to generate the grid
are computed lexicographically. In that case, one objective is optimized
first, and then the second objective is optimized while constraining the
first to remain within lexicographic_tol of its optimum.
Stored configuration
The configured method stores:
name = "epsilon_constraint",mode,primary,aliases,constrained,epsilon design information,
lexicographic configuration.
In manual mode, x$data$method$runs contains the explicit design grid.
In automatic mode, x$data$method$runs is initially NULL and is
generated later during solve.
For more than two objectives, automatic grid generation is currently unavailable because the number of epsilon combinations grows rapidly and requires explicit user control.
Examples
# Small toy problem
pu_tbl <- data.frame(
id = 1:4,
cost = c(1, 2, 3, 4)
)
feat_tbl <- data.frame(
id = 1:2,
name = c("feature_1", "feature_2")
)
dist_feat_tbl <- data.frame(
pu = c(1, 1, 2, 3, 4),
feature = c(1, 2, 2, 1, 2),
amount = c(5, 2, 3, 4, 1)
)
actions_df <- data.frame(
id = c("conservation", "restoration"),
name = c("conservation", "restoration")
)
effects_df <- data.frame(
pu = c(1, 2, 3, 4, 1, 2, 3, 4),
action = c("conservation", "conservation", "conservation", "conservation",
"restoration", "restoration", "restoration", "restoration"),
feature = c(1, 1, 1, 1, 2, 2, 2, 2),
benefit = c(2, 1, 0, 1, 3, 0, 1, 2),
loss = c(0, 0, 1, 0, 0, 1, 0, 0)
)
x <- create_problem(
pu = pu_tbl,
features = feat_tbl,
dist_features = dist_feat_tbl,
cost = "cost"
) |>
add_actions(actions_df, cost = c(conservation = 1, restoration = 2)) |>
add_effects(effects_df) |>
add_objective_min_cost(alias = "cost") |>
add_objective_max_benefit(alias = "benefit") |>
add_objective_min_loss(alias = "loss")
# Manual mode with one constrained objective
x1 <- set_method_epsilon_constraint(
x,
primary = "benefit",
aliases = c("benefit", "cost"),
mode = "manual",
eps = c(cost = 5)
)
x1$data$method
#> $name
#> [1] "epsilon_constraint"
#>
#> $mode
#> [1] "manual"
#>
#> $primary
#> [1] "benefit"
#>
#> $aliases
#> [1] "benefit" "cost"
#>
#> $constrained
#> [1] "cost"
#>
#> $eps
#> $eps$cost
#> [1] 5
#>
#>
#> $runs
#> eps_cost run_id
#> 1 5 1
#>
#> $lexicographic
#> [1] TRUE
#>
#> $lexicographic_tol
#> [1] 1e-08
#>
# Manual mode with multiple epsilon values
x2 <- set_method_epsilon_constraint(
x,
primary = "benefit",
aliases = c("benefit", "cost"),
mode = "manual",
eps = list(cost = c(4, 6, 8))
)
x2$data$method$runs
#> eps_cost run_id
#> 1 4 1
#> 2 6 2
#> 3 8 3
# Manual mode with more than two objectives
x3 <- set_method_epsilon_constraint(
x,
primary = "benefit",
aliases = c("benefit", "cost", "loss"),
mode = "manual",
eps = list(
cost = c(4, 6),
loss = c(0, 1)
)
)
x3$data$method$runs
#> eps_cost eps_loss run_id
#> 1 4 0 1
#> 2 6 0 2
#> 3 4 1 3
#> 4 6 1 4
# Automatic mode currently supports exactly two objectives
x4 <- set_method_epsilon_constraint(
x,
primary = "benefit",
aliases = c("benefit", "cost"),
mode = "auto",
n_points = 5,
include_extremes = TRUE,
lexicographic = TRUE,
lexicographic_tol = 1e-8
)
x4$data$method
#> $name
#> [1] "epsilon_constraint"
#>
#> $mode
#> [1] "auto"
#>
#> $primary
#> [1] "benefit"
#>
#> $aliases
#> [1] "benefit" "cost"
#>
#> $constrained
#> [1] "cost"
#>
#> $n_points
#> [1] 5
#>
#> $include_extremes
#> [1] TRUE
#>
#> $lexicographic
#> [1] TRUE
#>
#> $lexicographic_tol
#> [1] 1e-08
#>
#> $runs
#> NULL
#>