
Add locked action decisions to a planning problem
Source:R/add_constraint_locked_actions.R
add_constraint_locked_actions.RdFix feasible planning unit–action decisions to be selected or excluded.
This function modifies the status of existing feasible
(pu, action) pairs stored in the feasible action table. It does not
create new feasible action pairs and therefore must be used only after
add_actions has been called.
Arguments
- x
A
Problemobject with action feasibility already defined viaadd_actions.- locked_in
Optional specification of feasible
(pu, action)pairs that must be selected. It may beNULL, adata.frame, or a named list.- locked_out
Optional specification of feasible
(pu, action)pairs that must not be selected. It may beNULL, adata.frame, or a named list.
Value
An updated Problem object in which the status column of the
feasible action table has been modified to reflect locked-in and locked-out
decisions.
Details
Use this function when only specific feasible (pu, action) decisions
must be forced in or out of the solution, rather than whole planning units.
Let \(\mathcal{I}\) denote the set of planning units and \(\mathcal{A}\) the set of actions. Let \(\mathcal{D} \subseteq \mathcal{I} \times \mathcal{A}\) denote the set of feasible planning unit–action pairs already defined in the problem.
This function allows the user to define two subsets:
\(\mathcal{D}^{in} \subseteq \mathcal{D}\), the set of feasible pairs that must be selected,
\(\mathcal{D}^{out} \subseteq \mathcal{D}\), the set of feasible pairs that must not be selected.
These sets are encoded by updating the status column of the feasible
action table. The function validates that all requested locked-in and
locked-out pairs are already feasible. Therefore, it cannot be used to
introduce new planning unit–action combinations into the problem.
In optimization terms, if \(x_{ia}\) denotes the decision variable associated with planning unit \(i\) and action \(a\), then:
locked-in pairs conceptually impose \(x_{ia} = 1\),
locked-out pairs conceptually impose \(x_{ia} = 0\).
The exact translation into solver-side constraints occurs later when the model is built.
In contrast, add_constraint_locked_pu fixes whole planning
units through the unit-selection variables, whereas this function fixes only
specific feasible (pu, action) decisions.
Accepted formats
Both locked_in and locked_out accept the same formats:
NULL,a
data.framewith columnspuandaction, optionally including afeasiblecolumn used as a filter,a named list whose names are action ids and whose elements are either vectors of planning unit ids or
sfobjects.
If a feasible column is supplied in a data.frame, only rows
with feasible = TRUE are used. Missing values in feasible are
treated as FALSE.
If an sf specification is supplied, the problem object must contain
planning-unit geometry, and planning units are matched spatially using
sf::st_intersects().
Conflict checking
A given (pu, action) pair cannot be simultaneously requested in both
locked_in and locked_out. Such overlaps are rejected.
In addition, if a planning unit is already marked as locked out at the
planning-unit level, then all feasible actions in that planning unit are
forced to status = 3. Any attempt to lock in an action within such a
planning unit raises an error.
Order of precedence
User-supplied locked-in and locked-out action requests are first applied to
the feasible action table. Afterwards, any planning-unit-level
locked_out flag is enforced, overriding action-level status and
ensuring consistency with planning-unit exclusions.
Examples
pu <- data.frame(
id = 1:4,
cost = c(2, 3, 1, 4)
)
features <- data.frame(
id = 1:2,
name = c("sp1", "sp2")
)
dist_features <- data.frame(
pu = c(1, 1, 2, 3, 4, 4),
feature = c(1, 2, 1, 2, 1, 2),
amount = c(1, 2, 1, 3, 2, 1)
)
p <- create_problem(
pu = pu,
features = features,
dist_features = dist_features
)
p <- add_actions(
x = p,
actions = data.frame(id = c("conservation", "restoration")),
cost = c(conservation = 3, restoration = 8)
)
# Lock a few feasible decisions
p <- add_constraint_locked_actions(
x = p,
locked_in = data.frame(
pu = c(1, 2),
action = c("conservation", "restoration")
),
locked_out = data.frame(
pu = c(4),
action = c("conservation")
)
)
p$data$dist_actions
#> pu action cost status internal_pu internal_action
#> 1 1 conservation 3 2 1 1
#> 5 1 restoration 8 0 1 2
#> 2 2 conservation 3 0 2 1
#> 6 2 restoration 8 2 2 2
#> 3 3 conservation 3 0 3 1
#> 7 3 restoration 8 0 3 2
#> 4 4 conservation 3 3 4 1
#> 8 4 restoration 8 0 4 2
# Named-list interface
p2 <- add_constraint_locked_actions(
x = p,
locked_in = list(
conservation = c(1, 3)
),
locked_out = list(
restoration = c(2)
)
)
p2$data$dist_actions
#> pu action cost status internal_pu internal_action
#> 1 1 conservation 3 2 1 1
#> 5 1 restoration 8 0 1 2
#> 2 2 conservation 3 0 2 1
#> 6 2 restoration 8 3 2 2
#> 3 3 conservation 3 2 3 1
#> 7 3 restoration 8 0 3 2
#> 4 4 conservation 3 3 4 1
#> 8 4 restoration 8 0 4 2