Solve a planning problem stored in a Problem object.
This is the main execution step of the multiscape workflow. It reads
the problem specification stored in a Problem object, builds the
corresponding optimization model when needed, applies the configured solver
settings, and returns a solutionset-class object.
A SolutionSet is the standard result object returned by
solve(). Single-objective workflows are represented as one-run
SolutionSet objects, while multi-objective workflows are represented
as multi-run SolutionSet objects.
Arguments
- x
A
Problemobject created withcreate_problemand optionally enriched with actions, effects, targets, constraints, objectives, spatial relations, method settings, and solver settings.- ...
Additional arguments reserved for internal or legacy solver handling. These are not part of the main recommended user interface.
Value
A solutionset-class object.
The returned object contains run-level information, solver diagnostics,
objective values, and stored optimization outputs. For single-objective
problems, the returned SolutionSet contains one run. For
multi-objective workflows, it contains one or more runs generated by the
selected method.
Users will typically inspect or visualize results using accessor and plotting
functions such as get_pu, get_actions,
get_features, get_targets, and
plot_tradeoff.
Details
Role of solve()
The typical multiscape workflow is:
x <- create_problem(...)
x <- add_...(x, ...)
x <- set_...(x, ...)
res <- solve(x)Thus, solve() is the stage at which the stored problem specification
is turned into one or more optimization runs.
For most users, solve() is the standard execution entry point.
Explicit compilation with compile_model is optional and is
mainly useful for advanced inspection or debugging workflows.
What solve() uses from the problem object
The function uses the information already stored in the Problem
object, including:
baseline planning data;
actions, effects, profit, and spatial relations;
targets and constraints;
registered objectives;
an optional multi-objective method configuration;
solver settings.
If a model has not yet been built, it is built internally during the solve process. If a model snapshot or pointer already exists, the solving layer may reuse or refresh it depending on the internal model state.
Single-objective and multi-objective behaviour
solve() always returns a solutionset-class object.
Single-objective case. If exactly one objective is active and no multi-objective method is configured,
solve()runs a single optimization problem and returns a one-runSolutionSet.Multi-objective case. If a multi-objective method is configured,
solve()dispatches internally according to the stored method name and returns a multi-runSolutionSet.
This unified output structure makes it possible to use the same inspection, plotting, and analysis functions regardless of whether the original problem was single-objective or multi-objective.
Currently supported multi-objective method names are:
"weighted";"epsilon_constraint";"augmecon".
Consistency rule
If multiple objectives are registered but no multi-objective method has been
selected, solve() stops with an error. In practical terms:
one objective and no multi-objective method \(\Rightarrow\) single-objective solve;
multiple objectives and a valid multi-objective method \(\Rightarrow\) multi-objective solve;
multiple objectives and no multi-objective method \(\Rightarrow\) error.
Implicit conservation-planning model
If no explicit actions and no explicit effects are provided, solve()
can build the corresponding classical conservation-planning formulation
internally. In this case, selecting a planning unit is interpreted as applying
an implicit conservation action, and the baseline feature amounts stored in
dist_features count toward representation targets.
This allows standard reserve-selection problems to be solved without
requiring users to manually define actions and effects. Explicit
action-based workflows remain available by using add_actions
and add_effects.
Solver settings
Solver configuration is read from the Problem object, typically after
calling set_solver or one of its convenience wrappers such as
set_solver_gurobi or set_solver_cbc.
These settings may include:
the selected backend;
time limits;
optimality-gap settings;
CPU cores;
verbosity options;
backend-specific solver parameters.
Method dispatch
solve() is an S3 generic. The public method documented here is
solve.Problem(), which operates on Problem objects.
Examples
# ------------------------------------------------------------
# Minimal single-objective example
# ------------------------------------------------------------
pu <- data.frame(
id = 1:4,
cost = c(1, 2, 3, 4)
)
features <- data.frame(
id = 1:2,
name = c("sp1", "sp2")
)
dist_features <- data.frame(
pu = c(1, 1, 2, 3, 4),
feature = c(1, 2, 2, 1, 2),
amount = c(5, 2, 3, 4, 1)
)
x <- create_problem(
pu = pu,
features = features,
dist_features = dist_features,
cost = "cost"
) |>
add_constraint_targets_relative(0.05) |>
add_objective_min_cost(alias = "cost")
if (requireNamespace("rcbc", quietly = TRUE)) {
x <- set_solver_cbc(x, verbose = FALSE)
solset <- solve(x)
print(solset)
}
#> A multiscape solution set (<SolutionSet>)
#> ├─method
#> │├─name: `single`
#> │├─objectives: 1 (cost)
#> │└─run design: unspecified
#> └─content
#> │├─design rows: 1
#> │├─attempted runs: 1
#> │├─stored solutions: 1
#> │└─without solution: 0
#> └─run summary
#> │├─statuses: optimal: 1
#> │├─runtime: 0.02
#> │├─gap: 0
#> │├─design columns: none
#> │└─objective columns: value_cost
#> └─objective ranges
#> │└─cost: 1
#> # ℹ Use get_runs(), get_objectives(), get_pu(), and get_actions() to inspect
#> results.
# ------------------------------------------------------------
# Minimal action-based example
# ------------------------------------------------------------
actions <- data.frame(
id = c("conservation", "restoration")
)
effects <- data.frame(
action = rep(c("conservation", "restoration"), each = 2),
feature = rep(features$id, times = 2),
multiplier = c(1.00, 1.00, 1.50, 1.50)
)
x_actions <- create_problem(
pu = pu,
features = features,
dist_features = dist_features,
cost = "cost"
) |>
add_actions(
actions = actions,
cost = c(conservation = 1, restoration = 2)
) |>
add_effects(
effects = effects,
effect_type = "after"
) |>
add_constraint_targets_relative(0.05) |>
add_objective_min_cost(alias = "cost")
if (requireNamespace("rcbc", quietly = TRUE)) {
x_actions <- set_solver_cbc(x_actions, verbose = FALSE)
solset_actions <- solve(x_actions)
print(solset_actions)
}
#> A multiscape solution set (<SolutionSet>)
#> ├─method
#> │├─name: `single`
#> │├─objectives: 1 (cost)
#> │└─run design: unspecified
#> └─content
#> │├─design rows: 1
#> │├─attempted runs: 1
#> │├─stored solutions: 1
#> │└─without solution: 0
#> └─run summary
#> │├─statuses: optimal: 1
#> │├─runtime: 0.02
#> │├─gap: 0
#> │├─design columns: none
#> │└─objective columns: value_cost
#> └─objective ranges
#> │└─cost: 2
#> # ℹ Use get_runs(), get_objectives(), get_pu(), and get_actions() to inspect
#> results.
# ------------------------------------------------------------
# Minimal multi-objective example
# ------------------------------------------------------------
x_mo <- create_problem(
pu = pu,
features = features,
dist_features = dist_features,
cost = "cost"
) |>
add_constraint_targets_relative(0.05) |>
add_objective_min_cost(alias = "cost") |>
add_objective_max_benefit(alias = "benefit") |>
set_method_weighted_sum(
aliases = c("cost", "benefit"),
weights = c(0.5, 0.5),
normalize_weights = TRUE
)
#> Warning: `weights` is deprecated. Use `runs = run_manual(data.frame(weight_<alias> = ...))` instead.
if (requireNamespace("rcbc", quietly = TRUE)) {
x_mo <- set_solver_cbc(x_mo, verbose = FALSE)
solset_mo <- solve(x_mo)
print(solset_mo)
}
#> Error: Objective 'benefit' has no positive non-zero benefit coefficients.
#> This objective cannot be used as a benefit objective because all selected effects are zero, missing, or non-positive.
#> For add_objective_max_benefit(), the selected feature(s) must have positive action effects in add_effects().
