Append the runs and stored solutions from one
solutionset-class object to another.
This function combines two SolutionSet objects generated from the
same planning problem. It is intended for combining results obtained with
different multiscape solving workflows, such as weighted-sum,
epsilon-constraint, or AUGMECON methods, while keeping a single coherent
result object for downstream extraction, plotting, and analysis.
Arguments
- x
A
solutionset-classobject returned bysolve.- y
A second
solutionset-classobject returned bysolveand generated from the same planning problem asx.
Value
A new solutionset-class object containing the runs and
stored solutions from both input objects.
Details
solution_append() is a solution-set management function. It does not
modify the original input objects. Instead, it returns a new
SolutionSet containing the runs and solutions from both inputs.
Both input objects must be generated from the same planning problem. This is checked conservatively before appending. In particular, the two objects must have compatible:
planning units;
features and feature distributions;
actions, feasible action pairs, and effects;
profit data, when present;
targets;
locks and constraints;
spatial relations;
objective specifications.
Differences in method settings, run design, solver settings, solver status, runtime, gaps, and other solve diagnostics are allowed.
The appended runs and solutions are assigned new run_id and
solution_id values to keep identifiers unique in the combined object.
Identifiers are not required to match between the two inputs.
This function is not intended to combine results from different planning
problems, scenarios, target sets, or objective definitions. Such workflows
should be handled by a future comparison/binding function rather than by
solution_append().
Examples
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)
)
actions <- data.frame(
id = c("conservation", "restoration")
)
effects <- data.frame(
action = rep(actions$id, each = 2),
feature = rep(features$id, times = 2),
multiplier = c(
1.0, 1.0,
1.5, 1.5
)
)
make_problem <- function() {
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") |>
add_objective_max_benefit(alias = "benefit")
}
weighted_problem <- make_problem() |>
set_method_weighted_sum(
aliases = c("cost", "benefit"),
runs = run_grid(
n = 4,
include_extremes = TRUE
),
normalize_weights = TRUE
)
epsilon_problem <- make_problem() |>
set_method_epsilon_constraint(
primary = "cost",
runs = run_grid(
n = 4,
include_extremes = TRUE
)
)
if (requireNamespace("rcbc", quietly = TRUE)) {
weighted_problem <- set_solver_cbc(
weighted_problem,
verbose = FALSE
)
epsilon_problem <- set_solver_cbc(
epsilon_problem,
verbose = FALSE
)
weighted_solutions <- solve(weighted_problem)
epsilon_solutions <- solve(epsilon_problem)
combined_solutions <- solution_append(
weighted_solutions,
epsilon_solutions
)
# Inspect the combined run history
get_runs(combined_solutions)
# Inspect objective values from both methods
get_objectives(
combined_solutions,
format = "wide"
)
# The original objects remain unchanged
get_runs(weighted_solutions)
get_runs(epsilon_solutions)
}
#> run_id solution_id status runtime gap message value_benefit value_cost
#> 1 1 s1 optimal 0.00 0 0.0 2
#> 2 2 s2 optimal 0.02 0 3.5 3
#> 3 3 s3 optimal 0.00 0 5.0 7
#> 4 4 s4 optimal 0.00 0 7.5 18
